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