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