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