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