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