browser_about_handler.cc revision 72a454cd3513ac24fbdd0e0cb9ad70b86a99b801
1// Copyright (c) 2011 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/browser_about_handler.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/callback.h" 12#include "base/command_line.h" 13#include "base/i18n/number_formatting.h" 14#include "base/json/json_writer.h" 15#include "base/metrics/histogram.h" 16#include "base/metrics/stats_table.h" 17#include "base/path_service.h" 18#include "base/singleton.h" 19#include "base/stringprintf.h" 20#include "base/string_number_conversions.h" 21#include "base/string_piece.h" 22#include "base/string_util.h" 23#include "base/threading/thread.h" 24#include "base/tracked_objects.h" 25#include "base/utf_string_conversions.h" 26#include "base/values.h" 27#include "chrome/browser/about_flags.h" 28#include "chrome/browser/browser_process.h" 29#include "chrome/browser/browser_thread.h" 30#include "chrome/browser/defaults.h" 31#include "chrome/browser/dom_ui/chrome_url_data_manager.h" 32#include "chrome/browser/gpu_process_host.h" 33#include "chrome/browser/gpu_process_host_ui_shim.h" 34#include "chrome/browser/memory_details.h" 35#include "chrome/browser/metrics/histogram_synchronizer.h" 36#include "chrome/browser/net/predictor_api.h" 37#include "chrome/browser/platform_util.h" 38#include "chrome/browser/profiles/profile.h" 39#include "chrome/browser/profiles/profile_manager.h" 40#include "chrome/browser/renderer_host/render_process_host.h" 41#include "chrome/browser/renderer_host/render_view_host.h" 42#include "chrome/browser/ui/browser_dialogs.h" 43#include "chrome/common/about_handler.h" 44#include "chrome/common/chrome_paths.h" 45#include "chrome/common/chrome_version_info.h" 46#include "chrome/common/gpu_info.h" 47#include "chrome/common/jstemplate_builder.h" 48#include "chrome/common/net/gaia/google_service_auth_error.h" 49#include "chrome/common/render_messages.h" 50#include "chrome/common/url_constants.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 "webkit/glue/webkit_glue.h" 57#include "net/base/escape.h" 58#include "ui/base/l10n/l10n_util.h" 59#include "ui/base/resource/resource_bundle.h" 60#ifdef CHROME_V8 61#include "v8/include/v8.h" 62#endif 63 64#if defined(OS_WIN) 65#include "chrome/browser/enumerate_modules_model_win.h" 66#elif defined(OS_CHROMEOS) 67#include "chrome/browser/chromeos/cros/cros_library.h" 68#include "chrome/browser/chromeos/cros/network_library.h" 69#include "chrome/browser/chromeos/cros/syslogs_library.h" 70#include "chrome/browser/chromeos/version_loader.h" 71#include "chrome/browser/zygote_host_linux.h" 72#elif defined(OS_LINUX) 73#include "chrome/browser/zygote_host_linux.h" 74#endif 75 76#if defined(USE_TCMALLOC) 77#include "third_party/tcmalloc/chromium/src/google/malloc_extension.h" 78#endif 79 80using base::Time; 81using base::TimeDelta; 82 83#if defined(USE_TCMALLOC) 84// static 85AboutTcmallocOutputs* AboutTcmallocOutputs::GetInstance() { 86 return Singleton<AboutTcmallocOutputs>::get(); 87} 88 89// Glue between the callback task and the method in the singleton. 90void AboutTcmallocRendererCallback(base::ProcessId pid, std::string output) { 91 AboutTcmallocOutputs::GetInstance()->RendererCallback(pid, output); 92} 93#endif 94 95namespace { 96 97// The (alphabetized) paths used for the about pages. 98// Note: Keep these in sync with url_constants.h 99const char kAppCacheInternalsPath[] = "appcache-internals"; 100const char kBlobInternalsPath[] = "blob-internals"; 101const char kCreditsPath[] = "credits"; 102const char kCachePath[] = "view-http-cache"; 103#if defined(OS_WIN) 104const char kConflictsPath[] = "conflicts"; 105#endif 106const char kDnsPath[] = "dns"; 107const char kFlagsPath[] = "flags"; 108const char kGpuPath[] = "gpu"; 109const char kHistogramsPath[] = "histograms"; 110const char kMemoryRedirectPath[] = "memory-redirect"; 111const char kMemoryPath[] = "memory"; 112const char kStatsPath[] = "stats"; 113const char kTasksPath[] = "tasks"; 114const char kTcmallocPath[] = "tcmalloc"; 115const char kTermsPath[] = "terms"; 116const char kVersionPath[] = "version"; 117const char kAboutPath[] = "about"; 118// Not about:* pages, but included to make about:about look nicer 119const char kNetInternalsPath[] = "net-internals"; 120const char kPluginsPath[] = "plugins"; 121const char kSyncInternalsPath[] = "sync-internals"; 122 123#if defined(OS_LINUX) 124const char kLinuxProxyConfigPath[] = "linux-proxy-config"; 125const char kSandboxPath[] = "sandbox"; 126#endif 127 128#if defined(OS_CHROMEOS) 129const char kNetworkPath[] = "network"; 130const char kOSCreditsPath[] = "os-credits"; 131#endif 132 133// Add path here to be included in about:about 134const char *kAllAboutPaths[] = { 135 kAppCacheInternalsPath, 136 kBlobInternalsPath, 137 kCachePath, 138 kCreditsPath, 139#if defined(OS_WIN) 140 kConflictsPath, 141#endif 142 kDnsPath, 143 kFlagsPath, 144 kGpuPath, 145 kHistogramsPath, 146 kMemoryPath, 147 kNetInternalsPath, 148 kPluginsPath, 149 kStatsPath, 150 kSyncInternalsPath, 151 kTasksPath, 152 kTcmallocPath, 153 kTermsPath, 154 kVersionPath, 155#if defined(OS_LINUX) 156 kSandboxPath, 157#endif 158#if defined(OS_CHROMEOS) 159 kNetworkPath, 160 kOSCreditsPath, 161#endif 162 }; 163 164// When you type about:memory, it actually loads an intermediate URL that 165// redirects you to the final page. This avoids the problem where typing 166// "about:memory" on the new tab page or any other page where a process 167// transition would occur to the about URL will cause some confusion. 168// 169// The problem is that during the processing of the memory page, there are two 170// processes active, the original and the destination one. This can create the 171// impression that we're using more resources than we actually are. This 172// redirect solves the problem by eliminating the process transition during the 173// time that about memory is being computed. 174std::string GetAboutMemoryRedirectResponse() { 175 return "<meta http-equiv=\"refresh\" " 176 "content=\"0;chrome://about/memory\">"; 177} 178 179class AboutSource : public ChromeURLDataManager::DataSource { 180 public: 181 // Creates our datasource. 182 AboutSource(); 183 184 // Called when the network layer has requested a resource underneath 185 // the path we registered. 186 virtual void StartDataRequest(const std::string& path, 187 bool is_off_the_record, 188 int request_id); 189 190 virtual std::string GetMimeType(const std::string&) const { 191 return "text/html"; 192 } 193 194 // Send the response data. 195 void FinishDataRequest(const std::string& html, int request_id); 196 197 private: 198 virtual ~AboutSource(); 199 200 DISALLOW_COPY_AND_ASSIGN(AboutSource); 201}; 202 203// Handling about:memory is complicated enough to encapsulate its related 204// methods into a single class. The user should create it (on the heap) and call 205// its |StartFetch()| method. 206class AboutMemoryHandler : public MemoryDetails { 207 public: 208 AboutMemoryHandler(AboutSource* source, int request_id) 209 : source_(source), request_id_(request_id) {} 210 211 212 virtual void OnDetailsAvailable(); 213 214 private: 215 ~AboutMemoryHandler() {} 216 217 void BindProcessMetrics(DictionaryValue* data, 218 ProcessMemoryInformation* info); 219 void AppendProcess(ListValue* child_data, ProcessMemoryInformation* info); 220 221 scoped_refptr<AboutSource> source_; 222 int request_id_; 223 224 DISALLOW_COPY_AND_ASSIGN(AboutMemoryHandler); 225}; 226 227#if defined(OS_CHROMEOS) 228// ChromeOSAboutVersionHandler is responsible for loading the Chrome OS 229// version. 230// ChromeOSAboutVersionHandler handles deleting itself once the version has 231// been obtained and AboutSource notified. 232class ChromeOSAboutVersionHandler { 233 public: 234 ChromeOSAboutVersionHandler(AboutSource* source, int request_id); 235 236 // Callback from chromeos::VersionLoader giving the version. 237 void OnVersion(chromeos::VersionLoader::Handle handle, 238 std::string version); 239 240 private: 241 // Where the results are fed to. 242 scoped_refptr<AboutSource> source_; 243 244 // ID identifying the request. 245 int request_id_; 246 247 // Handles asynchronously loading the version. 248 chromeos::VersionLoader loader_; 249 250 // Used to request the version. 251 CancelableRequestConsumer consumer_; 252 253 DISALLOW_COPY_AND_ASSIGN(ChromeOSAboutVersionHandler); 254}; 255#endif 256 257// Individual about handlers --------------------------------------------------- 258 259std::string AboutAbout() { 260 std::string html; 261 html.append("<html><head><title>About Pages</title></head><body>\n"); 262 html.append("<h2>List of About pages</h2><ul>\n"); 263 for (size_t i = 0; i < arraysize(kAllAboutPaths); i++) { 264 if (kAllAboutPaths[i] == kAppCacheInternalsPath || 265 kAllAboutPaths[i] == kBlobInternalsPath || 266 kAllAboutPaths[i] == kCachePath || 267#if defined(OS_WIN) 268 kAllAboutPaths[i] == kConflictsPath || 269#endif 270 kAllAboutPaths[i] == kFlagsPath || 271 kAllAboutPaths[i] == kGpuPath || 272 kAllAboutPaths[i] == kNetInternalsPath || 273 kAllAboutPaths[i] == kPluginsPath) { 274 html.append("<li><a href='chrome://"); 275 } else { 276 html.append("<li><a href='chrome://about/"); 277 } 278 html.append(kAllAboutPaths[i]); 279 html.append("/'>about:"); 280 html.append(kAllAboutPaths[i]); 281 html.append("</a>\n"); 282 } 283 const char *debug[] = { "crash", "kill", "hang", "shorthang", 284 "gpucrash", "gpuhang" }; 285 html.append("</ul><h2>For Debug</h2>"); 286 html.append("</ul><p>The following pages are for debugging purposes only. " 287 "Because they crash or hang the renderer, they're not linked " 288 "directly; you can type them into the address bar if you need " 289 "them.</p><ul>"); 290 for (size_t i = 0; i < arraysize(debug); i++) { 291 html.append("<li>"); 292 html.append("about:"); 293 html.append(debug[i]); 294 html.append("\n"); 295 } 296 html.append("</ul></body></html>"); 297 return html; 298} 299 300#if defined(OS_CHROMEOS) 301std::string AboutNetwork(const std::string& query) { 302 int refresh; 303 base::StringToInt(query, &refresh); 304 return chromeos::CrosLibrary::Get()->GetNetworkLibrary()-> 305 GetHtmlInfo(refresh); 306} 307#endif 308 309// AboutDnsHandler bounces the request back to the IO thread to collect 310// the DNS information. 311class AboutDnsHandler : public base::RefCountedThreadSafe<AboutDnsHandler> { 312 public: 313 static void Start(AboutSource* source, int request_id) { 314 scoped_refptr<AboutDnsHandler> handler( 315 new AboutDnsHandler(source, request_id)); 316 handler->StartOnUIThread(); 317 } 318 319 private: 320 AboutDnsHandler(AboutSource* source, int request_id) 321 : source_(source), 322 request_id_(request_id) { 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 324 } 325 326 // Calls FinishOnUIThread() on completion. 327 void StartOnUIThread() { 328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 329 BrowserThread::PostTask( 330 BrowserThread::IO, FROM_HERE, 331 NewRunnableMethod(this, &AboutDnsHandler::StartOnIOThread)); 332 } 333 334 void StartOnIOThread() { 335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 336 337 std::string data; 338 chrome_browser_net::PredictorGetHtmlInfo(&data); 339 340 BrowserThread::PostTask( 341 BrowserThread::UI, FROM_HERE, 342 NewRunnableMethod(this, &AboutDnsHandler::FinishOnUIThread, data)); 343 } 344 345 void FinishOnUIThread(const std::string& data) { 346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 347 source_->FinishDataRequest(data, request_id_); 348 } 349 350 // Where the results are fed to. 351 scoped_refptr<AboutSource> source_; 352 353 // ID identifying the request. 354 int request_id_; 355 356 DISALLOW_COPY_AND_ASSIGN(AboutDnsHandler); 357}; 358 359#if defined(USE_TCMALLOC) 360std::string AboutTcmalloc(const std::string& query) { 361 std::string data; 362 AboutTcmallocOutputsType* outputs = 363 AboutTcmallocOutputs::GetInstance()->outputs(); 364 365 // Display any stats for which we sent off requests the last time. 366 data.append("<html><head><title>About tcmalloc</title></head><body>\n"); 367 data.append("<p>Stats as of last page load;"); 368 data.append("reload to get stats as of this page load.</p>\n"); 369 data.append("<table width=\"100%\">\n"); 370 for (AboutTcmallocOutputsType::const_iterator oit = outputs->begin(); 371 oit != outputs->end(); 372 oit++) { 373 data.append("<tr><td bgcolor=\"yellow\">"); 374 data.append(oit->first); 375 data.append("</td></tr>\n"); 376 data.append("<tr><td><pre>\n"); 377 data.append(oit->second); 378 data.append("</pre></td></tr>\n"); 379 } 380 data.append("</table>\n"); 381 data.append("</body></html>\n"); 382 383 // Reset our collector singleton. 384 outputs->clear(); 385 386 // Populate the collector with stats from the local browser process 387 // and send off requests to all the renderer processes. 388 char buffer[1024 * 32]; 389 MallocExtension::instance()->GetStats(buffer, sizeof(buffer)); 390 std::string browser("Browser"); 391 AboutTcmallocOutputs::GetInstance()->SetOutput(browser, buffer); 392 RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator()); 393 while (!it.IsAtEnd()) { 394 it.GetCurrentValue()->Send(new ViewMsg_GetRendererTcmalloc); 395 it.Advance(); 396 } 397 398 return data; 399} 400#endif 401 402std::string AboutHistograms(const std::string& query) { 403 TimeDelta wait_time = TimeDelta::FromMilliseconds(10000); 404 405 HistogramSynchronizer* current_synchronizer = 406 HistogramSynchronizer::CurrentSynchronizer(); 407 DCHECK(current_synchronizer != NULL); 408 current_synchronizer->FetchRendererHistogramsSynchronously(wait_time); 409 410 std::string data; 411 base::StatisticsRecorder::WriteHTMLGraph(query, &data); 412 return data; 413} 414 415void AboutMemory(AboutSource* source, int request_id) { 416 // The AboutMemoryHandler cleans itself up, but |StartFetch()| will want the 417 // refcount to be greater than 0. 418 scoped_refptr<AboutMemoryHandler> 419 handler(new AboutMemoryHandler(source, request_id)); 420 handler->StartFetch(); 421} 422 423#ifdef TRACK_ALL_TASK_OBJECTS 424static std::string AboutObjects(const std::string& query) { 425 std::string data; 426 tracked_objects::ThreadData::WriteHTML(query, &data); 427 return data; 428} 429#endif // TRACK_ALL_TASK_OBJECTS 430 431// Handler for filling in the "about:stats" page, as called by the browser's 432// About handler processing. 433// |query| is roughly the query string of the about:stats URL. 434// Returns a string containing the HTML to render for the about:stats page. 435// Conditional Output: 436// if |query| is "json", returns a JSON format of all counters. 437// if |query| is "raw", returns plain text of counter deltas. 438// otherwise, returns HTML with pretty JS/HTML to display the data. 439std::string AboutStats(const std::string& query) { 440 // We keep the DictionaryValue tree live so that we can do delta 441 // stats computations across runs. 442 static DictionaryValue root; 443 static base::TimeTicks last_sample_time = base::TimeTicks::Now(); 444 445 base::TimeTicks now = base::TimeTicks::Now(); 446 base::TimeDelta time_since_last_sample = now - last_sample_time; 447 last_sample_time = now; 448 449 base::StatsTable* table = base::StatsTable::current(); 450 if (!table) 451 return std::string(); 452 453 // We maintain two lists - one for counters and one for timers. 454 // Timers actually get stored on both lists. 455 ListValue* counters; 456 if (!root.GetList("counters", &counters)) { 457 counters = new ListValue(); 458 root.Set("counters", counters); 459 } 460 461 ListValue* timers; 462 if (!root.GetList("timers", &timers)) { 463 timers = new ListValue(); 464 root.Set("timers", timers); 465 } 466 467 // NOTE: Counters start at index 1. 468 for (int index = 1; index <= table->GetMaxCounters(); index++) { 469 // Get the counter's full name 470 std::string full_name = table->GetRowName(index); 471 if (full_name.length() == 0) 472 break; 473 DCHECK_EQ(':', full_name[1]); 474 char counter_type = full_name[0]; 475 std::string name = full_name.substr(2); 476 477 // JSON doesn't allow '.' in names. 478 size_t pos; 479 while ((pos = name.find(".")) != std::string::npos) 480 name.replace(pos, 1, ":"); 481 482 // Try to see if this name already exists. 483 DictionaryValue* counter = NULL; 484 for (size_t scan_index = 0; 485 scan_index < counters->GetSize(); scan_index++) { 486 DictionaryValue* dictionary; 487 if (counters->GetDictionary(scan_index, &dictionary)) { 488 std::string scan_name; 489 if (dictionary->GetString("name", &scan_name) && scan_name == name) { 490 counter = dictionary; 491 } 492 } else { 493 NOTREACHED(); // Should always be there 494 } 495 } 496 497 if (counter == NULL) { 498 counter = new DictionaryValue(); 499 counter->SetString("name", name); 500 counters->Append(counter); 501 } 502 503 switch (counter_type) { 504 case 'c': 505 { 506 int new_value = table->GetRowValue(index); 507 int prior_value = 0; 508 int delta = 0; 509 if (counter->GetInteger("value", &prior_value)) { 510 delta = new_value - prior_value; 511 } 512 counter->SetInteger("value", new_value); 513 counter->SetInteger("delta", delta); 514 } 515 break; 516 case 'm': 517 { 518 // TODO(mbelshe): implement me. 519 } 520 break; 521 case 't': 522 { 523 int time = table->GetRowValue(index); 524 counter->SetInteger("time", time); 525 526 // Store this on the timers list as well. 527 timers->Append(counter); 528 } 529 break; 530 default: 531 NOTREACHED(); 532 } 533 } 534 535 std::string data; 536 if (query == "json") { 537 base::JSONWriter::WriteWithOptionalEscape(&root, true, false, &data); 538 } else if (query == "raw") { 539 // Dump the raw counters which have changed in text format. 540 data = "<pre>"; 541 data.append(StringPrintf("Counter changes in the last %ldms\n", 542 static_cast<long int>(time_since_last_sample.InMilliseconds()))); 543 for (size_t i = 0; i < counters->GetSize(); ++i) { 544 Value* entry = NULL; 545 bool rv = counters->Get(i, &entry); 546 if (!rv) 547 continue; // None of these should fail. 548 DictionaryValue* counter = static_cast<DictionaryValue*>(entry); 549 int delta; 550 rv = counter->GetInteger("delta", &delta); 551 if (!rv) 552 continue; 553 if (delta > 0) { 554 std::string name; 555 rv = counter->GetString("name", &name); 556 if (!rv) 557 continue; 558 int value; 559 rv = counter->GetInteger("value", &value); 560 if (!rv) 561 continue; 562 data.append(name); 563 data.append(":"); 564 data.append(base::IntToString(delta)); 565 data.append("\n"); 566 } 567 } 568 data.append("</pre>"); 569 } else { 570 // Get about_stats.html and process a pretty page. 571 static const base::StringPiece stats_html( 572 ResourceBundle::GetSharedInstance().GetRawDataResource( 573 IDR_ABOUT_STATS_HTML)); 574 575 // Create jstemplate and return. 576 data = jstemplate_builder::GetTemplateHtml( 577 stats_html, &root, "t" /* template root node id */); 578 579 // Clear the timer list since we stored the data in the timers list as well. 580 for (int index = static_cast<int>(timers->GetSize())-1; index >= 0; 581 index--) { 582 Value* value; 583 timers->Remove(index, &value); 584 // We don't care about the value pointer; it's still tracked 585 // on the counters list. 586 } 587 } 588 589 return data; 590} 591 592#if defined(OS_LINUX) 593std::string AboutLinuxProxyConfig() { 594 std::string data; 595 data.append("<!DOCTYPE HTML>\n"); 596 data.append("<html><head><meta charset=\"utf-8\"><title>"); 597 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_LINUX_PROXY_CONFIG_TITLE)); 598 data.append("</title>"); 599 data.append("<style>body { max-width: 70ex; padding: 2ex 5ex; }</style>"); 600 data.append("</head><body>\n"); 601 FilePath binary = CommandLine::ForCurrentProcess()->GetProgram(); 602 data.append(l10n_util::GetStringFUTF8( 603 IDS_ABOUT_LINUX_PROXY_CONFIG_BODY, 604 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 605 ASCIIToUTF16(binary.BaseName().value()))); 606 data.append("</body></html>\n"); 607 return data; 608} 609 610void AboutSandboxRow(std::string* data, const std::string& prefix, int name_id, 611 bool good) { 612 data->append("<tr><td>"); 613 data->append(prefix); 614 data->append(l10n_util::GetStringUTF8(name_id)); 615 if (good) { 616 data->append("</td><td style=\"color: green;\">"); 617 data->append( 618 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_YES_BUTTON_LABEL)); 619 } else { 620 data->append("</td><td style=\"color: red;\">"); 621 data->append( 622 l10n_util::GetStringUTF8(IDS_CONFIRM_MESSAGEBOX_NO_BUTTON_LABEL)); 623 } 624 data->append("</td></tr>"); 625} 626 627std::string AboutSandbox() { 628 std::string data; 629 data.append("<!DOCTYPE HTML>\n"); 630 data.append("<html><head><meta charset=\"utf-8\"><title>"); 631 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE)); 632 data.append("</title>"); 633 data.append("</head><body>\n"); 634 data.append("<h1>"); 635 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_TITLE)); 636 data.append("</h1>"); 637 638 const int status = ZygoteHost::GetInstance()->sandbox_status(); 639 640 data.append("<table>"); 641 642 AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SUID_SANDBOX, 643 status & ZygoteHost::kSandboxSUID); 644 if (status & ZygoteHost::kSandboxPIDNS) { 645 AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_PID_NAMESPACES, 646 status & ZygoteHost::kSandboxPIDNS); 647 AboutSandboxRow(&data, " ", IDS_ABOUT_SANDBOX_NET_NAMESPACES, 648 status & ZygoteHost::kSandboxNetNS); 649 } 650 AboutSandboxRow(&data, "", IDS_ABOUT_SANDBOX_SECCOMP_SANDBOX, 651 status & ZygoteHost::kSandboxSeccomp); 652 653 data.append("</table>"); 654 655 bool good = ((status & ZygoteHost::kSandboxSUID) && 656 (status & ZygoteHost::kSandboxPIDNS)) || 657 (status & ZygoteHost::kSandboxSeccomp); 658 if (good) { 659 data.append("<p style=\"color: green\">"); 660 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_OK)); 661 } else { 662 data.append("<p style=\"color: red\">"); 663 data.append(l10n_util::GetStringUTF8(IDS_ABOUT_SANDBOX_BAD)); 664 } 665 data.append("</p>"); 666 667 data.append("</body></html>\n"); 668 return data; 669} 670#endif 671 672std::string AboutVersion(DictionaryValue* localized_strings) { 673 localized_strings->SetString("title", 674 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_TITLE)); 675 chrome::VersionInfo version_info; 676 677 std::string webkit_version = webkit_glue::GetWebKitVersion(); 678#ifdef CHROME_V8 679 std::string js_version(v8::V8::GetVersion()); 680 std::string js_engine = "V8"; 681#else 682 std::string js_version = webkit_version; 683 std::string js_engine = "JavaScriptCore"; 684#endif 685 686 localized_strings->SetString("name", 687 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME)); 688 localized_strings->SetString("version", version_info.Version()); 689 localized_strings->SetString("version_modifier", 690 platform_util::GetVersionStringModifier()); 691 localized_strings->SetString("js_engine", js_engine); 692 localized_strings->SetString("js_version", js_version); 693 localized_strings->SetString("webkit_version", webkit_version); 694 localized_strings->SetString("company", 695 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COMPANY_NAME)); 696 localized_strings->SetString("copyright", 697 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COPYRIGHT)); 698 localized_strings->SetString("cl", version_info.LastChange()); 699 localized_strings->SetString("official", 700 l10n_util::GetStringUTF16( 701 version_info.IsOfficialBuild() ? 702 IDS_ABOUT_VERSION_OFFICIAL 703 : IDS_ABOUT_VERSION_UNOFFICIAL)); 704 localized_strings->SetString("user_agent_name", 705 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_USER_AGENT)); 706 localized_strings->SetString("useragent", webkit_glue::GetUserAgent(GURL())); 707 localized_strings->SetString("command_line_name", 708 l10n_util::GetStringUTF16(IDS_ABOUT_VERSION_COMMAND_LINE)); 709 710#if defined(OS_WIN) 711 localized_strings->SetString("command_line", 712 WideToUTF16(CommandLine::ForCurrentProcess()->command_line_string())); 713#elif defined(OS_POSIX) 714 std::string command_line = ""; 715 typedef std::vector<std::string> ArgvList; 716 const ArgvList& argv = CommandLine::ForCurrentProcess()->argv(); 717 for (ArgvList::const_iterator iter = argv.begin(); iter != argv.end(); iter++) 718 command_line += " " + *iter; 719 // TODO(viettrungluu): |command_line| could really have any encoding, whereas 720 // below we assumes it's UTF-8. 721 localized_strings->SetString("command_line", command_line); 722#endif 723 724 base::StringPiece version_html( 725 ResourceBundle::GetSharedInstance().GetRawDataResource( 726 IDR_ABOUT_VERSION_HTML)); 727 728 return jstemplate_builder::GetTemplatesHtml( 729 version_html, localized_strings, "t" /* template root node id */); 730} 731 732std::string VersionNumberToString(uint32 value) { 733 int hi = (value >> 8) & 0xff; 734 int low = value & 0xff; 735 return base::IntToString(hi) + "." + base::IntToString(low); 736} 737 738// AboutSource ----------------------------------------------------------------- 739 740AboutSource::AboutSource() 741 : DataSource(chrome::kAboutScheme, MessageLoop::current()) { 742} 743 744AboutSource::~AboutSource() { 745} 746 747void AboutSource::StartDataRequest(const std::string& path_raw, 748 bool is_off_the_record, int request_id) { 749 std::string path = path_raw; 750 std::string info; 751 if (path.find("/") != std::string::npos) { 752 size_t pos = path.find("/"); 753 info = path.substr(pos + 1, path.length() - (pos + 1)); 754 path = path.substr(0, pos); 755 } 756 path = StringToLowerASCII(path); 757 758 std::string response; 759 if (path == kDnsPath) { 760 AboutDnsHandler::Start(this, request_id); 761 return; 762 } else if (path == kHistogramsPath) { 763 response = AboutHistograms(info); 764 } else if (path == kMemoryPath) { 765 AboutMemory(this, request_id); 766 return; 767 } else if (path == kMemoryRedirectPath) { 768 response = GetAboutMemoryRedirectResponse(); 769#ifdef TRACK_ALL_TASK_OBJECTS 770 } else if (path == kTasksPath) { 771 response = AboutObjects(info); 772#endif 773 } else if (path == kStatsPath) { 774 response = AboutStats(info); 775#if defined(USE_TCMALLOC) 776 } else if (path == kTcmallocPath) { 777 response = AboutTcmalloc(info); 778#endif 779 } else if (path == kVersionPath || path.empty()) { 780#if defined(OS_CHROMEOS) 781 new ChromeOSAboutVersionHandler(this, request_id); 782 return; 783#else 784 DictionaryValue value; 785 response = AboutVersion(&value); 786#endif 787 } else if (path == kCreditsPath) { 788 response = ResourceBundle::GetSharedInstance().GetRawDataResource( 789 IDR_CREDITS_HTML).as_string(); 790 } else if (path == kAboutPath) { 791 response = AboutAbout(); 792#if defined(OS_CHROMEOS) 793 } else if (path == kOSCreditsPath) { 794 response = ResourceBundle::GetSharedInstance().GetRawDataResource( 795 IDR_OS_CREDITS_HTML).as_string(); 796 } else if (path == kNetworkPath) { 797 response = AboutNetwork(info); 798#endif 799 } else if (path == kTermsPath) { 800 response = ResourceBundle::GetSharedInstance().GetRawDataResource( 801 IDR_TERMS_HTML).as_string(); 802#if defined(OS_LINUX) 803 } else if (path == kLinuxProxyConfigPath) { 804 response = AboutLinuxProxyConfig(); 805 } else if (path == kSandboxPath) { 806 response = AboutSandbox(); 807#endif 808 } 809 810 FinishDataRequest(response, request_id); 811} 812 813void AboutSource::FinishDataRequest(const std::string& response, 814 int request_id) { 815 scoped_refptr<RefCountedBytes> html_bytes(new RefCountedBytes); 816 html_bytes->data.resize(response.size()); 817 std::copy(response.begin(), response.end(), html_bytes->data.begin()); 818 SendResponse(request_id, html_bytes); 819} 820 821// AboutMemoryHandler ---------------------------------------------------------- 822 823// Helper for AboutMemory to bind results from a ProcessMetrics object 824// to a DictionaryValue. Fills ws_usage and comm_usage so that the objects 825// can be used in caller's scope (e.g for appending to a net total). 826void AboutMemoryHandler::BindProcessMetrics(DictionaryValue* data, 827 ProcessMemoryInformation* info) { 828 DCHECK(data && info); 829 830 // Bind metrics to dictionary. 831 data->SetInteger("ws_priv", static_cast<int>(info->working_set.priv)); 832 data->SetInteger("ws_shareable", 833 static_cast<int>(info->working_set.shareable)); 834 data->SetInteger("ws_shared", static_cast<int>(info->working_set.shared)); 835 data->SetInteger("comm_priv", static_cast<int>(info->committed.priv)); 836 data->SetInteger("comm_map", static_cast<int>(info->committed.mapped)); 837 data->SetInteger("comm_image", static_cast<int>(info->committed.image)); 838 data->SetInteger("pid", info->pid); 839 data->SetString("version", info->version); 840 data->SetInteger("processes", info->num_processes); 841} 842 843// Helper for AboutMemory to append memory usage information for all 844// sub-processes (i.e. renderers, plugins) used by Chrome. 845void AboutMemoryHandler::AppendProcess(ListValue* child_data, 846 ProcessMemoryInformation* info) { 847 DCHECK(child_data && info); 848 849 // Append a new DictionaryValue for this renderer to our list. 850 DictionaryValue* child = new DictionaryValue(); 851 child_data->Append(child); 852 BindProcessMetrics(child, info); 853 854 std::string child_label( 855 ChildProcessInfo::GetFullTypeNameInEnglish(info->type, 856 info->renderer_type)); 857 if (info->is_diagnostics) 858 child_label.append(" (diagnostics)"); 859 child->SetString("child_name", child_label); 860 ListValue* titles = new ListValue(); 861 child->Set("titles", titles); 862 for (size_t i = 0; i < info->titles.size(); ++i) 863 titles->Append(new StringValue(info->titles[i])); 864} 865 866 867void AboutMemoryHandler::OnDetailsAvailable() { 868 // the root of the JSON hierarchy for about:memory jstemplate 869 DictionaryValue root; 870 ListValue* browsers = new ListValue(); 871 root.Set("browsers", browsers); 872 873 const std::vector<ProcessData>& browser_processes = processes(); 874 875 // Aggregate per-process data into browser summary data. 876 std::wstring log_string; 877 for (size_t index = 0; index < browser_processes.size(); index++) { 878 if (browser_processes[index].processes.size() == 0) 879 continue; 880 881 // Sum the information for the processes within this browser. 882 ProcessMemoryInformation aggregate; 883 ProcessMemoryInformationList::const_iterator iterator; 884 iterator = browser_processes[index].processes.begin(); 885 aggregate.pid = iterator->pid; 886 aggregate.version = iterator->version; 887 while (iterator != browser_processes[index].processes.end()) { 888 if (!iterator->is_diagnostics || 889 browser_processes[index].processes.size() == 1) { 890 aggregate.working_set.priv += iterator->working_set.priv; 891 aggregate.working_set.shared += iterator->working_set.shared; 892 aggregate.working_set.shareable += iterator->working_set.shareable; 893 aggregate.committed.priv += iterator->committed.priv; 894 aggregate.committed.mapped += iterator->committed.mapped; 895 aggregate.committed.image += iterator->committed.image; 896 aggregate.num_processes++; 897 } 898 ++iterator; 899 } 900 DictionaryValue* browser_data = new DictionaryValue(); 901 browsers->Append(browser_data); 902 browser_data->SetString("name", browser_processes[index].name); 903 904 BindProcessMetrics(browser_data, &aggregate); 905 906 // We log memory info as we record it. 907 if (log_string.length() > 0) 908 log_string.append(L", "); 909 log_string.append(UTF16ToWide(browser_processes[index].name)); 910 log_string.append(L", "); 911 log_string.append(UTF8ToWide( 912 base::Int64ToString(aggregate.working_set.priv))); 913 log_string.append(L", "); 914 log_string.append(UTF8ToWide( 915 base::Int64ToString(aggregate.working_set.shared))); 916 log_string.append(L", "); 917 log_string.append(UTF8ToWide( 918 base::Int64ToString(aggregate.working_set.shareable))); 919 } 920 if (log_string.length() > 0) 921 VLOG(1) << "memory: " << log_string; 922 923 // Set the browser & renderer detailed process data. 924 DictionaryValue* browser_data = new DictionaryValue(); 925 root.Set("browzr_data", browser_data); 926 ListValue* child_data = new ListValue(); 927 root.Set("child_data", child_data); 928 929 ProcessData process = browser_processes[0]; // Chrome is the first browser. 930 root.SetString("current_browser_name", process.name); 931 932 for (size_t index = 0; index < process.processes.size(); index++) { 933 if (process.processes[index].type == ChildProcessInfo::BROWSER_PROCESS) 934 BindProcessMetrics(browser_data, &process.processes[index]); 935 else 936 AppendProcess(child_data, &process.processes[index]); 937 } 938 939 root.SetBoolean("show_other_browsers", 940 browser_defaults::kShowOtherBrowsersInAboutMemory); 941 942 // Get about_memory.html 943 static const base::StringPiece memory_html( 944 ResourceBundle::GetSharedInstance().GetRawDataResource( 945 IDR_ABOUT_MEMORY_HTML)); 946 947 // Create jstemplate and return. 948 std::string template_html = jstemplate_builder::GetTemplateHtml( 949 memory_html, &root, "t" /* template root node id */); 950 951 source_->FinishDataRequest(template_html, request_id_); 952} 953 954#if defined(OS_CHROMEOS) 955// ChromeOSAboutVersionHandler ----------------------------------------------- 956 957ChromeOSAboutVersionHandler::ChromeOSAboutVersionHandler(AboutSource* source, 958 int request_id) 959 : source_(source), 960 request_id_(request_id) { 961 loader_.GetVersion(&consumer_, 962 NewCallback(this, &ChromeOSAboutVersionHandler::OnVersion), 963 chromeos::VersionLoader::VERSION_FULL); 964} 965 966void ChromeOSAboutVersionHandler::OnVersion( 967 chromeos::VersionLoader::Handle handle, 968 std::string version) { 969 DictionaryValue localized_strings; 970 localized_strings.SetString("os_name", 971 l10n_util::GetStringUTF16(IDS_PRODUCT_OS_NAME)); 972 localized_strings.SetString("os_version", version); 973 localized_strings.SetBoolean("is_chrome_os", true); 974 source_->FinishDataRequest(AboutVersion(&localized_strings), request_id_); 975 976 // CancelableRequestProvider isn't happy when it's deleted and servicing a 977 // task, so we delay the deletion. 978 MessageLoop::current()->DeleteSoon(FROM_HERE, this); 979} 980 981#endif 982 983// Returns true if |url|'s spec starts with |about_specifier|, and is 984// terminated by the start of a path. 985bool StartsWithAboutSpecifier(const GURL& url, const char* about_specifier) { 986 return StartsWithASCII(url.spec(), about_specifier, true) && 987 (url.spec().size() == strlen(about_specifier) || 988 url.spec()[strlen(about_specifier)] == '/'); 989} 990 991// Transforms a URL of the form "about:foo/XXX" to <url_prefix> + "XXX". 992GURL RemapAboutURL(const std::string& url_prefix, const GURL& url) { 993 std::string path; 994 size_t split = url.spec().find('/'); 995 if (split != std::string::npos) 996 path = url.spec().substr(split + 1); 997 return GURL(url_prefix + path); 998} 999 1000} // namespace 1001 1002// ----------------------------------------------------------------------------- 1003 1004bool WillHandleBrowserAboutURL(GURL* url, Profile* profile) { 1005 // We only handle about: schemes. 1006 if (!url->SchemeIs(chrome::kAboutScheme)) 1007 return false; 1008 1009 // about:blank is special. Frames are allowed to access about:blank, 1010 // but they are not allowed to access other types of about pages. 1011 // Just ignore the about:blank and let the TAB_CONTENTS_WEB handle it. 1012 if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutBlankURL)) 1013 return false; 1014 1015 // Rewrite about:cache/* URLs to chrome://view-http-cache/* 1016 if (StartsWithAboutSpecifier(*url, chrome::kAboutCacheURL)) { 1017 *url = RemapAboutURL(chrome::kNetworkViewCacheURL, *url); 1018 return true; 1019 } 1020 1021#if defined(OS_WIN) 1022 // Rewrite about:conflicts/* URLs to chrome://conflicts/* 1023 if (StartsWithAboutSpecifier(*url, chrome::kAboutConflicts)) { 1024 *url = GURL(chrome::kChromeUIConflictsURL); 1025 return true; 1026 } 1027#endif 1028 1029 // Rewrite about:flags and about:vaporware to chrome://flags/. 1030 if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutFlagsURL) || 1031 LowerCaseEqualsASCII(url->spec(), chrome::kAboutVaporwareURL)) { 1032 *url = GURL(chrome::kChromeUIFlagsURL); 1033 return true; 1034 } 1035 1036 // Rewrite about:net-internals/* URLs to chrome://net-internals/* 1037 if (StartsWithAboutSpecifier(*url, chrome::kAboutNetInternalsURL)) { 1038 *url = RemapAboutURL(chrome::kNetworkViewInternalsURL, *url); 1039 return true; 1040 } 1041 1042 // Rewrite about:gpu/* URLs to chrome://gpu-internals/* 1043 if (StartsWithAboutSpecifier(*url, chrome::kAboutGpuURL)) { 1044 *url = RemapAboutURL(chrome::kGpuInternalsURL, *url); 1045 return true; 1046 } 1047 1048 // Rewrite about:appcache-internals/* URLs to chrome://appcache/* 1049 if (StartsWithAboutSpecifier(*url, chrome::kAboutAppCacheInternalsURL)) { 1050 *url = RemapAboutURL(chrome::kAppCacheViewInternalsURL, *url); 1051 return true; 1052 } 1053 1054 // Rewrite about:sync-internals/* URLs (and about:sync, too, for 1055 // legacy reasons) to chrome://sync-internals/* 1056 if (StartsWithAboutSpecifier(*url, chrome::kAboutSyncInternalsURL) || 1057 StartsWithAboutSpecifier(*url, chrome::kAboutSyncURL)) { 1058 *url = RemapAboutURL(chrome::kSyncViewInternalsURL, *url); 1059 return true; 1060 } 1061 1062 // Rewrite about:plugins to chrome://plugins/. 1063 if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutPluginsURL)) { 1064 *url = GURL(chrome::kChromeUIPluginsURL); 1065 return true; 1066 } 1067 1068 // Handle URL to crash the browser process. 1069 if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutBrowserCrash)) { 1070 // Induce an intentional crash in the browser process. 1071 int* bad_pointer = NULL; 1072 *bad_pointer = 42; 1073 return true; 1074 } 1075 1076 // Handle URLs to wreck the gpu process. 1077 if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuCrashURL)) { 1078 GpuProcessHostUIShim::GetInstance()->SendAboutGpuCrash(); 1079 return true; 1080 } 1081 if (LowerCaseEqualsASCII(url->spec(), chrome::kAboutGpuHangURL)) { 1082 GpuProcessHostUIShim::GetInstance()->SendAboutGpuHang(); 1083 return true; 1084 } 1085 1086 // There are a few about: URLs that we hand over to the renderer. If the 1087 // renderer wants them, don't do any rewriting. 1088 if (chrome_about_handler::WillHandle(*url)) 1089 return false; 1090 1091 // Anything else requires our special handler; make sure it's initialized. 1092 InitializeAboutDataSource(profile); 1093 1094 // Special case about:memory to go through a redirect before ending up on 1095 // the final page. See GetAboutMemoryRedirectResponse above for why. 1096 if (LowerCaseEqualsASCII(url->path(), kMemoryPath)) { 1097 *url = GURL("chrome://about/memory-redirect"); 1098 return true; 1099 } 1100 1101 // Rewrite the about URL to use chrome:. WebKit treats all about URLS the 1102 // same (blank page), so if we want to display content, we need another 1103 // scheme. 1104 std::string about_url = "chrome://about/"; 1105 about_url.append(url->path()); 1106 *url = GURL(about_url); 1107 return true; 1108} 1109 1110void InitializeAboutDataSource(Profile* profile) { 1111 profile->GetChromeURLDataManager()->AddDataSource(new AboutSource()); 1112} 1113 1114// This function gets called with the fixed-up chrome: URLs, so we have to 1115// compare against those instead of "about:blah". 1116bool HandleNonNavigationAboutURL(const GURL& url) { 1117 // about:ipc is currently buggy, so we disable it for official builds. 1118#if !defined(OFFICIAL_BUILD) 1119 1120#if (defined(OS_MACOSX) || defined(OS_WIN)) && defined(IPC_MESSAGE_LOG_ENABLED) 1121 if (LowerCaseEqualsASCII(url.spec(), chrome::kChromeUIIPCURL)) { 1122 // Run the dialog. This will re-use the existing one if it's already up. 1123 browser::ShowAboutIPCDialog(); 1124 return true; 1125 } 1126#endif 1127 1128#endif // OFFICIAL_BUILD 1129 1130 return false; 1131} 1132