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