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/chromeos/drive_internals_ui.h" 6 7 #include "base/bind.h" 8 #include "base/files/file_enumerator.h" 9 #include "base/files/file_util.h" 10 #include "base/format_macros.h" 11 #include "base/memory/scoped_vector.h" 12 #include "base/memory/weak_ptr.h" 13 #include "base/path_service.h" 14 #include "base/prefs/pref_service.h" 15 #include "base/strings/stringprintf.h" 16 #include "base/sys_info.h" 17 #include "chrome/browser/chromeos/drive/debug_info_collector.h" 18 #include "chrome/browser/chromeos/drive/drive.pb.h" 19 #include "chrome/browser/chromeos/drive/drive_integration_service.h" 20 #include "chrome/browser/chromeos/drive/file_system_util.h" 21 #include "chrome/browser/chromeos/drive/job_list.h" 22 #include "chrome/browser/chromeos/file_manager/path_util.h" 23 #include "chrome/browser/drive/drive_api_util.h" 24 #include "chrome/browser/drive/drive_notification_manager.h" 25 #include "chrome/browser/drive/drive_notification_manager_factory.h" 26 #include "chrome/browser/drive/drive_service_interface.h" 27 #include "chrome/browser/drive/event_logger.h" 28 #include "chrome/browser/profiles/profile.h" 29 #include "chrome/common/pref_names.h" 30 #include "chrome/common/url_constants.h" 31 #include "content/public/browser/browser_thread.h" 32 #include "content/public/browser/web_ui.h" 33 #include "content/public/browser/web_ui_data_source.h" 34 #include "content/public/browser/web_ui_message_handler.h" 35 #include "google_apis/drive/auth_service.h" 36 #include "google_apis/drive/drive_api_parser.h" 37 #include "google_apis/drive/gdata_errorcode.h" 38 #include "google_apis/drive/time_util.h" 39 #include "grit/browser_resources.h" 40 41 using content::BrowserThread; 42 43 namespace chromeos { 44 45 namespace { 46 47 // Gets metadata of all files and directories in |root_path| 48 // recursively. Stores the result as a list of dictionaries like: 49 // 50 // [{ path: 'GCache/v1/tmp/<local_id>', 51 // size: 12345, 52 // is_directory: false, 53 // last_modified: '2005-08-09T09:57:00-08:00', 54 // },...] 55 // 56 // The list is sorted by the path. 57 void GetGCacheContents(const base::FilePath& root_path, 58 base::ListValue* gcache_contents, 59 base::DictionaryValue* gcache_summary) { 60 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 61 DCHECK(gcache_contents); 62 DCHECK(gcache_summary); 63 64 // Use this map to sort the result list by the path. 65 std::map<base::FilePath, base::DictionaryValue*> files; 66 67 const int options = (base::FileEnumerator::FILES | 68 base::FileEnumerator::DIRECTORIES | 69 base::FileEnumerator::SHOW_SYM_LINKS); 70 base::FileEnumerator enumerator(root_path, true /* recursive */, options); 71 72 int64 total_size = 0; 73 for (base::FilePath current = enumerator.Next(); !current.empty(); 74 current = enumerator.Next()) { 75 base::FileEnumerator::FileInfo info = enumerator.GetInfo(); 76 int64 size = info.GetSize(); 77 const bool is_directory = info.IsDirectory(); 78 const bool is_symbolic_link = base::IsLink(info.GetName()); 79 const base::Time last_modified = info.GetLastModifiedTime(); 80 81 base::DictionaryValue* entry = new base::DictionaryValue; 82 entry->SetString("path", current.value()); 83 // Use double instead of integer for large files. 84 entry->SetDouble("size", size); 85 entry->SetBoolean("is_directory", is_directory); 86 entry->SetBoolean("is_symbolic_link", is_symbolic_link); 87 entry->SetString( 88 "last_modified", 89 google_apis::util::FormatTimeAsStringLocaltime(last_modified)); 90 // Print lower 9 bits in octal format. 91 entry->SetString( 92 "permission", 93 base::StringPrintf("%03o", info.stat().st_mode & 0x1ff)); 94 files[current] = entry; 95 96 total_size += size; 97 } 98 99 // Convert |files| into |gcache_contents|. 100 for (std::map<base::FilePath, base::DictionaryValue*>::const_iterator 101 iter = files.begin(); iter != files.end(); ++iter) { 102 gcache_contents->Append(iter->second); 103 } 104 105 gcache_summary->SetDouble("total_size", total_size); 106 } 107 108 // Gets the available disk space for the path |home_path|. 109 void GetFreeDiskSpace(const base::FilePath& home_path, 110 base::DictionaryValue* local_storage_summary) { 111 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); 112 DCHECK(local_storage_summary); 113 114 const int64 free_space = base::SysInfo::AmountOfFreeDiskSpace(home_path); 115 local_storage_summary->SetDouble("free_space", free_space); 116 } 117 118 // Formats |entry| into text. 119 std::string FormatEntry(const base::FilePath& path, 120 const drive::ResourceEntry& entry) { 121 DCHECK_CURRENTLY_ON(BrowserThread::UI); 122 123 using base::StringAppendF; 124 125 std::string out; 126 StringAppendF(&out, "%s\n", path.AsUTF8Unsafe().c_str()); 127 StringAppendF(&out, " title: %s\n", entry.title().c_str()); 128 StringAppendF(&out, " local_id: %s\n", entry.local_id().c_str()); 129 StringAppendF(&out, " resource_id: %s\n", entry.resource_id().c_str()); 130 StringAppendF(&out, " parent_local_id: %s\n", 131 entry.parent_local_id().c_str()); 132 StringAppendF(&out, " shared: %s\n", entry.shared() ? "true" : "false"); 133 StringAppendF(&out, " shared_with_me: %s\n", 134 entry.shared_with_me() ? "true" : "false"); 135 136 const drive::PlatformFileInfoProto& file_info = entry.file_info(); 137 StringAppendF(&out, " file_info\n"); 138 StringAppendF(&out, " size: %" PRId64 "\n", file_info.size()); 139 StringAppendF(&out, " is_directory: %d\n", file_info.is_directory()); 140 StringAppendF(&out, " is_symbolic_link: %d\n", 141 file_info.is_symbolic_link()); 142 143 const base::Time last_modified = base::Time::FromInternalValue( 144 file_info.last_modified()); 145 const base::Time last_accessed = base::Time::FromInternalValue( 146 file_info.last_accessed()); 147 const base::Time creation_time = base::Time::FromInternalValue( 148 file_info.creation_time()); 149 StringAppendF(&out, " last_modified: %s\n", 150 google_apis::util::FormatTimeAsString(last_modified).c_str()); 151 StringAppendF(&out, " last_accessed: %s\n", 152 google_apis::util::FormatTimeAsString(last_accessed).c_str()); 153 StringAppendF(&out, " creation_time: %s\n", 154 google_apis::util::FormatTimeAsString(creation_time).c_str()); 155 156 if (entry.has_file_specific_info()) { 157 const drive::FileSpecificInfo& file_specific_info = 158 entry.file_specific_info(); 159 StringAppendF(&out, " alternate_url: %s\n", 160 file_specific_info.alternate_url().c_str()); 161 StringAppendF(&out, " content_mime_type: %s\n", 162 file_specific_info.content_mime_type().c_str()); 163 StringAppendF(&out, " file_md5: %s\n", 164 file_specific_info.md5().c_str()); 165 StringAppendF(&out, " document_extension: %s\n", 166 file_specific_info.document_extension().c_str()); 167 StringAppendF(&out, " is_hosted_document: %d\n", 168 file_specific_info.is_hosted_document()); 169 } 170 171 if (entry.has_directory_specific_info()) { 172 StringAppendF(&out, " directory_info\n"); 173 const drive::DirectorySpecificInfo& directory_specific_info = 174 entry.directory_specific_info(); 175 StringAppendF(&out, " changestamp: %" PRId64 "\n", 176 directory_specific_info.changestamp()); 177 } 178 179 return out; 180 } 181 182 std::string SeverityToString(logging::LogSeverity severity) { 183 switch (severity) { 184 case logging::LOG_INFO: 185 return "info"; 186 case logging::LOG_WARNING: 187 return "warning"; 188 case logging::LOG_ERROR: 189 return "error"; 190 default: // Treat all other higher severities as ERROR. 191 return "error"; 192 } 193 } 194 195 // Appends {'key': key, 'value': value} dictionary to the |list|. 196 void AppendKeyValue(base::ListValue* list, 197 const std::string& key, 198 const std::string& value) { 199 base::DictionaryValue* dict = new base::DictionaryValue; 200 dict->SetString("key", key); 201 dict->SetString("value", value); 202 list->Append(dict); 203 } 204 205 // Class to handle messages from chrome://drive-internals. 206 class DriveInternalsWebUIHandler : public content::WebUIMessageHandler { 207 public: 208 DriveInternalsWebUIHandler() 209 : last_sent_event_id_(-1), 210 weak_ptr_factory_(this) { 211 } 212 213 virtual ~DriveInternalsWebUIHandler() { 214 } 215 216 private: 217 // WebUIMessageHandler override. 218 virtual void RegisterMessages() OVERRIDE; 219 220 // Returns a DriveIntegrationService. 221 drive::DriveIntegrationService* GetIntegrationService(); 222 223 // Returns a DriveService instance. 224 drive::DriveServiceInterface* GetDriveService(); 225 226 // Returns a DebugInfoCollector instance. 227 drive::DebugInfoCollector* GetDebugInfoCollector(); 228 229 // Called when the page is first loaded. 230 void OnPageLoaded(const base::ListValue* args); 231 232 // Updates respective sections. 233 void UpdateDriveRelatedPreferencesSection(); 234 void UpdateConnectionStatusSection( 235 drive::DriveServiceInterface* drive_service); 236 void UpdateAboutResourceSection( 237 drive::DriveServiceInterface* drive_service); 238 void UpdateAppListSection( 239 drive::DriveServiceInterface* drive_service); 240 void UpdateLocalMetadataSection( 241 drive::DebugInfoCollector* debug_info_collector); 242 void UpdateDeltaUpdateStatusSection( 243 drive::DebugInfoCollector* debug_info_collector); 244 void UpdateInFlightOperationsSection(drive::JobListInterface* job_list); 245 void UpdateGCacheContentsSection(); 246 void UpdateFileSystemContentsSection(); 247 void UpdateLocalStorageUsageSection(); 248 void UpdateCacheContentsSection( 249 drive::DebugInfoCollector* debug_info_collector); 250 void UpdateEventLogSection(); 251 void UpdatePathConfigurationsSection(); 252 253 // Called when GetGCacheContents() is complete. 254 void OnGetGCacheContents(base::ListValue* gcache_contents, 255 base::DictionaryValue* cache_summary); 256 257 // Called when GetResourceEntryByPath() is complete. 258 void OnGetResourceEntryByPath(const base::FilePath& path, 259 drive::FileError error, 260 scoped_ptr<drive::ResourceEntry> entry); 261 262 // Called when ReadDirectoryByPath() is complete. 263 void OnReadDirectoryByPath(const base::FilePath& parent_path, 264 drive::FileError error, 265 scoped_ptr<drive::ResourceEntryVector> entries); 266 267 // Called as the iterator for DebugInfoCollector::IterateFileCache(). 268 void UpdateCacheEntry(const std::string& local_id, 269 const drive::FileCacheEntry& cache_entry); 270 271 // Called when GetFreeDiskSpace() is complete. 272 void OnGetFreeDiskSpace(base::DictionaryValue* local_storage_summary); 273 274 // Called when GetAboutResource() call to DriveService is complete. 275 void OnGetAboutResource( 276 google_apis::GDataErrorCode status, 277 scoped_ptr<google_apis::AboutResource> about_resource); 278 279 // Called when GetAppList() call to DriveService is complete. 280 void OnGetAppList( 281 google_apis::GDataErrorCode status, 282 scoped_ptr<google_apis::AppList> app_list); 283 284 // Callback for DebugInfoCollector::GetMetadata for local update. 285 void OnGetFilesystemMetadataForLocal( 286 const drive::FileSystemMetadata& metadata); 287 288 // Callback for DebugInfoCollector::GetMetadata for delta update. 289 void OnGetFilesystemMetadataForDeltaUpdate( 290 const drive::FileSystemMetadata& metadata); 291 292 // Called when the page requests periodic update. 293 void OnPeriodicUpdate(const base::ListValue* args); 294 295 // Called when the corresponding button on the page is pressed. 296 void ClearAccessToken(const base::ListValue* args); 297 void ClearRefreshToken(const base::ListValue* args); 298 void ResetDriveFileSystem(const base::ListValue* args); 299 void ListFileEntries(const base::ListValue* args); 300 301 // Called after file system reset for ResetDriveFileSystem is done. 302 void ResetFinished(bool success); 303 304 // The last event sent to the JavaScript side. 305 int last_sent_event_id_; 306 307 base::WeakPtrFactory<DriveInternalsWebUIHandler> weak_ptr_factory_; 308 DISALLOW_COPY_AND_ASSIGN(DriveInternalsWebUIHandler); 309 }; 310 311 void DriveInternalsWebUIHandler::OnGetAboutResource( 312 google_apis::GDataErrorCode status, 313 scoped_ptr<google_apis::AboutResource> parsed_about_resource) { 314 DCHECK_CURRENTLY_ON(BrowserThread::UI); 315 316 if (status != google_apis::HTTP_SUCCESS) { 317 LOG(ERROR) << "Failed to get about resource"; 318 return; 319 } 320 DCHECK(parsed_about_resource); 321 322 base::DictionaryValue about_resource; 323 about_resource.SetDouble("account-quota-total", 324 parsed_about_resource->quota_bytes_total()); 325 about_resource.SetDouble("account-quota-used", 326 parsed_about_resource->quota_bytes_used()); 327 about_resource.SetDouble("account-largest-changestamp-remote", 328 parsed_about_resource->largest_change_id()); 329 about_resource.SetString("root-resource-id", 330 parsed_about_resource->root_folder_id()); 331 332 web_ui()->CallJavascriptFunction("updateAboutResource", about_resource); 333 } 334 335 void DriveInternalsWebUIHandler::OnGetAppList( 336 google_apis::GDataErrorCode status, 337 scoped_ptr<google_apis::AppList> parsed_app_list) { 338 DCHECK_CURRENTLY_ON(BrowserThread::UI); 339 340 if (status != google_apis::HTTP_SUCCESS) { 341 LOG(ERROR) << "Failed to get app list"; 342 return; 343 } 344 DCHECK(parsed_app_list); 345 346 base::DictionaryValue app_list; 347 app_list.SetString("etag", parsed_app_list->etag()); 348 349 base::ListValue* items = new base::ListValue(); 350 for (size_t i = 0; i < parsed_app_list->items().size(); ++i) { 351 const google_apis::AppResource* app = parsed_app_list->items()[i]; 352 base::DictionaryValue* app_data = new base::DictionaryValue(); 353 app_data->SetString("name", app->name()); 354 app_data->SetString("application_id", app->application_id()); 355 app_data->SetString("object_type", app->object_type()); 356 app_data->SetBoolean("supports_create", app->supports_create()); 357 358 items->Append(app_data); 359 } 360 app_list.Set("items", items); 361 362 web_ui()->CallJavascriptFunction("updateAppList", app_list); 363 } 364 365 void DriveInternalsWebUIHandler::RegisterMessages() { 366 web_ui()->RegisterMessageCallback( 367 "pageLoaded", 368 base::Bind(&DriveInternalsWebUIHandler::OnPageLoaded, 369 weak_ptr_factory_.GetWeakPtr())); 370 web_ui()->RegisterMessageCallback( 371 "periodicUpdate", 372 base::Bind(&DriveInternalsWebUIHandler::OnPeriodicUpdate, 373 weak_ptr_factory_.GetWeakPtr())); 374 web_ui()->RegisterMessageCallback( 375 "clearAccessToken", 376 base::Bind(&DriveInternalsWebUIHandler::ClearAccessToken, 377 weak_ptr_factory_.GetWeakPtr())); 378 web_ui()->RegisterMessageCallback( 379 "clearRefreshToken", 380 base::Bind(&DriveInternalsWebUIHandler::ClearRefreshToken, 381 weak_ptr_factory_.GetWeakPtr())); 382 web_ui()->RegisterMessageCallback( 383 "resetDriveFileSystem", 384 base::Bind(&DriveInternalsWebUIHandler::ResetDriveFileSystem, 385 weak_ptr_factory_.GetWeakPtr())); 386 web_ui()->RegisterMessageCallback( 387 "listFileEntries", 388 base::Bind(&DriveInternalsWebUIHandler::ListFileEntries, 389 weak_ptr_factory_.GetWeakPtr())); 390 } 391 392 drive::DriveIntegrationService* 393 DriveInternalsWebUIHandler::GetIntegrationService() { 394 DCHECK_CURRENTLY_ON(BrowserThread::UI); 395 396 Profile* profile = Profile::FromWebUI(web_ui()); 397 drive::DriveIntegrationService* service = 398 drive::DriveIntegrationServiceFactory::FindForProfile(profile); 399 if (!service || !service->is_enabled()) 400 return NULL; 401 return service; 402 } 403 404 drive::DriveServiceInterface* DriveInternalsWebUIHandler::GetDriveService() { 405 DCHECK_CURRENTLY_ON(BrowserThread::UI); 406 407 Profile* profile = Profile::FromWebUI(web_ui()); 408 return drive::util::GetDriveServiceByProfile(profile); 409 } 410 411 drive::DebugInfoCollector* DriveInternalsWebUIHandler::GetDebugInfoCollector() { 412 DCHECK_CURRENTLY_ON(BrowserThread::UI); 413 414 drive::DriveIntegrationService* integration_service = GetIntegrationService(); 415 return integration_service ? 416 integration_service->debug_info_collector() : NULL; 417 } 418 419 void DriveInternalsWebUIHandler::OnPageLoaded(const base::ListValue* args) { 420 DCHECK_CURRENTLY_ON(BrowserThread::UI); 421 422 drive::DriveIntegrationService* integration_service = 423 GetIntegrationService(); 424 // |integration_service| may be NULL in the guest/incognito mode. 425 if (!integration_service) 426 return; 427 428 drive::DriveServiceInterface* drive_service = 429 integration_service->drive_service(); 430 DCHECK(drive_service); 431 drive::DebugInfoCollector* debug_info_collector = 432 integration_service->debug_info_collector(); 433 DCHECK(debug_info_collector); 434 435 UpdateDriveRelatedPreferencesSection(); 436 UpdateConnectionStatusSection(drive_service); 437 UpdateAboutResourceSection(drive_service); 438 UpdateAppListSection(drive_service); 439 UpdateLocalMetadataSection(debug_info_collector); 440 UpdateDeltaUpdateStatusSection(debug_info_collector); 441 UpdateInFlightOperationsSection(integration_service->job_list()); 442 UpdateGCacheContentsSection(); 443 UpdateCacheContentsSection(debug_info_collector); 444 UpdateLocalStorageUsageSection(); 445 UpdatePathConfigurationsSection(); 446 447 // When the drive-internals page is reloaded by the reload key, the page 448 // content is recreated, but this WebUI object is not (instead, OnPageLoaded 449 // is called again). In that case, we have to forget the last sent ID here, 450 // and resent whole the logs to the page. 451 last_sent_event_id_ = -1; 452 UpdateEventLogSection(); 453 } 454 455 void DriveInternalsWebUIHandler::UpdateDriveRelatedPreferencesSection() { 456 DCHECK_CURRENTLY_ON(BrowserThread::UI); 457 458 const char* kDriveRelatedPreferences[] = { 459 prefs::kDisableDrive, 460 prefs::kDisableDriveOverCellular, 461 prefs::kDisableDriveHostedFiles, 462 }; 463 464 Profile* profile = Profile::FromWebUI(web_ui()); 465 PrefService* pref_service = profile->GetPrefs(); 466 467 base::ListValue preferences; 468 for (size_t i = 0; i < arraysize(kDriveRelatedPreferences); ++i) { 469 const std::string key = kDriveRelatedPreferences[i]; 470 // As of now, all preferences are boolean. 471 const std::string value = 472 (pref_service->GetBoolean(key.c_str()) ? "true" : "false"); 473 AppendKeyValue(&preferences, key, value); 474 } 475 476 web_ui()->CallJavascriptFunction("updateDriveRelatedPreferences", 477 preferences); 478 } 479 480 void DriveInternalsWebUIHandler::UpdateConnectionStatusSection( 481 drive::DriveServiceInterface* drive_service) { 482 DCHECK_CURRENTLY_ON(BrowserThread::UI); 483 DCHECK(drive_service); 484 485 std::string status; 486 switch (drive::util::GetDriveConnectionStatus(Profile::FromWebUI(web_ui()))) { 487 case drive::util::DRIVE_DISCONNECTED_NOSERVICE: 488 status = "no service"; 489 break; 490 case drive::util::DRIVE_DISCONNECTED_NONETWORK: 491 status = "no network"; 492 break; 493 case drive::util::DRIVE_DISCONNECTED_NOTREADY: 494 status = "not ready"; 495 break; 496 case drive::util::DRIVE_CONNECTED_METERED: 497 status = "metered"; 498 break; 499 case drive::util::DRIVE_CONNECTED: 500 status = "connected"; 501 break; 502 } 503 504 base::DictionaryValue connection_status; 505 connection_status.SetString("status", status); 506 connection_status.SetBoolean("has-refresh-token", 507 drive_service->HasRefreshToken()); 508 connection_status.SetBoolean("has-access-token", 509 drive_service->HasAccessToken()); 510 web_ui()->CallJavascriptFunction("updateConnectionStatus", connection_status); 511 } 512 513 void DriveInternalsWebUIHandler::UpdateAboutResourceSection( 514 drive::DriveServiceInterface* drive_service) { 515 DCHECK_CURRENTLY_ON(BrowserThread::UI); 516 DCHECK(drive_service); 517 518 drive_service->GetAboutResource( 519 base::Bind(&DriveInternalsWebUIHandler::OnGetAboutResource, 520 weak_ptr_factory_.GetWeakPtr())); 521 } 522 523 void DriveInternalsWebUIHandler::UpdateAppListSection( 524 drive::DriveServiceInterface* drive_service) { 525 DCHECK_CURRENTLY_ON(BrowserThread::UI); 526 DCHECK(drive_service); 527 528 drive_service->GetAppList( 529 base::Bind(&DriveInternalsWebUIHandler::OnGetAppList, 530 weak_ptr_factory_.GetWeakPtr())); 531 } 532 533 void DriveInternalsWebUIHandler::UpdateLocalMetadataSection( 534 drive::DebugInfoCollector* debug_info_collector) { 535 DCHECK_CURRENTLY_ON(BrowserThread::UI); 536 DCHECK(debug_info_collector); 537 538 debug_info_collector->GetMetadata( 539 base::Bind(&DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal, 540 weak_ptr_factory_.GetWeakPtr())); 541 } 542 543 void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal( 544 const drive::FileSystemMetadata& metadata) { 545 DCHECK_CURRENTLY_ON(BrowserThread::UI); 546 547 base::DictionaryValue local_metadata; 548 local_metadata.SetDouble("account-largest-changestamp-local", 549 metadata.largest_changestamp); 550 local_metadata.SetBoolean("account-metadata-refreshing", metadata.refreshing); 551 web_ui()->CallJavascriptFunction("updateLocalMetadata", local_metadata); 552 } 553 554 void DriveInternalsWebUIHandler::ClearAccessToken(const base::ListValue* args) { 555 DCHECK_CURRENTLY_ON(BrowserThread::UI); 556 557 drive::DriveServiceInterface* drive_service = GetDriveService(); 558 if (drive_service) 559 drive_service->ClearAccessToken(); 560 } 561 562 void DriveInternalsWebUIHandler::ClearRefreshToken( 563 const base::ListValue* args) { 564 DCHECK_CURRENTLY_ON(BrowserThread::UI); 565 566 drive::DriveServiceInterface* drive_service = GetDriveService(); 567 if (drive_service) 568 drive_service->ClearRefreshToken(); 569 } 570 571 void DriveInternalsWebUIHandler::ResetDriveFileSystem( 572 const base::ListValue* args) { 573 DCHECK_CURRENTLY_ON(BrowserThread::UI); 574 575 drive::DriveIntegrationService* integration_service = 576 GetIntegrationService(); 577 if (integration_service) { 578 integration_service->ClearCacheAndRemountFileSystem( 579 base::Bind(&DriveInternalsWebUIHandler::ResetFinished, 580 weak_ptr_factory_.GetWeakPtr())); 581 } 582 } 583 584 void DriveInternalsWebUIHandler::ResetFinished(bool success) { 585 DCHECK_CURRENTLY_ON(BrowserThread::UI); 586 587 web_ui()->CallJavascriptFunction("updateResetStatus", 588 base::FundamentalValue(success)); 589 } 590 591 void DriveInternalsWebUIHandler::ListFileEntries(const base::ListValue* args) { 592 DCHECK_CURRENTLY_ON(BrowserThread::UI); 593 594 UpdateFileSystemContentsSection(); 595 } 596 597 void DriveInternalsWebUIHandler::UpdateDeltaUpdateStatusSection( 598 drive::DebugInfoCollector* debug_info_collector) { 599 DCHECK_CURRENTLY_ON(BrowserThread::UI); 600 DCHECK(debug_info_collector); 601 602 debug_info_collector->GetMetadata( 603 base::Bind( 604 &DriveInternalsWebUIHandler::OnGetFilesystemMetadataForDeltaUpdate, 605 weak_ptr_factory_.GetWeakPtr())); 606 } 607 608 void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForDeltaUpdate( 609 const drive::FileSystemMetadata& metadata) { 610 DCHECK_CURRENTLY_ON(BrowserThread::UI); 611 612 Profile* profile = Profile::FromWebUI(web_ui()); 613 drive::DriveNotificationManager* drive_notification_manager = 614 drive::DriveNotificationManagerFactory::FindForBrowserContext(profile); 615 if (!drive_notification_manager) 616 return; 617 618 base::DictionaryValue delta_update_status; 619 delta_update_status.SetBoolean( 620 "push-notification-enabled", 621 drive_notification_manager->push_notification_enabled()); 622 delta_update_status.SetString( 623 "last-update-check-time", 624 google_apis::util::FormatTimeAsStringLocaltime( 625 metadata.last_update_check_time)); 626 delta_update_status.SetString( 627 "last-update-check-error", 628 drive::FileErrorToString(metadata.last_update_check_error)); 629 630 web_ui()->CallJavascriptFunction("updateDeltaUpdateStatus", 631 delta_update_status); 632 } 633 634 void DriveInternalsWebUIHandler::UpdateInFlightOperationsSection( 635 drive::JobListInterface* job_list) { 636 DCHECK_CURRENTLY_ON(BrowserThread::UI); 637 DCHECK(job_list); 638 639 std::vector<drive::JobInfo> info_list = job_list->GetJobInfoList(); 640 641 base::ListValue in_flight_operations; 642 for (size_t i = 0; i < info_list.size(); ++i) { 643 const drive::JobInfo& info = info_list[i]; 644 645 base::DictionaryValue* dict = new base::DictionaryValue; 646 dict->SetInteger("id", info.job_id); 647 dict->SetString("type", drive::JobTypeToString(info.job_type)); 648 dict->SetString("file_path", info.file_path.AsUTF8Unsafe()); 649 dict->SetString("state", drive::JobStateToString(info.state)); 650 dict->SetDouble("progress_current", info.num_completed_bytes); 651 dict->SetDouble("progress_total", info.num_total_bytes); 652 in_flight_operations.Append(dict); 653 } 654 web_ui()->CallJavascriptFunction("updateInFlightOperations", 655 in_flight_operations); 656 } 657 658 void DriveInternalsWebUIHandler::UpdateGCacheContentsSection() { 659 DCHECK_CURRENTLY_ON(BrowserThread::UI); 660 661 // Start updating the GCache contents section. 662 Profile* profile = Profile::FromWebUI(web_ui()); 663 const base::FilePath root_path = drive::util::GetCacheRootPath(profile); 664 base::ListValue* gcache_contents = new base::ListValue; 665 base::DictionaryValue* gcache_summary = new base::DictionaryValue; 666 BrowserThread::PostBlockingPoolTaskAndReply( 667 FROM_HERE, 668 base::Bind(&GetGCacheContents, 669 root_path, 670 gcache_contents, 671 gcache_summary), 672 base::Bind(&DriveInternalsWebUIHandler::OnGetGCacheContents, 673 weak_ptr_factory_.GetWeakPtr(), 674 base::Owned(gcache_contents), 675 base::Owned(gcache_summary))); 676 } 677 678 void DriveInternalsWebUIHandler::UpdateFileSystemContentsSection() { 679 DCHECK_CURRENTLY_ON(BrowserThread::UI); 680 681 drive::DebugInfoCollector* debug_info_collector = GetDebugInfoCollector(); 682 if (!debug_info_collector) 683 return; 684 685 // Start rendering the file system tree as text. 686 const base::FilePath root_path = drive::util::GetDriveGrandRootPath(); 687 688 debug_info_collector->GetResourceEntry( 689 root_path, 690 base::Bind(&DriveInternalsWebUIHandler::OnGetResourceEntryByPath, 691 weak_ptr_factory_.GetWeakPtr(), 692 root_path)); 693 694 debug_info_collector->ReadDirectory( 695 root_path, 696 base::Bind(&DriveInternalsWebUIHandler::OnReadDirectoryByPath, 697 weak_ptr_factory_.GetWeakPtr(), 698 root_path)); 699 } 700 701 void DriveInternalsWebUIHandler::UpdateLocalStorageUsageSection() { 702 DCHECK_CURRENTLY_ON(BrowserThread::UI); 703 704 // Propagate the amount of local free space in bytes. 705 base::FilePath home_path; 706 if (PathService::Get(base::DIR_HOME, &home_path)) { 707 base::DictionaryValue* local_storage_summary = new base::DictionaryValue; 708 BrowserThread::PostBlockingPoolTaskAndReply( 709 FROM_HERE, 710 base::Bind(&GetFreeDiskSpace, home_path, local_storage_summary), 711 base::Bind(&DriveInternalsWebUIHandler::OnGetFreeDiskSpace, 712 weak_ptr_factory_.GetWeakPtr(), 713 base::Owned(local_storage_summary))); 714 } else { 715 LOG(ERROR) << "Home directory not found"; 716 } 717 } 718 719 void DriveInternalsWebUIHandler::UpdateCacheContentsSection( 720 drive::DebugInfoCollector* debug_info_collector) { 721 DCHECK_CURRENTLY_ON(BrowserThread::UI); 722 DCHECK(debug_info_collector); 723 724 debug_info_collector->IterateFileCache( 725 base::Bind(&DriveInternalsWebUIHandler::UpdateCacheEntry, 726 weak_ptr_factory_.GetWeakPtr()), 727 base::Bind(&base::DoNothing)); 728 } 729 730 void DriveInternalsWebUIHandler::UpdateEventLogSection() { 731 DCHECK_CURRENTLY_ON(BrowserThread::UI); 732 733 drive::DriveIntegrationService* integration_service = 734 GetIntegrationService(); 735 if (!integration_service) 736 return; 737 738 const std::vector<drive::EventLogger::Event> log = 739 integration_service->event_logger()->GetHistory(); 740 741 base::ListValue list; 742 for (size_t i = 0; i < log.size(); ++i) { 743 // Skip events which were already sent. 744 if (log[i].id <= last_sent_event_id_) 745 continue; 746 747 std::string severity = SeverityToString(log[i].severity); 748 749 base::DictionaryValue* dict = new base::DictionaryValue; 750 dict->SetString("key", 751 google_apis::util::FormatTimeAsStringLocaltime(log[i].when)); 752 dict->SetString("value", "[" + severity + "] " + log[i].what); 753 dict->SetString("class", "log-" + severity); 754 list.Append(dict); 755 last_sent_event_id_ = log[i].id; 756 } 757 if (!list.empty()) 758 web_ui()->CallJavascriptFunction("updateEventLog", list); 759 } 760 761 void DriveInternalsWebUIHandler::UpdatePathConfigurationsSection() { 762 DCHECK_CURRENTLY_ON(BrowserThread::UI); 763 764 Profile* const profile = Profile::FromWebUI(web_ui()); 765 766 base::ListValue paths; 767 768 AppendKeyValue( 769 &paths, "Downloads", 770 file_manager::util::GetDownloadsFolderForProfile(profile).AsUTF8Unsafe()); 771 AppendKeyValue( 772 &paths, "Drive", 773 drive::util::GetDriveMountPointPath(profile).AsUTF8Unsafe()); 774 775 const char* kPathPreferences[] = { 776 prefs::kSelectFileLastDirectory, 777 prefs::kSaveFileDefaultDirectory, 778 prefs::kDownloadDefaultDirectory, 779 }; 780 for (size_t i = 0; i < arraysize(kPathPreferences); ++i) { 781 const char* const key = kPathPreferences[i]; 782 AppendKeyValue(&paths, key, 783 profile->GetPrefs()->GetFilePath(key).AsUTF8Unsafe()); 784 } 785 786 web_ui()->CallJavascriptFunction("updatePathConfigurations", paths); 787 } 788 789 void DriveInternalsWebUIHandler::OnGetGCacheContents( 790 base::ListValue* gcache_contents, 791 base::DictionaryValue* gcache_summary) { 792 DCHECK_CURRENTLY_ON(BrowserThread::UI); 793 DCHECK(gcache_contents); 794 DCHECK(gcache_summary); 795 796 web_ui()->CallJavascriptFunction("updateGCacheContents", 797 *gcache_contents, 798 *gcache_summary); 799 } 800 801 void DriveInternalsWebUIHandler::OnGetResourceEntryByPath( 802 const base::FilePath& path, 803 drive::FileError error, 804 scoped_ptr<drive::ResourceEntry> entry) { 805 DCHECK_CURRENTLY_ON(BrowserThread::UI); 806 807 if (error == drive::FILE_ERROR_OK) { 808 DCHECK(entry.get()); 809 const base::StringValue value(FormatEntry(path, *entry) + "\n"); 810 web_ui()->CallJavascriptFunction("updateFileSystemContents", value); 811 } 812 } 813 814 void DriveInternalsWebUIHandler::OnReadDirectoryByPath( 815 const base::FilePath& parent_path, 816 drive::FileError error, 817 scoped_ptr<drive::ResourceEntryVector> entries) { 818 DCHECK_CURRENTLY_ON(BrowserThread::UI); 819 820 if (error == drive::FILE_ERROR_OK) { 821 DCHECK(entries.get()); 822 823 drive::DebugInfoCollector* debug_info_collector = GetDebugInfoCollector(); 824 std::string file_system_as_text; 825 for (size_t i = 0; i < entries->size(); ++i) { 826 const drive::ResourceEntry& entry = (*entries)[i]; 827 const base::FilePath current_path = parent_path.Append( 828 base::FilePath::FromUTF8Unsafe(entry.base_name())); 829 830 file_system_as_text.append(FormatEntry(current_path, entry) + "\n"); 831 832 if (entry.file_info().is_directory()) { 833 debug_info_collector->ReadDirectory( 834 current_path, 835 base::Bind(&DriveInternalsWebUIHandler::OnReadDirectoryByPath, 836 weak_ptr_factory_.GetWeakPtr(), 837 current_path)); 838 } 839 } 840 841 // There may be pending ReadDirectoryByPath() calls, but we can update 842 // the page with what we have now. This results in progressive 843 // updates, which is good for a large file system. 844 const base::StringValue value(file_system_as_text); 845 web_ui()->CallJavascriptFunction("updateFileSystemContents", value); 846 } 847 } 848 849 void DriveInternalsWebUIHandler::UpdateCacheEntry( 850 const std::string& local_id, 851 const drive::FileCacheEntry& cache_entry) { 852 DCHECK_CURRENTLY_ON(BrowserThread::UI); 853 854 // Convert |cache_entry| into a dictionary. 855 base::DictionaryValue value; 856 value.SetString("local_id", local_id); 857 value.SetString("md5", cache_entry.md5()); 858 value.SetBoolean("is_present", cache_entry.is_present()); 859 value.SetBoolean("is_pinned", cache_entry.is_pinned()); 860 value.SetBoolean("is_dirty", cache_entry.is_dirty()); 861 862 web_ui()->CallJavascriptFunction("updateCacheContents", value); 863 } 864 865 void DriveInternalsWebUIHandler::OnGetFreeDiskSpace( 866 base::DictionaryValue* local_storage_summary) { 867 DCHECK_CURRENTLY_ON(BrowserThread::UI); 868 DCHECK(local_storage_summary); 869 870 web_ui()->CallJavascriptFunction( 871 "updateLocalStorageUsage", *local_storage_summary); 872 } 873 874 void DriveInternalsWebUIHandler::OnPeriodicUpdate(const base::ListValue* args) { 875 DCHECK_CURRENTLY_ON(BrowserThread::UI); 876 877 drive::DriveIntegrationService* integration_service = 878 GetIntegrationService(); 879 // |integration_service| may be NULL in the guest/incognito mode. 880 if (!integration_service) 881 return; 882 883 UpdateInFlightOperationsSection(integration_service->job_list()); 884 UpdateEventLogSection(); 885 } 886 887 } // namespace 888 889 DriveInternalsUI::DriveInternalsUI(content::WebUI* web_ui) 890 : WebUIController(web_ui) { 891 web_ui->AddMessageHandler(new DriveInternalsWebUIHandler()); 892 893 content::WebUIDataSource* source = 894 content::WebUIDataSource::Create(chrome::kChromeUIDriveInternalsHost); 895 source->AddResourcePath("drive_internals.css", IDR_DRIVE_INTERNALS_CSS); 896 source->AddResourcePath("drive_internals.js", IDR_DRIVE_INTERNALS_JS); 897 source->SetDefaultResource(IDR_DRIVE_INTERNALS_HTML); 898 899 Profile* profile = Profile::FromWebUI(web_ui); 900 content::WebUIDataSource::Add(profile, source); 901 } 902 903 } // namespace chromeos 904