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