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