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(" ", " ")); 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, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES, 815 status & content::kSandboxLinuxPIDNS); 816 AboutSandboxRow(&data, " ", 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