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