browser_about_handler.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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/browser_about_handler.h"
6
7#include <algorithm>
8#include <string>
9#include <vector>
10
11#include "base/callback.h"
12#include "base/command_line.h"
13#include "base/i18n/number_formatting.h"
14#include "base/json/json_writer.h"
15#include "base/metrics/histogram.h"
16#include "base/metrics/stats_table.h"
17#include "base/path_service.h"
18#include "base/singleton.h"
19#include "base/stringprintf.h"
20#include "base/string_number_conversions.h"
21#include "base/string_piece.h"
22#include "base/string_util.h"
23#include "base/threading/thread.h"
24#include "base/tracked_objects.h"
25#include "base/utf_string_conversions.h"
26#include "base/values.h"
27#include "chrome/browser/about_flags.h"
28#include "chrome/browser/browser_process.h"
29#include "chrome/browser/browser_thread.h"
30#include "chrome/browser/defaults.h"
31#include "chrome/browser/dom_ui/chrome_url_data_manager.h"
32#include "chrome/browser/gpu_process_host.h"
33#include "chrome/browser/gpu_process_host_ui_shim.h"
34#include "chrome/browser/memory_details.h"
35#include "chrome/browser/metrics/histogram_synchronizer.h"
36#include "chrome/browser/net/predictor_api.h"
37#include "chrome/browser/platform_util.h"
38#include "chrome/browser/profiles/profile.h"
39#include "chrome/browser/profiles/profile_manager.h"
40#include "chrome/browser/renderer_host/render_process_host.h"
41#include "chrome/browser/renderer_host/render_view_host.h"
42#include "chrome/browser/ui/browser_dialogs.h"
43#include "chrome/common/about_handler.h"
44#include "chrome/common/chrome_paths.h"
45#include "chrome/common/chrome_version_info.h"
46#include "chrome/common/gpu_info.h"
47#include "chrome/common/jstemplate_builder.h"
48#include "chrome/common/net/gaia/google_service_auth_error.h"
49#include "chrome/common/render_messages.h"
50#include "chrome/common/url_constants.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 "webkit/glue/webkit_glue.h"
57#include "net/base/escape.h"
58#include "ui/base/l10n/l10n_util.h"
59#include "ui/base/resource/resource_bundle.h"
60#ifdef CHROME_V8
61#include "v8/include/v8.h"
62#endif
63
64#if defined(OS_WIN)
65#include "chrome/browser/enumerate_modules_model_win.h"
66#elif defined(OS_CHROMEOS)
67#include "chrome/browser/chromeos/cros/cros_library.h"
68#include "chrome/browser/chromeos/cros/network_library.h"
69#include "chrome/browser/chromeos/cros/syslogs_library.h"
70#include "chrome/browser/chromeos/version_loader.h"
71#include "chrome/browser/zygote_host_linux.h"
72#elif defined(OS_LINUX)
73#include "chrome/browser/zygote_host_linux.h"
74#endif
75
76#if defined(USE_TCMALLOC)
77#include "third_party/tcmalloc/chromium/src/google/malloc_extension.h"
78#endif
79
80using base::Time;
81using base::TimeDelta;
82
83#if defined(USE_TCMALLOC)
84// static
85AboutTcmallocOutputs* AboutTcmallocOutputs::GetInstance() {
86  return Singleton<AboutTcmallocOutputs>::get();
87}
88
89// Glue between the callback task and the method in the singleton.
90void AboutTcmallocRendererCallback(base::ProcessId pid, std::string output) {
91  AboutTcmallocOutputs::GetInstance()->RendererCallback(pid, output);
92}
93#endif
94
95namespace {
96
97// The (alphabetized) paths used for the about pages.
98// Note: Keep these in sync with url_constants.h
99const char kAppCacheInternalsPath[] = "appcache-internals";
100const char kBlobInternalsPath[] = "blob-internals";
101const char kCreditsPath[] = "credits";
102const char kCachePath[] = "view-http-cache";
103#if defined(OS_WIN)
104const char kConflictsPath[] = "conflicts";
105#endif
106const char kDnsPath[] = "dns";
107const char kFlagsPath[] = "flags";
108const char kGpuPath[] = "gpu";
109const char kHistogramsPath[] = "histograms";
110const char kMemoryRedirectPath[] = "memory-redirect";
111const char kMemoryPath[] = "memory";
112const char kStatsPath[] = "stats";
113const char kTasksPath[] = "tasks";
114const char kTcmallocPath[] = "tcmalloc";
115const char kTermsPath[] = "terms";
116const char kVersionPath[] = "version";
117const char kAboutPath[] = "about";
118// Not about:* pages, but included to make about:about look nicer
119const char kNetInternalsPath[] = "net-internals";
120const char kPluginsPath[] = "plugins";
121const char kSyncInternalsPath[] = "sync-internals";
122
123#if defined(OS_LINUX)
124const char kLinuxProxyConfigPath[] = "linux-proxy-config";
125const char kSandboxPath[] = "sandbox";
126#endif
127
128#if defined(OS_CHROMEOS)
129const char kNetworkPath[] = "network";
130const char kOSCreditsPath[] = "os-credits";
131#endif
132
133// Add path here to be included in about:about
134const char *kAllAboutPaths[] = {
135  kAppCacheInternalsPath,
136  kBlobInternalsPath,
137  kCachePath,
138  kCreditsPath,
139#if defined(OS_WIN)
140  kConflictsPath,
141#endif
142  kDnsPath,
143  kFlagsPath,
144  kGpuPath,
145  kHistogramsPath,
146  kMemoryPath,
147  kNetInternalsPath,
148  kPluginsPath,
149  kStatsPath,
150  kSyncInternalsPath,
151  kTasksPath,
152  kTcmallocPath,
153  kTermsPath,
154  kVersionPath,
155#if defined(OS_LINUX)
156  kSandboxPath,
157#endif
158#if defined(OS_CHROMEOS)
159  kNetworkPath,
160  kOSCreditsPath,
161#endif
162  };
163
164// When you type about:memory, it actually loads an intermediate URL that
165// redirects you to the final page. This avoids the problem where typing
166// "about:memory" on the new tab page or any other page where a process
167// transition would occur to the about URL will cause some confusion.
168//
169// The problem is that during the processing of the memory page, there are two
170// processes active, the original and the destination one. This can create the
171// impression that we're using more resources than we actually are. This
172// redirect solves the problem by eliminating the process transition during the
173// time that about memory is being computed.
174std::string GetAboutMemoryRedirectResponse() {
175  return "<meta http-equiv=\"refresh\" "
176      "content=\"0;chrome://about/memory\">";
177}
178
179class AboutSource : public ChromeURLDataManager::DataSource {
180 public:
181  // Creates our datasource.
182  AboutSource();
183
184  // Called when the network layer has requested a resource underneath
185  // the path we registered.
186  virtual void StartDataRequest(const std::string& path,
187                                bool is_off_the_record,
188                                int request_id);
189
190  virtual std::string GetMimeType(const std::string&) const {
191    return "text/html";
192  }
193
194  // Send the response data.
195  void FinishDataRequest(const std::string& html, int request_id);
196
197 private:
198  virtual ~AboutSource();
199
200  DISALLOW_COPY_AND_ASSIGN(AboutSource);
201};
202
203// Handling about:memory is complicated enough to encapsulate its related
204// methods into a single class. The user should create it (on the heap) and call
205// its |StartFetch()| method.
206class AboutMemoryHandler : public MemoryDetails {
207 public:
208  AboutMemoryHandler(AboutSource* source, int request_id)
209    : source_(source), request_id_(request_id) {}
210
211
212  virtual void OnDetailsAvailable();
213
214 private:
215  ~AboutMemoryHandler() {}
216
217  void BindProcessMetrics(DictionaryValue* data,
218                          ProcessMemoryInformation* info);
219  void AppendProcess(ListValue* child_data, ProcessMemoryInformation* info);
220
221  scoped_refptr<AboutSource> source_;
222  int request_id_;
223
224  DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler);
225};
226
227#if defined(OS_CHROMEOS)
228// ChromeOSAboutVersionHandler is responsible for loading the Chrome OS
229// version.
230// ChromeOSAboutVersionHandler handles deleting itself once the version has
231// been obtained and AboutSource notified.
232class ChromeOSAboutVersionHandler {
233 public:
234  ChromeOSAboutVersionHandler(AboutSource* source, int request_id);
235
236  // Callback from chromeos::VersionLoader giving the version.
237  void OnVersion(chromeos::VersionLoader::Handle handle,
238                 std::string version);
239
240 private:
241  // Where the results are fed to.
242  scoped_refptr<AboutSource> source_;
243
244  // ID identifying the request.
245  int request_id_;
246
247  // Handles asynchronously loading the version.
248  chromeos::VersionLoader loader_;
249
250  // Used to request the version.
251  CancelableRequestConsumer consumer_;
252
253  DISALLOW_COPY_AND_ASSIGN(ChromeOSAboutVersionHandler);
254};
255#endif
256
257// Individual about handlers ---------------------------------------------------
258
259std::string AboutAbout() {
260  std::string html;
261  html.append("<html><head><title>About Pages</title></head><body>\n");
262  html.append("<h2>List of About pages</h2><ul>\n");
263  for (size_t i = 0; i < arraysize(kAllAboutPaths); i++) {
264    if (kAllAboutPaths[i] == kAppCacheInternalsPath ||
265        kAllAboutPaths[i] == kBlobInternalsPath ||
266        kAllAboutPaths[i] == kCachePath ||
267#if defined(OS_WIN)
268        kAllAboutPaths[i] == kConflictsPath ||
269#endif
270        kAllAboutPaths[i] == kFlagsPath ||
271        kAllAboutPaths[i] == kGpuPath ||
272        kAllAboutPaths[i] == kNetInternalsPath ||
273        kAllAboutPaths[i] == kPluginsPath) {
274      html.append("<li><a href='chrome://");
275    } else {
276      html.append("<li><a href='chrome://about/");
277    }
278    html.append(kAllAboutPaths[i]);
279    html.append("/'>about:");
280    html.append(kAllAboutPaths[i]);
281    html.append("</a>\n");
282  }
283  const char *debug[] = { "crash", "kill", "hang", "shorthang",
284                          "gpucrash", "gpuhang" };
285  html.append("</ul><h2>For Debug</h2>");
286  html.append("</ul><p>The following pages are for debugging purposes only. "
287              "Because they crash or hang the renderer, they're not linked "
288              "directly; you can type them into the address bar if you need "
289              "them.</p><ul>");
290  for (size_t i = 0; i < arraysize(debug); i++) {
291    html.append("<li>");
292    html.append("about:");
293    html.append(debug[i]);
294    html.append("\n");
295  }
296  html.append("</ul></body></html>");
297  return html;
298}
299
300#if defined(OS_CHROMEOS)
301std::string AboutNetwork(const std::string& query) {
302  int refresh;
303  base::StringToInt(query, &refresh);
304  return chromeos::CrosLibrary::Get()->GetNetworkLibrary()->
305      GetHtmlInfo(refresh);
306}
307#endif
308
309// AboutDnsHandler bounces the request back to the IO thread to collect
310// the DNS information.
311class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> {
312 public:
313  static void Start(AboutSource* source, int request_id) {
314    scoped_refptr<AboutDnsHandler> handler(
315        new AboutDnsHandler(source, request_id));
316    handler->StartOnUIThread();
317  }
318
319 private:
320  AboutDnsHandler(AboutSource* source, int request_id)
321      : source_(source),
322        request_id_(request_id) {
323    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324  }
325
326  // Calls FinishOnUIThread() on completion.
327  void StartOnUIThread() {
328    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
329    BrowserThread::PostTask(
330        BrowserThread::IO, FROM_HERE,
331        NewRunnableMethod(this, &AboutDnsHandler::StartOnIOThread));
332  }
333
334  void StartOnIOThread() {
335    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
336
337    std::string data;
338    chrome_browser_net::PredictorGetHtmlInfo(&data);
339
340    BrowserThread::PostTask(
341        BrowserThread::UI, FROM_HERE,
342        NewRunnableMethod(this, &AboutDnsHandler::FinishOnUIThread, data));
343  }
344
345  void FinishOnUIThread(const std::string& data) {
346    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347    source_->FinishDataRequest(data, request_id_);
348  }
349
350  // Where the results are fed to.
351  scoped_refptr<AboutSource> source_;
352
353  // ID identifying the request.
354  int request_id_;
355
356  DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler);
357};
358
359#if defined(USE_TCMALLOC)
360std::string AboutTcmalloc(const std::string& query) {
361  std::string data;
362  AboutTcmallocOutputsType* outputs =
363      AboutTcmallocOutputs::GetInstance()->outputs();
364
365  // Display any stats for which we sent off requests the last time.
366  data.append("<html><head><title>About tcmalloc</title></head><body>\n");
367  data.append("<p>Stats as of last page load;");
368  data.append("reload to get stats as of this page load.</p>\n");
369  data.append("<table width=\"100%\">\n");
370  for (AboutTcmallocOutputsType::const_iterator oit = outputs->begin();
371       oit != outputs->end();
372       oit++) {
373    data.append("<tr><td bgcolor=\"yellow\">");
374    data.append(oit->first);
375    data.append("</td></tr>\n");
376    data.append("<tr><td><pre>\n");
377    data.append(oit->second);
378    data.append("</pre></td></tr>\n");
379  }
380  data.append("</table>\n");
381  data.append("</body></html>\n");
382
383  // Reset our collector singleton.
384  outputs->clear();
385
386  // Populate the collector with stats from the local browser process
387  // and send off requests to all the renderer processes.
388  char buffer[1024 * 32];
389  MallocExtension::instance()->GetStats(buffer, sizeof(buffer));
390  std::string browser("Browser");
391  AboutTcmallocOutputs::GetInstance()->SetOutput(browser, buffer);
392  RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
393  while (!it.IsAtEnd()) {
394    it.GetCurrentValue()->Send(new ViewMsg_GetRendererTcmalloc);
395    it.Advance();
396  }
397
398  return data;
399}
400#endif
401
402std::string AboutHistograms(const std::string& query) {
403  TimeDelta wait_time = TimeDelta::FromMilliseconds(10000);
404
405  HistogramSynchronizer* current_synchronizer =
406      HistogramSynchronizer::CurrentSynchronizer();
407  DCHECK(current_synchronizer != NULL);
408  current_synchronizer->FetchRendererHistogramsSynchronously(wait_time);
409
410  std::string data;
411  base::StatisticsRecorder::WriteHTMLGraph(query, &data);
412  return data;
413}
414
415void AboutMemory(AboutSource* source, int request_id) {
416  // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want the
417  // refcount to be greater than 0.
418  scoped_refptr<AboutMemoryHandler>
419      handler(new AboutMemoryHandler(source, request_id));
420  handler->StartFetch();
421}
422
423#ifdef TRACK_ALL_TASK_OBJECTS
424static std::string AboutObjects(const std::string& query) {
425  std::string data;
426  tracked_objects::ThreadData::WriteHTML(query, &data);
427  return data;
428}
429#endif  // TRACK_ALL_TASK_OBJECTS
430
431// Handler for filling in the "about:stats" page, as called by the browser's
432// About handler processing.
433// |query| is roughly the query string of the about:stats URL.
434// Returns a string containing the HTML to render for the about:stats page.
435// Conditional Output:
436//      if |query| is "json", returns a JSON format of all counters.
437//      if |query| is "raw", returns plain text of counter deltas.
438//      otherwise, returns HTML with pretty JS/HTML to display the data.
439std::string AboutStats(const std::string& query) {
440  // We keep the DictionaryValue tree live so that we can do delta
441  // stats computations across runs.
442  static DictionaryValue root;
443  static base::TimeTicks last_sample_time = base::TimeTicks::Now();
444
445  base::TimeTicks now = base::TimeTicks::Now();
446  base::TimeDelta time_since_last_sample = now - last_sample_time;
447  last_sample_time = now;
448
449  base::StatsTable* table = base::StatsTable::current();
450  if (!table)
451    return std::string();
452
453  // We maintain two lists - one for counters and one for timers.
454  // Timers actually get stored on both lists.
455  ListValue* counters;
456  if (!root.GetList("counters", &counters)) {
457    counters = new ListValue();
458    root.Set("counters", counters);
459  }
460
461  ListValue* timers;
462  if (!root.GetList("timers", &timers)) {
463    timers = new ListValue();
464    root.Set("timers", timers);
465  }
466
467  // NOTE: Counters start at index 1.
468  for (int index = 1; index <= table->GetMaxCounters(); index++) {
469    // Get the counter's full name
470    std::string full_name = table->GetRowName(index);
471    if (full_name.length() == 0)
472      break;
473    DCHECK_EQ(':', full_name[1]);
474    char counter_type = full_name[0];
475    std::string name = full_name.substr(2);
476
477    // JSON doesn't allow '.' in names.
478    size_t pos;
479    while ((pos = name.find(".")) != std::string::npos)
480      name.replace(pos, 1, ":");
481
482    // Try to see if this name already exists.
483    DictionaryValue* counter = NULL;
484    for (size_t scan_index = 0;
485         scan_index < counters->GetSize(); scan_index++) {
486      DictionaryValue* dictionary;
487      if (counters->GetDictionary(scan_index, &dictionary)) {
488        std::string scan_name;
489        if (dictionary->GetString("name", &scan_name) && scan_name == name) {
490          counter = dictionary;
491        }
492      } else {
493        NOTREACHED();  // Should always be there
494      }
495    }
496
497    if (counter == NULL) {
498      counter = new DictionaryValue();
499      counter->SetString("name", name);
500      counters->Append(counter);
501    }
502
503    switch (counter_type) {
504      case 'c':
505        {
506          int new_value = table->GetRowValue(index);
507          int prior_value = 0;
508          int delta = 0;
509          if (counter->GetInteger("value", &prior_value)) {
510            delta = new_value - prior_value;
511          }
512          counter->SetInteger("value", new_value);
513          counter->SetInteger("delta", delta);
514        }
515        break;
516      case 'm':
517        {
518          // TODO(mbelshe): implement me.
519        }
520        break;
521      case 't':
522        {
523          int time = table->GetRowValue(index);
524          counter->SetInteger("time", time);
525
526          // Store this on the timers list as well.
527          timers->Append(counter);
528        }
529        break;
530      default:
531        NOTREACHED();
532    }
533  }
534
535  std::string data;
536  if (query == "json") {
537    base::JSONWriter::WriteWithOptionalEscape(&root, true, false, &data);
538  } else if (query == "raw") {
539    // Dump the raw counters which have changed in text format.
540    data = "<pre>";
541    data.append(StringPrintf("Counter changes in the last %ldms\n",
542        static_cast<long int>(time_since_last_sample.InMilliseconds())));
543    for (size_t i = 0; i < counters->GetSize(); ++i) {
544      Value* entry = NULL;
545      bool rv = counters->Get(i, &entry);
546      if (!rv)
547        continue;  // None of these should fail.
548      DictionaryValue* counter = static_cast<DictionaryValue*>(entry);
549      int delta;
550      rv = counter->GetInteger("delta", &delta);
551      if (!rv)
552        continue;
553      if (delta > 0) {
554        std::string name;
555        rv = counter->GetString("name", &name);
556        if (!rv)
557          continue;
558        int value;
559        rv = counter->GetInteger("value", &value);
560        if (!rv)
561          continue;
562        data.append(name);
563        data.append(":");
564        data.append(base::IntToString(delta));
565        data.append("\n");
566      }
567    }
568    data.append("</pre>");
569  } else {
570    // Get about_stats.html and process a pretty page.
571    static const base::StringPiece stats_html(
572        ResourceBundle::GetSharedInstance().GetRawDataResource(
573            IDR_ABOUT_STATS_HTML));
574
575    // Create jstemplate and return.
576    data = jstemplate_builder::GetTemplateHtml(
577        stats_html, &root, "t" /* template root node id */);
578
579    // Clear the timer list since we stored the data in the timers list as well.
580    for (int index = static_cast<int>(timers->GetSize())-1; index >= 0;
581         index--) {
582      Value* value;
583      timers->Remove(index, &value);
584      // We don't care about the value pointer; it's still tracked
585      // on the counters list.
586    }
587  }
588
589  return data;
590}
591
592#if defined(OS_LINUX)
593std::string AboutLinuxProxyConfig() {
594  std::string data;
595  data.append("<!DOCTYPE HTML>\n");
596  data.append("<html><head><meta charset=\"utf-8\"><title>");
597  data.append(l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE));
598  data.append("</title>");
599  data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>");
600  data.append("</head><body>\n");
601  FilePath binary = CommandLine::ForCurrentProcess()->GetProgram();
602  data.append(l10n_util::GetStringFUTF8(
603                  IDS_ABOUT_LINUX_PROXY_CONFIG_BODY,
604                  l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
605                  ASCIIToUTF16(binary.BaseName().value())));
606  data.append("</body></html>\n");
607  return data;
608}
609
610void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id,
611                     bool good) {
612  data->append("<tr><td>");
613  data->append(prefix);
614  data->append(l10n_util::GetStringUTF8(name_id));
615  if (good) {
616    data->append("</td><td style=\"color: green;\">");
617    data->append(
618        l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL));
619  } else {
620    data->append("</td><td style=\"color: red;\">");
621    data->append(
622        l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL));
623  }
624  data->append("</td></tr>");
625}
626
627std::string AboutSandbox() {
628  std::string data;
629  data.append("<!DOCTYPE HTML>\n");
630  data.append("<html><head><meta charset=\"utf-8\"><title>");
631  data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
632  data.append("</title>");
633  data.append("</head><body>\n");
634  data.append("<h1>");
635  data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE));
636  data.append("</h1>");
637
638  const int status = ZygoteHost::GetInstance()->sandbox_status();
639
640  data.append("<table>");
641
642  AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SUID_SANDBOX,
643                  status & ZygoteHost::kSandboxSUID);
644  if (status & ZygoteHost::kSandboxPIDNS) {
645    AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_PID_NAMESPACES,
646                    status & ZygoteHost::kSandboxPIDNS);
647    AboutSandboxRow(&data, "&nbsp;&nbsp;", IDS_ABOUT_SANDBOX_NET_NAMESPACES,
648                    status & ZygoteHost::kSandboxNetNS);
649  }
650  AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SECCOMP_SANDBOX,
651                  status & ZygoteHost::kSandboxSeccomp);
652
653  data.append("</table>");
654
655  bool good = ((status & ZygoteHost::kSandboxSUID) &&
656               (status & ZygoteHost::kSandboxPIDNS)) ||
657              (status & ZygoteHost::kSandboxSeccomp);
658  if (good) {
659    data.append("<p style=\"color: green\">");
660    data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK));
661  } else {
662    data.append("<p style=\"color: red\">");
663    data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD));
664  }
665  data.append("</p>");
666
667  data.append("</body></html>\n");
668  return data;
669}
670#endif
671
672std::string AboutVersion(DictionaryValue* localized_strings) {
673  localized_strings->SetString("title",
674      l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_TITLE));
675  chrome::VersionInfo version_info;
676
677  std::string webkit_version = webkit_glue::GetWebKitVersion();
678#ifdef CHROME_V8
679  std::string js_version(v8::V8::GetVersion());
680  std::string js_engine = "V8";
681#else
682  std::string js_version = webkit_version;
683  std::string js_engine = "JavaScriptCore";
684#endif
685
686  localized_strings->SetString("name",
687      l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
688  localized_strings->SetString("version", version_info.Version());
689  localized_strings->SetString("version_modifier",
690                               platform_util::GetVersionStringModifier());
691  localized_strings->SetString("js_engine", js_engine);
692  localized_strings->SetString("js_version", js_version);
693  localized_strings->SetString("webkit_version", webkit_version);
694  localized_strings->SetString("company",
695      l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COMPANY_NAME));
696  localized_strings->SetString("copyright",
697      l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT));
698  localized_strings->SetString("cl", version_info.LastChange());
699  localized_strings->SetString("official",
700      l10n_util::GetStringUTF16(
701          version_info.IsOfficialBuild() ?
702              IDS_ABOUT_VERSION_OFFICIAL
703            : IDS_ABOUT_VERSION_UNOFFICIAL));
704  localized_strings->SetString("user_agent_name",
705      l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_USER_AGENT));
706  localized_strings->SetString("useragent", webkit_glue::GetUserAgent(GURL()));
707  localized_strings->SetString("command_line_name",
708      l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COMMAND_LINE));
709
710#if defined(OS_WIN)
711  localized_strings->SetString("command_line",
712      WideToUTF16(CommandLine::ForCurrentProcess()->command_line_string()));
713#elif defined(OS_POSIX)
714  std::string command_line = "";
715  typedef std::vector<std::string> ArgvList;
716  const ArgvList& argv = CommandLine::ForCurrentProcess()->argv();
717  for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++)
718    command_line += " " + *iter;
719  // TODO(viettrungluu): |command_line| could really have any encoding, whereas
720  // below we assumes it's UTF-8.
721  localized_strings->SetString("command_line", command_line);
722#endif
723
724  base::StringPiece version_html(
725      ResourceBundle::GetSharedInstance().GetRawDataResource(
726          IDR_ABOUT_VERSION_HTML));
727
728  return jstemplate_builder::GetTemplatesHtml(
729      version_html, localized_strings, "t" /* template root node id */);
730}
731
732std::string VersionNumberToString(uint32 value) {
733  int hi = (value >> 8) & 0xff;
734  int low = value & 0xff;
735  return base::IntToString(hi) + "." + base::IntToString(low);
736}
737
738// AboutSource -----------------------------------------------------------------
739
740AboutSource::AboutSource()
741    : DataSource(chrome::kAboutScheme, MessageLoop::current()) {
742}
743
744AboutSource::~AboutSource() {
745}
746
747void AboutSource::StartDataRequest(const std::string& path_raw,
748    bool is_off_the_record, int request_id) {
749  std::string path = path_raw;
750  std::string info;
751  if (path.find("/") != std::string::npos) {
752    size_t pos = path.find("/");
753    info = path.substr(pos + 1, path.length() - (pos + 1));
754    path = path.substr(0, pos);
755  }
756  path = StringToLowerASCII(path);
757
758  std::string response;
759  if (path == kDnsPath) {
760    AboutDnsHandler::Start(this, request_id);
761    return;
762  } else if (path == kHistogramsPath) {
763    response = AboutHistograms(info);
764  } else if (path == kMemoryPath) {
765    AboutMemory(this, request_id);
766    return;
767  } else if (path == kMemoryRedirectPath) {
768    response = GetAboutMemoryRedirectResponse();
769#ifdef TRACK_ALL_TASK_OBJECTS
770  } else if (path == kTasksPath) {
771    response = AboutObjects(info);
772#endif
773  } else if (path == kStatsPath) {
774    response = AboutStats(info);
775#if defined(USE_TCMALLOC)
776  } else if (path == kTcmallocPath) {
777    response = AboutTcmalloc(info);
778#endif
779  } else if (path == kVersionPath || path.empty()) {
780#if defined(OS_CHROMEOS)
781    new ChromeOSAboutVersionHandler(this, request_id);
782    return;
783#else
784    DictionaryValue value;
785    response = AboutVersion(&value);
786#endif
787  } else if (path == kCreditsPath) {
788    response = ResourceBundle::GetSharedInstance().GetRawDataResource(
789        IDR_CREDITS_HTML).as_string();
790  } else if (path == kAboutPath) {
791    response = AboutAbout();
792#if defined(OS_CHROMEOS)
793  } else if (path == kOSCreditsPath) {
794    response = ResourceBundle::GetSharedInstance().GetRawDataResource(
795        IDR_OS_CREDITS_HTML).as_string();
796  } else if (path == kNetworkPath) {
797    response = AboutNetwork(info);
798#endif
799  } else if (path == kTermsPath) {
800    response = ResourceBundle::GetSharedInstance().GetRawDataResource(
801        IDR_TERMS_HTML).as_string();
802#if defined(OS_LINUX)
803  } else if (path == kLinuxProxyConfigPath) {
804    response = AboutLinuxProxyConfig();
805  } else if (path == kSandboxPath) {
806    response = AboutSandbox();
807#endif
808  }
809
810  FinishDataRequest(response, request_id);
811}
812
813void AboutSource::FinishDataRequest(const std::string& response,
814                                    int request_id) {
815  scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes);
816  html_bytes->data.resize(response.size());
817  std::copy(response.begin(), response.end(), html_bytes->data.begin());
818  SendResponse(request_id, html_bytes);
819}
820
821// AboutMemoryHandler ----------------------------------------------------------
822
823// Helper for AboutMemory to bind results from a ProcessMetrics object
824// to a DictionaryValue. Fills ws_usage and comm_usage so that the objects
825// can be used in caller's scope (e.g for appending to a net total).
826void AboutMemoryHandler::BindProcessMetrics(DictionaryValue* data,
827                                            ProcessMemoryInformation* info) {
828  DCHECK(data && info);
829
830  // Bind metrics to dictionary.
831  data->SetInteger("ws_priv", static_cast<int>(info->working_set.priv));
832  data->SetInteger("ws_shareable",
833    static_cast<int>(info->working_set.shareable));
834  data->SetInteger("ws_shared", static_cast<int>(info->working_set.shared));
835  data->SetInteger("comm_priv", static_cast<int>(info->committed.priv));
836  data->SetInteger("comm_map", static_cast<int>(info->committed.mapped));
837  data->SetInteger("comm_image", static_cast<int>(info->committed.image));
838  data->SetInteger("pid", info->pid);
839  data->SetString("version", info->version);
840  data->SetInteger("processes", info->num_processes);
841}
842
843// Helper for AboutMemory to append memory usage information for all
844// sub-processes (i.e. renderers, plugins) used by Chrome.
845void AboutMemoryHandler::AppendProcess(ListValue* child_data,
846                                       ProcessMemoryInformation* info) {
847  DCHECK(child_data && info);
848
849  // Append a new DictionaryValue for this renderer to our list.
850  DictionaryValue* child = new DictionaryValue();
851  child_data->Append(child);
852  BindProcessMetrics(child, info);
853
854  std::string child_label(
855      ChildProcessInfo::GetFullTypeNameInEnglish(info->type,
856                                                 info->renderer_type));
857  if (info->is_diagnostics)
858    child_label.append(" (diagnostics)");
859  child->SetString("child_name", child_label);
860  ListValue* titles = new ListValue();
861  child->Set("titles", titles);
862  for (size_t i = 0; i < info->titles.size(); ++i)
863    titles->Append(new StringValue(info->titles[i]));
864}
865
866
867void AboutMemoryHandler::OnDetailsAvailable() {
868  // the root of the JSON hierarchy for about:memory jstemplate
869  DictionaryValue root;
870  ListValue* browsers = new ListValue();
871  root.Set("browsers", browsers);
872
873  const std::vector<ProcessData>& browser_processes = processes();
874
875  // Aggregate per-process data into browser summary data.
876  std::wstring log_string;
877  for (size_t index = 0; index < browser_processes.size(); index++) {
878    if (browser_processes[index].processes.size() == 0)
879      continue;
880
881    // Sum the information for the processes within this browser.
882    ProcessMemoryInformation aggregate;
883    ProcessMemoryInformationList::const_iterator iterator;
884    iterator = browser_processes[index].processes.begin();
885    aggregate.pid = iterator->pid;
886    aggregate.version = iterator->version;
887    while (iterator != browser_processes[index].processes.end()) {
888      if (!iterator->is_diagnostics ||
889          browser_processes[index].processes.size() == 1) {
890        aggregate.working_set.priv += iterator->working_set.priv;
891        aggregate.working_set.shared += iterator->working_set.shared;
892        aggregate.working_set.shareable += iterator->working_set.shareable;
893        aggregate.committed.priv += iterator->committed.priv;
894        aggregate.committed.mapped += iterator->committed.mapped;
895        aggregate.committed.image += iterator->committed.image;
896        aggregate.num_processes++;
897      }
898      ++iterator;
899    }
900    DictionaryValue* browser_data = new DictionaryValue();
901    browsers->Append(browser_data);
902    browser_data->SetString("name", browser_processes[index].name);
903
904    BindProcessMetrics(browser_data, &aggregate);
905
906    // We log memory info as we record it.
907    if (log_string.length() > 0)
908      log_string.append(L", ");
909    log_string.append(UTF16ToWide(browser_processes[index].name));
910    log_string.append(L", ");
911    log_string.append(UTF8ToWide(
912        base::Int64ToString(aggregate.working_set.priv)));
913    log_string.append(L", ");
914    log_string.append(UTF8ToWide(
915        base::Int64ToString(aggregate.working_set.shared)));
916    log_string.append(L", ");
917    log_string.append(UTF8ToWide(
918        base::Int64ToString(aggregate.working_set.shareable)));
919  }
920  if (log_string.length() > 0)
921    VLOG(1) << "memory: " << log_string;
922
923  // Set the browser & renderer detailed process data.
924  DictionaryValue* browser_data = new DictionaryValue();
925  root.Set("browzr_data", browser_data);
926  ListValue* child_data = new ListValue();
927  root.Set("child_data", child_data);
928
929  ProcessData process = browser_processes[0];  // Chrome is the first browser.
930  root.SetString("current_browser_name", process.name);
931
932  for (size_t index = 0; index < process.processes.size(); index++) {
933    if (process.processes[index].type == ChildProcessInfo::BROWSER_PROCESS)
934      BindProcessMetrics(browser_data, &process.processes[index]);
935    else
936      AppendProcess(child_data, &process.processes[index]);
937  }
938
939  root.SetBoolean("show_other_browsers",
940      browser_defaults::kShowOtherBrowsersInAboutMemory);
941
942  // Get about_memory.html
943  static const base::StringPiece memory_html(
944      ResourceBundle::GetSharedInstance().GetRawDataResource(
945          IDR_ABOUT_MEMORY_HTML));
946
947  // Create jstemplate and return.
948  std::string template_html = jstemplate_builder::GetTemplateHtml(
949      memory_html, &root, "t" /* template root node id */);
950
951  source_->FinishDataRequest(template_html, request_id_);
952}
953
954#if defined(OS_CHROMEOS)
955// ChromeOSAboutVersionHandler  -----------------------------------------------
956
957ChromeOSAboutVersionHandler::ChromeOSAboutVersionHandler(AboutSource* source,
958                                                         int request_id)
959    : source_(source),
960      request_id_(request_id) {
961  loader_.GetVersion(&consumer_,
962                     NewCallback(this, &ChromeOSAboutVersionHandler::OnVersion),
963                     chromeos::VersionLoader::VERSION_FULL);
964}
965
966void ChromeOSAboutVersionHandler::OnVersion(
967    chromeos::VersionLoader::Handle handle,
968    std::string version) {
969  DictionaryValue localized_strings;
970  localized_strings.SetString("os_name",
971                              l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME));
972  localized_strings.SetString("os_version", version);
973  localized_strings.SetBoolean("is_chrome_os", true);
974  source_->FinishDataRequest(AboutVersion(&localized_strings), request_id_);
975
976  // CancelableRequestProvider isn't happy when it's deleted and servicing a
977  // task, so we delay the deletion.
978  MessageLoop::current()->DeleteSoon(FROM_HERE, this);
979}
980
981#endif
982
983// Returns true if |url|'s spec starts with |about_specifier|, and is
984// terminated by the start of a path.
985bool StartsWithAboutSpecifier(const GURL& url, const char* about_specifier) {
986  return StartsWithASCII(url.spec(), about_specifier, true) &&
987         (url.spec().size() == strlen(about_specifier) ||
988          url.spec()[strlen(about_specifier)] == '/');
989}
990
991// Transforms a URL of the form "about:foo/XXX" to <url_prefix> + "XXX".
992GURL RemapAboutURL(const std::string& url_prefix, const GURL& url) {
993  std::string path;
994  size_t split = url.spec().find('/');
995  if (split != std::string::npos)
996    path = url.spec().substr(split + 1);
997  return GURL(url_prefix + path);
998}
999
1000}  // namespace
1001
1002// -----------------------------------------------------------------------------
1003
1004bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) {
1005  // We only handle about: schemes.
1006  if (!url->SchemeIs(chrome::kAboutScheme))
1007    return false;
1008
1009  // about:blank is special. Frames are allowed to access about:blank,
1010  // but they are not allowed to access other types of about pages.
1011  // Just ignore the about:blank and let the TAB_CONTENTS_WEB handle it.
1012  if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutBlankURL))
1013    return false;
1014
1015  // Rewrite about:cache/* URLs to chrome://view-http-cache/*
1016  if (StartsWithAboutSpecifier(*url, chrome::kAboutCacheURL)) {
1017    *url = RemapAboutURL(chrome::kNetworkViewCacheURL, *url);
1018    return true;
1019  }
1020
1021#if defined(OS_WIN)
1022  // Rewrite about:conflicts/* URLs to chrome://conflicts/*
1023  if (StartsWithAboutSpecifier(*url, chrome::kAboutConflicts)) {
1024    *url = GURL(chrome::kChromeUIConflictsURL);
1025    return true;
1026  }
1027#endif
1028
1029  // Rewrite about:flags and about:vaporware to chrome://flags/.
1030  if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutFlagsURL) ||
1031      LowerCaseEqualsASCII(url->spec(), chrome::kAboutVaporwareURL)) {
1032    *url = GURL(chrome::kChromeUIFlagsURL);
1033    return true;
1034  }
1035
1036  // Rewrite about:net-internals/* URLs to chrome://net-internals/*
1037  if (StartsWithAboutSpecifier(*url, chrome::kAboutNetInternalsURL)) {
1038    *url = RemapAboutURL(chrome::kNetworkViewInternalsURL, *url);
1039    return true;
1040  }
1041
1042  // Rewrite about:gpu/* URLs to chrome://gpu-internals/*
1043  if (StartsWithAboutSpecifier(*url, chrome::kAboutGpuURL)) {
1044    *url = RemapAboutURL(chrome::kGpuInternalsURL, *url);
1045    return true;
1046  }
1047
1048  // Rewrite about:appcache-internals/* URLs to chrome://appcache/*
1049  if (StartsWithAboutSpecifier(*url, chrome::kAboutAppCacheInternalsURL)) {
1050    *url = RemapAboutURL(chrome::kAppCacheViewInternalsURL, *url);
1051    return true;
1052  }
1053
1054  // Rewrite about:sync-internals/* URLs (and about:sync, too, for
1055  // legacy reasons) to chrome://sync-internals/*
1056  if (StartsWithAboutSpecifier(*url, chrome::kAboutSyncInternalsURL) ||
1057      StartsWithAboutSpecifier(*url, chrome::kAboutSyncURL)) {
1058    *url = RemapAboutURL(chrome::kSyncViewInternalsURL, *url);
1059    return true;
1060  }
1061
1062  // Rewrite about:plugins to chrome://plugins/.
1063  if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutPluginsURL)) {
1064    *url = GURL(chrome::kChromeUIPluginsURL);
1065    return true;
1066  }
1067
1068  // Handle URL to crash the browser process.
1069  if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutBrowserCrash)) {
1070    // Induce an intentional crash in the browser process.
1071    int* bad_pointer = NULL;
1072    *bad_pointer = 42;
1073    return true;
1074  }
1075
1076  // Handle URLs to wreck the gpu process.
1077  if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuCrashURL)) {
1078    GpuProcessHostUIShim::GetInstance()->SendAboutGpuCrash();
1079    return true;
1080  }
1081  if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuHangURL)) {
1082    GpuProcessHostUIShim::GetInstance()->SendAboutGpuHang();
1083    return true;
1084  }
1085
1086  // There are a few about: URLs that we hand over to the renderer. If the
1087  // renderer wants them, don't do any rewriting.
1088  if (chrome_about_handler::WillHandle(*url))
1089    return false;
1090
1091  // Anything else requires our special handler; make sure it's initialized.
1092  InitializeAboutDataSource(profile);
1093
1094  // Special case about:memory to go through a redirect before ending up on
1095  // the final page. See GetAboutMemoryRedirectResponse above for why.
1096  if (LowerCaseEqualsASCII(url->path(), kMemoryPath)) {
1097    *url = GURL("chrome://about/memory-redirect");
1098    return true;
1099  }
1100
1101  // Rewrite the about URL to use chrome:. WebKit treats all about URLS the
1102  // same (blank page), so if we want to display content, we need another
1103  // scheme.
1104  std::string about_url = "chrome://about/";
1105  about_url.append(url->path());
1106  *url = GURL(about_url);
1107  return true;
1108}
1109
1110void InitializeAboutDataSource(Profile* profile) {
1111  profile->GetChromeURLDataManager()->AddDataSource(new AboutSource());
1112}
1113
1114// This function gets called with the fixed-up chrome: URLs, so we have to
1115// compare against those instead of "about:blah".
1116bool HandleNonNavigationAboutURL(const GURL& url) {
1117  // about:ipc is currently buggy, so we disable it for official builds.
1118#if !defined(OFFICIAL_BUILD)
1119
1120#if (defined(OS_MACOSX) || defined(OS_WIN)) && defined(IPC_MESSAGE_LOG_ENABLED)
1121  if (LowerCaseEqualsASCII(url.spec(), chrome::kChromeUIIPCURL)) {
1122    // Run the dialog. This will re-use the existing one if it's already up.
1123    browser::ShowAboutIPCDialog();
1124    return true;
1125  }
1126#endif
1127
1128#endif  // OFFICIAL_BUILD
1129
1130  return false;
1131}
1132