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