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