- add sources.
[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/load_flags.h"
56 #include "net/base/net_util.h"
57 #include "net/http/http_response_headers.h"
58 #include "net/url_request/url_fetcher.h"
59 #include "net/url_request/url_request_status.h"
60 #include "ui/base/l10n/l10n_util.h"
61 #include "ui/base/resource/resource_bundle.h"
62 #include "ui/base/webui/jstemplate_builder.h"
63 #include "ui/base/webui/web_ui_util.h"
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 kMemoryJsPath[] = "memory.js";
95 const char kMemoryCssPath[] = "about_memory.css";
96 const char kStatsJsPath[] = "stats.js";
97 const char kStringsJsPath[] = "strings.js";
98
99 #if defined(OS_CHROMEOS)
100 // chrome://terms falls back to offline page after kOnlineTermsTimeoutSec.
101 const int kOnlineTermsTimeoutSec = 7;
102 #endif  // defined(OS_CHROMEOS)
103
104 // When you type about:memory, it actually loads this intermediate URL that
105 // redirects you to the final page. This avoids the problem where typing
106 // "about:memory" on the new tab page or any other page where a process
107 // transition would occur to the about URL will cause some confusion.
108 //
109 // The problem is that during the processing of the memory page, there are two
110 // processes active, the original and the destination one. This can create the
111 // impression that we're using more resources than we actually are. This
112 // redirect solves the problem by eliminating the process transition during the
113 // time that about memory is being computed.
114 std::string GetAboutMemoryRedirectResponse(Profile* profile) {
115   return base::StringPrintf("<meta http-equiv='refresh' content='0;%s'>",
116                             chrome::kChromeUIMemoryRedirectURL);
117 }
118
119 // Handling about:memory is complicated enough to encapsulate its related
120 // methods into a single class. The user should create it (on the heap) and call
121 // its |StartFetch()| method.
122 class AboutMemoryHandler : public MemoryDetails {
123  public:
124   explicit AboutMemoryHandler(
125       const content::URLDataSource::GotDataCallback& callback)
126       : callback_(callback) {
127   }
128
129   virtual void OnDetailsAvailable() OVERRIDE;
130
131  private:
132   virtual ~AboutMemoryHandler() {}
133
134   void BindProcessMetrics(DictionaryValue* data,
135                           ProcessMemoryInformation* info);
136   void AppendProcess(ListValue* child_data, 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(BrowserThread::CurrentlyOn(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 if (CommandLine::ForCurrentProcess()->HasSwitch(
251                    chromeos::switches::kDisableOnlineEULA)) {
252       // Fallback to the local file.
253       BrowserThread::PostTask(
254           BrowserThread::FILE, FROM_HERE,
255           base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread, this));
256     } else {
257       // Try to load online version of ChromeOS terms first.
258       // ChromeOSOnlineTermsHandler object destroys itself.
259       new ChromeOSOnlineTermsHandler(
260           base::Bind(&ChromeOSTermsHandler::OnOnlineEULAFetched, this),
261           locale_);
262     }
263   }
264
265   void OnOnlineEULAFetched(ChromeOSOnlineTermsHandler* loader) {
266     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267     loader->GetResponseResult(&contents_);
268     if (contents_.empty()) {
269       // Load local ChromeOS terms from the file.
270       BrowserThread::PostTask(
271           BrowserThread::FILE, FROM_HERE,
272           base::Bind(&ChromeOSTermsHandler::LoadEulaFileOnFileThread, this));
273     } else {
274       ResponseOnUIThread();
275     }
276   }
277
278   void LoadOemEulaFileOnFileThread() {
279     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
280     const chromeos::StartupCustomizationDocument* customization =
281         chromeos::StartupCustomizationDocument::GetInstance();
282     if (customization->IsReady()) {
283       base::FilePath oem_eula_file_path;
284       if (net::FileURLToFilePath(GURL(customization->GetEULAPage(locale_)),
285                                  &oem_eula_file_path)) {
286         if (!base::ReadFileToString(oem_eula_file_path, &contents_)) {
287           contents_.clear();
288         }
289       }
290     }
291     BrowserThread::PostTask(
292         BrowserThread::UI, FROM_HERE,
293         base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
294   }
295
296   void LoadEulaFileOnFileThread() {
297     std::string file_path =
298         base::StringPrintf(chrome::kEULAPathFormat, locale_.c_str());
299     if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
300       // No EULA for given language - try en-US as default.
301       file_path = base::StringPrintf(chrome::kEULAPathFormat, "en-US");
302       if (!base::ReadFileToString(base::FilePath(file_path), &contents_)) {
303         // File with EULA not found, ResponseOnUIThread will load EULA from
304         // resources if contents_ is empty.
305         contents_.clear();
306       }
307     }
308     BrowserThread::PostTask(
309         BrowserThread::UI, FROM_HERE,
310         base::Bind(&ChromeOSTermsHandler::ResponseOnUIThread, this));
311   }
312
313   void ResponseOnUIThread() {
314     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
315     // If we fail to load Chrome OS EULA from disk, load it from resources.
316     // Do nothing if OEM EULA load failed.
317     if (contents_.empty() && path_ != chrome::kOemEulaURLPath)
318       contents_ = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
319     callback_.Run(base::RefCountedString::TakeString(&contents_));
320   }
321
322   // Path in the URL.
323   const std::string path_;
324
325   // Callback to run with the response.
326   content::URLDataSource::GotDataCallback callback_;
327
328   // Locale of the EULA.
329   const std::string locale_;
330
331   // EULA contents that was loaded from file.
332   std::string contents_;
333
334   DISALLOW_COPY_AND_ASSIGN(ChromeOSTermsHandler);
335 };
336
337 #endif
338
339 }  // namespace
340
341 // Individual about handlers ---------------------------------------------------
342
343 namespace about_ui {
344
345 void AppendHeader(std::string* output, int refresh,
346                   const std::string& unescaped_title) {
347   output->append("<!DOCTYPE HTML>\n<html>\n<head>\n");
348   if (!unescaped_title.empty()) {
349     output->append("<title>");
350     output->append(net::EscapeForHTML(unescaped_title));
351     output->append("</title>\n");
352   }
353   output->append("<meta charset='utf-8'>\n");
354   if (refresh > 0) {
355     output->append("<meta http-equiv='refresh' content='");
356     output->append(base::IntToString(refresh));
357     output->append("'/>\n");
358   }
359 }
360
361 void AppendBody(std::string *output) {
362   output->append("</head>\n<body>\n");
363 }
364
365 void AppendFooter(std::string *output) {
366   output->append("</body>\n</html>\n");
367 }
368
369 }  // namespace about_ui
370
371 using about_ui::AppendHeader;
372 using about_ui::AppendBody;
373 using about_ui::AppendFooter;
374
375 namespace {
376
377 std::string ChromeURLs() {
378   std::string html;
379   AppendHeader(&html, 0, "Chrome URLs");
380   AppendBody(&html);
381   html += "<h2>List of Chrome URLs</h2>\n<ul>\n";
382   std::vector<std::string> hosts(
383       chrome::kChromeHostURLs,
384       chrome::kChromeHostURLs + chrome::kNumberOfChromeHostURLs);
385   std::sort(hosts.begin(), hosts.end());
386   for (std::vector<std::string>::const_iterator i = hosts.begin();
387        i != hosts.end(); ++i)
388     html += "<li><a href='chrome://" + *i + "/'>chrome://" + *i + "</a></li>\n";
389   html += "</ul>\n<h2>For Debug</h2>\n"
390       "<p>The following pages are for debugging purposes only. Because they "
391       "crash or hang the renderer, they're not linked directly; you can type "
392       "them into the address bar if you need them.</p>\n<ul>";
393   for (int i = 0; i < chrome::kNumberOfChromeDebugURLs; i++)
394     html += "<li>" + std::string(chrome::kChromeDebugURLs[i]) + "</li>\n";
395   html += "</ul>\n";
396   AppendFooter(&html);
397   return html;
398 }
399
400 #if defined(OS_CHROMEOS)
401
402 // Html output helper functions
403
404 // Helper function to wrap HTML with a tag.
405 std::string WrapWithTag(const std::string& tag, const std::string& text) {
406   return "<" + tag + ">" + text + "</" + tag + ">";
407 }
408
409 // Helper function to wrap Html with <td> tag.
410 std::string WrapWithTD(const std::string& text) {
411   return "<td>" + text + "</td>";
412 }
413
414 // Helper function to wrap Html with <tr> tag.
415 std::string WrapWithTR(const std::string& text) {
416   return "<tr>" + text + "</tr>";
417 }
418
419 std::string AddStringRow(const std::string& name, const std::string& value) {
420   std::string row;
421   row.append(WrapWithTD(name));
422   row.append(WrapWithTD(value));
423   return WrapWithTR(row);
424 }
425
426 void AddContentSecurityPolicy(std::string* output) {
427   output->append("<meta http-equiv='Content-Security-Policy' "
428       "content='default-src 'none';'>");
429 }
430
431 // TODO(stevenjb): L10N AboutDiscards.
432
433 std::string AboutDiscardsRun() {
434   std::string output;
435   AppendHeader(&output, 0, "About discards");
436   output.append(
437       base::StringPrintf("<meta http-equiv='refresh' content='2;%s'>",
438       chrome::kChromeUIDiscardsURL));
439   AddContentSecurityPolicy(&output);
440   output.append(WrapWithTag("p", "Discarding a tab..."));
441   g_browser_process->platform_part()->
442       oom_priority_manager()->LogMemoryAndDiscardTab();
443   AppendFooter(&output);
444   return output;
445 }
446
447 std::string AboutDiscards(const std::string& path) {
448   std::string output;
449   const char kRunCommand[] = "run";
450   if (path == kRunCommand)
451     return AboutDiscardsRun();
452   AppendHeader(&output, 0, "About discards");
453   AddContentSecurityPolicy(&output);
454   AppendBody(&output);
455   output.append("<h3>About discards</h3>");
456   output.append(
457       "<p>Tabs sorted from most interesting to least interesting. The least "
458       "interesting tab may be discarded if we run out of physical memory.</p>");
459
460   chromeos::OomPriorityManager* oom =
461       g_browser_process->platform_part()->oom_priority_manager();
462   std::vector<string16> titles = oom->GetTabTitles();
463   if (!titles.empty()) {
464     output.append("<ul>");
465     std::vector<string16>::iterator it = titles.begin();
466     for ( ; it != titles.end(); ++it) {
467       std::string title = UTF16ToUTF8(*it);
468       title = net::EscapeForHTML(title);
469       output.append(WrapWithTag("li", title));
470     }
471     output.append("</ul>");
472   } else {
473     output.append("<p>None found.  Wait 10 seconds, then refresh.</p>");
474   }
475   output.append(base::StringPrintf("%d discards this session. ",
476                              oom->discard_count()));
477   output.append(base::StringPrintf("<a href='%s%s'>Discard tab now</a>",
478                                    chrome::kChromeUIDiscardsURL,
479                                    kRunCommand));
480
481   base::SystemMemoryInfoKB meminfo;
482   base::GetSystemMemoryInfo(&meminfo);
483   output.append("<h3>System memory information in MB</h3>");
484   output.append("<table>");
485   // Start with summary statistics.
486   output.append(AddStringRow(
487       "Total", base::IntToString(meminfo.total / 1024)));
488   output.append(AddStringRow(
489       "Free", base::IntToString(meminfo.free / 1024)));
490   int mem_allocated_kb = meminfo.active_anon + meminfo.inactive_anon;
491 #if defined(ARCH_CPU_ARM_FAMILY)
492   // ARM counts allocated graphics memory separately from anonymous.
493   if (meminfo.gem_size != -1)
494     mem_allocated_kb += meminfo.gem_size / 1024;
495 #endif
496   output.append(AddStringRow(
497       "Allocated", base::IntToString(mem_allocated_kb / 1024)));
498   // Add some space, then detailed numbers.
499   output.append(AddStringRow("&nbsp;", "&nbsp;"));
500   output.append(AddStringRow(
501       "Buffered", base::IntToString(meminfo.buffers / 1024)));
502   output.append(AddStringRow(
503       "Cached", base::IntToString(meminfo.cached / 1024)));
504   output.append(AddStringRow(
505       "Active Anon", base::IntToString(meminfo.active_anon / 1024)));
506   output.append(AddStringRow(
507       "Inactive Anon", base::IntToString(meminfo.inactive_anon / 1024)));
508   output.append(AddStringRow(
509       "Shared", base::IntToString(meminfo.shmem / 1024)));
510   output.append(AddStringRow(
511       "Graphics", base::IntToString(meminfo.gem_size / 1024 / 1024)));
512   output.append("</table>");
513
514   AppendFooter(&output);
515   return output;
516 }
517
518 #endif  // OS_CHROMEOS
519
520 // AboutDnsHandler bounces the request back to the IO thread to collect
521 // the DNS information.
522 class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
523  public:
524   static void Start(Profile* profile,
525                     const content::URLDataSource::GotDataCallback& callback) {
526     scoped_refptr<AboutDnsHandler> handler(
527         new AboutDnsHandler(profile, callback));
528     handler->StartOnUIThread();
529   }
530
531  private:
532   friend class base::RefCountedThreadSafe<AboutDnsHandler>;
533
534   AboutDnsHandler(Profile* profile,
535                   const content::URLDataSource::GotDataCallback& callback)
536       : profile_(profile),
537         callback_(callback) {
538     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
539   }
540
541   virtual ~AboutDnsHandler() {}
542
543   // Calls FinishOnUIThread() on completion.
544   void StartOnUIThread() {
545     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
546     chrome_browser_net::Predictor* predictor = profile_->GetNetworkPredictor();
547     BrowserThread::PostTask(
548         BrowserThread::IO, FROM_HERE,
549         base::Bind(&AboutDnsHandler::StartOnIOThread, this, predictor));
550   }
551
552   void StartOnIOThread(chrome_browser_net::Predictor* predictor) {
553     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
554
555     std::string data;
556     AppendHeader(&data, 0, "About DNS");
557     AppendBody(&data);
558     chrome_browser_net::Predictor::PredictorGetHtmlInfo(predictor, &data);
559     AppendFooter(&data);
560
561     BrowserThread::PostTask(
562         BrowserThread::UI, FROM_HERE,
563         base::Bind(&AboutDnsHandler::FinishOnUIThread, this, data));
564   }
565
566   void FinishOnUIThread(const std::string& data) {
567     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
568     std::string data_copy(data);
569     callback_.Run(base::RefCountedString::TakeString(&data_copy));
570   }
571
572   Profile* profile_;
573
574   // Callback to run with the response.
575   content::URLDataSource::GotDataCallback callback_;
576
577   DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
578 };
579
580 void FinishMemoryDataRequest(
581     const std::string& path,
582     const content::URLDataSource::GotDataCallback& callback) {
583   if (path == kStringsJsPath) {
584     // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want
585     // the refcount to be greater than 0.
586     scoped_refptr<AboutMemoryHandler> handler(new AboutMemoryHandler(callback));
587     // TODO(jamescook): Maybe this shouldn't update UMA?
588     handler->StartFetch(MemoryDetails::UPDATE_USER_METRICS);
589   } else {
590     int id = IDR_ABOUT_MEMORY_HTML;
591     if (path == kMemoryJsPath) {
592       id = IDR_ABOUT_MEMORY_JS;
593     } else if (path == kMemoryCssPath) {
594       id = IDR_ABOUT_MEMORY_CSS;
595     }
596
597     std::string result =
598         ResourceBundle::GetSharedInstance().GetRawDataResource(id).as_string();
599     callback.Run(base::RefCountedString::TakeString(&result));
600   }
601 }
602
603 // Handler for filling in the "about:stats" page, as called by the browser's
604 // About handler processing.
605 // |query| is roughly the query string of the about:stats URL.
606 // Returns a string containing the HTML to render for the about:stats page.
607 // Conditional Output:
608 //      if |query| is "json", returns a JSON format of all counters.
609 //      if |query| is "raw", returns plain text of counter deltas.
610 //      otherwise, returns HTML with pretty JS/HTML to display the data.
611 std::string AboutStats(const std::string& query) {
612   // We keep the DictionaryValue tree live so that we can do delta
613   // stats computations across runs.
614   CR_DEFINE_STATIC_LOCAL(DictionaryValue, root, ());
615   static base::TimeTicks last_sample_time = base::TimeTicks::Now();
616
617   base::TimeTicks now = base::TimeTicks::Now();
618   base::TimeDelta time_since_last_sample = now - last_sample_time;
619   last_sample_time = now;
620
621   base::StatsTable* table = base::StatsTable::current();
622   if (!table)
623     return std::string();
624
625   // We maintain two lists - one for counters and one for timers.
626   // Timers actually get stored on both lists.
627   ListValue* counters;
628   if (!root.GetList("counters", &counters)) {
629     counters = new ListValue();
630     root.Set("counters", counters);
631   }
632
633   ListValue* timers;
634   if (!root.GetList("timers", &timers)) {
635     timers = new ListValue();
636     root.Set("timers", timers);
637   }
638
639   // NOTE: Counters start at index 1.
640   for (int index = 1; index <= table->GetMaxCounters(); index++) {
641     // Get the counter's full name
642     std::string full_name = table->GetRowName(index);
643     if (full_name.length() == 0)
644       break;
645     DCHECK_EQ(':', full_name[1]);
646     char counter_type = full_name[0];
647     std::string name = full_name.substr(2);
648
649     // JSON doesn't allow '.' in names.
650     size_t pos;
651     while ((pos = name.find(".")) != std::string::npos)
652       name.replace(pos, 1, ":");
653
654     // Try to see if this name already exists.
655     DictionaryValue* counter = NULL;
656     for (size_t scan_index = 0;
657          scan_index < counters->GetSize(); scan_index++) {
658       DictionaryValue* dictionary;
659       if (counters->GetDictionary(scan_index, &dictionary)) {
660         std::string scan_name;
661         if (dictionary->GetString("name", &scan_name) && scan_name == name) {
662           counter = dictionary;
663         }
664       } else {
665         NOTREACHED();  // Should always be there
666       }
667     }
668
669     if (counter == NULL) {
670       counter = new DictionaryValue();
671       counter->SetString("name", name);
672       counters->Append(counter);
673     }
674
675     switch (counter_type) {
676       case 'c':
677         {
678           int new_value = table->GetRowValue(index);
679           int prior_value = 0;
680           int delta = 0;
681           if (counter->GetInteger("value", &prior_value)) {
682             delta = new_value - prior_value;
683           }
684           counter->SetInteger("value", new_value);
685           counter->SetInteger("delta", delta);
686         }
687         break;
688       case 'm':
689         {
690           // TODO(mbelshe): implement me.
691         }
692         break;
693       case 't':
694         {
695           int time = table->GetRowValue(index);
696           counter->SetInteger("time", time);
697
698           // Store this on the timers list as well.
699           timers->Append(counter);
700         }
701         break;
702       default:
703         NOTREACHED();
704     }
705   }
706
707   std::string data;
708   if (query == "json" || query == kStringsJsPath) {
709     base::JSONWriter::WriteWithOptions(
710           &root,
711           base::JSONWriter::OPTIONS_DO_NOT_ESCAPE |
712               base::JSONWriter::OPTIONS_PRETTY_PRINT,
713           &data);
714     if (query == kStringsJsPath)
715       data = "var templateData = " + data + ";";
716   } else if (query == "raw") {
717     // Dump the raw counters which have changed in text format.
718     data = "<pre>";
719     data.append(base::StringPrintf("Counter changes in the last %ldms\n",
720         static_cast<long int>(time_since_last_sample.InMilliseconds())));
721     for (size_t i = 0; i < counters->GetSize(); ++i) {
722       Value* entry = NULL;
723       bool rv = counters->Get(i, &entry);
724       if (!rv)
725         continue;  // None of these should fail.
726       DictionaryValue* counter = static_cast<DictionaryValue*>(entry);
727       int delta;
728       rv = counter->GetInteger("delta", &delta);
729       if (!rv)
730         continue;
731       if (delta > 0) {
732         std::string name;
733         rv = counter->GetString("name", &name);
734         if (!rv)
735           continue;
736         int value;
737         rv = counter->GetInteger("value", &value);
738         if (!rv)
739           continue;
740         data.append(name);
741         data.append(":");
742         data.append(base::IntToString(delta));
743         data.append("\n");
744       }
745     }
746     data.append("</pre>");
747   } else {
748     // Get about_stats.html/js from resource bundle.
749     data = ResourceBundle::GetSharedInstance().GetRawDataResource(
750         (query == kStatsJsPath ?
751          IDR_ABOUT_STATS_JS : IDR_ABOUT_STATS_HTML)).as_string();
752
753     if (query != kStatsJsPath) {
754       // Clear the timer list since we stored the data in the timers list
755       // as well.
756       for (int index = static_cast<int>(timers->GetSize())-1; index >= 0;
757            index--) {
758         scoped_ptr<Value> value;
759         timers->Remove(index, &value);
760         // We don't care about the value pointer; it's still tracked
761         // on the counters list.
762         ignore_result(value.release());
763       }
764     }
765   }
766
767   return data;
768 }
769
770 #if defined(OS_LINUX) || defined(OS_OPENBSD)
771 std::string AboutLinuxProxyConfig() {
772   std::string data;
773   AppendHeader(&data, 0,
774                l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
775   data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
776   AppendBody(&data);
777   base::FilePath binary = CommandLine::ForCurrentProcess()->GetProgram();
778   data.append(l10n_util::GetStringFUTF8(
779       IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
780       l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
781       ASCIIToUTF16(binary.BaseName().value())));
782   AppendFooter(&data);
783   return data;
784 }
785
786 void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id,
787                      bool good) {
788   data->append("<tr><td>");
789   data->append(prefix);
790   data->append(l10n_util::GetStringUTF8(name_id));
791   if (good) {
792     data->append("</td><td style='color: green;'>");
793     data->append(
794         l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
795   } else {
796     data->append("</td><td style='color: red;'>");
797     data->append(
798         l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
799   }
800   data->append("</td></tr>");
801 }
802
803 std::string AboutSandbox() {
804   std::string data;
805   AppendHeader(&data, 0, l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
806   AppendBody(&data);
807   data.append("<h1>");
808   data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
809   data.append("</h1>");
810
811   // Get expected sandboxing status of renderers.
812   const int status = content::ZygoteHost::GetInstance()->GetSandboxStatus();
813
814   data.append("<table>");
815
816   AboutSandboxRow(&data,
817                   std::string(),
818                   IDS_ABOUT_SANDBOX_SUID_SANDBOX,
819                   status & content::kSandboxLinuxSUID);
820   AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_PID_NAMESPACES,
821                   status & content::kSandboxLinuxPIDNS);
822   AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_NET_NAMESPACES,
823                   status & content::kSandboxLinuxNetNS);
824   AboutSandboxRow(&data,
825                   std::string(),
826                   IDS_ABOUT_SANDBOX_SECCOMP_BPF_SANDBOX,
827                   status & content::kSandboxLinuxSeccompBpf);
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(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(ListValue* child_data,
878                                        ProcessMemoryInformation* info) {
879   DCHECK(child_data && info);
880
881   // Append a new DictionaryValue for this renderer to our list.
882   DictionaryValue* child = new 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   ListValue* titles = new ListValue();
893   child->Set("titles", titles);
894   for (size_t i = 0; i < info->titles.size(); ++i)
895     titles->Append(new StringValue(info->titles[i]));
896 }
897
898 void AboutMemoryHandler::OnDetailsAvailable() {
899   // the root of the JSON hierarchy for about:memory jstemplate
900   scoped_ptr<DictionaryValue> root(new DictionaryValue);
901   ListValue* browsers = new 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   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     DictionaryValue* browser_data = new 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 += ASCIIToUTF16(", ");
940     log_string += browser_processes[index].name + ASCIIToUTF16(", ") +
941                   base::Int64ToString16(aggregate.working_set.priv) +
942                   ASCIIToUTF16(", ") +
943                   base::Int64ToString16(aggregate.working_set.shared) +
944                   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   DictionaryValue* browser_data = new DictionaryValue();
952   root->Set("browzr_data", browser_data);
953   ListValue* child_data = new 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   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_view_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 = (path == kCreditsJsPath) ? IDR_CREDITS_JS : IDR_CREDITS_HTML;
1008     response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1009         idr).as_string();
1010 #if defined(OS_CHROMEOS)
1011   } else if (source_name_ == chrome::kChromeUIDiscardsHost) {
1012     response = AboutDiscards(path);
1013 #endif
1014   } else if (source_name_ == chrome::kChromeUIDNSHost) {
1015     AboutDnsHandler::Start(profile(), callback);
1016     return;
1017 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1018   } else if (source_name_ == chrome::kChromeUILinuxProxyConfigHost) {
1019     response = AboutLinuxProxyConfig();
1020 #endif
1021   } else if (source_name_ == chrome::kChromeUIMemoryHost) {
1022     response = GetAboutMemoryRedirectResponse(profile());
1023   } else if (source_name_ == chrome::kChromeUIMemoryRedirectHost) {
1024     FinishMemoryDataRequest(path, callback);
1025     return;
1026 #if defined(OS_CHROMEOS)
1027   } else if (source_name_ == chrome::kChromeUIOSCreditsHost) {
1028     response = ResourceBundle::GetSharedInstance().GetRawDataResource(
1029         IDR_OS_CREDITS_HTML).as_string();
1030 #endif
1031 #if defined(OS_LINUX) || defined(OS_OPENBSD)
1032   } else if (source_name_ == chrome::kChromeUISandboxHost) {
1033     response = AboutSandbox();
1034 #endif
1035   } else if (source_name_ == chrome::kChromeUIStatsHost) {
1036     response = AboutStats(path);
1037   } else if (source_name_ == chrome::kChromeUITermsHost) {
1038 #if defined(OS_CHROMEOS)
1039     ChromeOSTermsHandler::Start(path, callback);
1040     return;
1041 #else
1042     response = l10n_util::GetStringUTF8(IDS_TERMS_HTML);
1043 #endif
1044   }
1045
1046   FinishDataRequest(response, callback);
1047 }
1048
1049 void AboutUIHTMLSource::FinishDataRequest(
1050     const std::string& html,
1051     const content::URLDataSource::GotDataCallback& callback) {
1052   std::string html_copy(html);
1053   callback.Run(base::RefCountedString::TakeString(&html_copy));
1054 }
1055
1056 std::string AboutUIHTMLSource::GetMimeType(const std::string& path) const {
1057   if (path == kCreditsJsPath ||
1058       path == kStatsJsPath   ||
1059       path == kStringsJsPath ||
1060       path == kMemoryJsPath) {
1061     return "application/javascript";
1062   }
1063   return "text/html";
1064 }
1065
1066 bool AboutUIHTMLSource::ShouldAddContentSecurityPolicy() const {
1067 #if defined(OS_CHROMEOS)
1068   if (source_name_ == chrome::kChromeUIOSCreditsHost)
1069     return false;
1070 #endif
1071   return content::URLDataSource::ShouldAddContentSecurityPolicy();
1072 }
1073
1074 bool AboutUIHTMLSource::ShouldDenyXFrameOptions() const {
1075 #if defined(OS_CHROMEOS)
1076   if (source_name_ == chrome::kChromeUITermsHost) {
1077     // chrome://terms page is embedded in iframe to chrome://oobe.
1078     return false;
1079   }
1080 #endif
1081   return content::URLDataSource::ShouldDenyXFrameOptions();
1082 }
1083
1084 AboutUI::AboutUI(content::WebUI* web_ui, const std::string& name)
1085     : WebUIController(web_ui) {
1086   Profile* profile = Profile::FromWebUI(web_ui);
1087
1088 #if defined(ENABLE_THEMES)
1089   // Set up the chrome://theme/ source.
1090   ThemeSource* theme = new ThemeSource(profile);
1091   content::URLDataSource::Add(profile, theme);
1092 #endif
1093
1094   content::URLDataSource::Add(profile, new AboutUIHTMLSource(name, profile));
1095 }