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