| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/webui/chromeos/drive_internals_ui.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/files/file_enumerator.h" |
| #include "base/files/file_util.h" |
| #include "base/format_macros.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/path_service.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/sys_info.h" |
| #include "base/task_scheduler/post_task.h" |
| #include "chrome/browser/chromeos/drive/debug_info_collector.h" |
| #include "chrome/browser/chromeos/drive/drive_integration_service.h" |
| #include "chrome/browser/chromeos/drive/file_system_util.h" |
| #include "chrome/browser/chromeos/file_manager/path_util.h" |
| #include "chrome/browser/drive/drive_notification_manager_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/grit/browser_resources.h" |
| #include "components/drive/drive.pb.h" |
| #include "components/drive/drive_api_util.h" |
| #include "components/drive/drive_notification_manager.h" |
| #include "components/drive/drive_pref_names.h" |
| #include "components/drive/event_logger.h" |
| #include "components/drive/job_list.h" |
| #include "components/drive/service/drive_service_interface.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/browser/web_ui_data_source.h" |
| #include "content/public/browser/web_ui_message_handler.h" |
| #include "google_apis/drive/auth_service.h" |
| #include "google_apis/drive/drive_api_error_codes.h" |
| #include "google_apis/drive/drive_api_parser.h" |
| #include "google_apis/drive/time_util.h" |
| |
| using content::BrowserThread; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| // Gets metadata of all files and directories in |root_path| |
| // recursively. Stores the result as a list of dictionaries like: |
| // |
| // [{ path: 'GCache/v1/tmp/<local_id>', |
| // size: 12345, |
| // is_directory: false, |
| // last_modified: '2005-08-09T09:57:00-08:00', |
| // },...] |
| // |
| // The list is sorted by the path. |
| void GetGCacheContents(const base::FilePath& root_path, |
| base::ListValue* gcache_contents, |
| base::DictionaryValue* gcache_summary) { |
| DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(gcache_contents); |
| DCHECK(gcache_summary); |
| |
| // Use this map to sort the result list by the path. |
| std::map<base::FilePath, std::unique_ptr<base::DictionaryValue>> files; |
| |
| const int options = (base::FileEnumerator::FILES | |
| base::FileEnumerator::DIRECTORIES | |
| base::FileEnumerator::SHOW_SYM_LINKS); |
| base::FileEnumerator enumerator(root_path, true /* recursive */, options); |
| |
| int64_t total_size = 0; |
| for (base::FilePath current = enumerator.Next(); !current.empty(); |
| current = enumerator.Next()) { |
| base::FileEnumerator::FileInfo info = enumerator.GetInfo(); |
| int64_t size = info.GetSize(); |
| const bool is_directory = info.IsDirectory(); |
| const bool is_symbolic_link = base::IsLink(info.GetName()); |
| const base::Time last_modified = info.GetLastModifiedTime(); |
| |
| auto entry = base::MakeUnique<base::DictionaryValue>(); |
| entry->SetString("path", current.value()); |
| // Use double instead of integer for large files. |
| entry->SetDouble("size", size); |
| entry->SetBoolean("is_directory", is_directory); |
| entry->SetBoolean("is_symbolic_link", is_symbolic_link); |
| entry->SetString( |
| "last_modified", |
| google_apis::util::FormatTimeAsStringLocaltime(last_modified)); |
| // Print lower 9 bits in octal format. |
| entry->SetString( |
| "permission", |
| base::StringPrintf("%03o", info.stat().st_mode & 0x1ff)); |
| files[current] = std::move(entry); |
| |
| total_size += size; |
| } |
| |
| // Convert |files| into |gcache_contents|. |
| for (auto& it : files) |
| gcache_contents->Append(std::move(it.second)); |
| |
| gcache_summary->SetDouble("total_size", total_size); |
| } |
| |
| // Gets the available disk space for the path |home_path|. |
| void GetFreeDiskSpace(const base::FilePath& home_path, |
| base::DictionaryValue* local_storage_summary) { |
| DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| DCHECK(local_storage_summary); |
| |
| const int64_t free_space = base::SysInfo::AmountOfFreeDiskSpace(home_path); |
| local_storage_summary->SetDouble("free_space", free_space); |
| } |
| |
| // Formats |entry| into text. |
| std::string FormatEntry(const base::FilePath& path, |
| const drive::ResourceEntry& entry) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| using base::StringAppendF; |
| |
| std::string out; |
| StringAppendF(&out, "%s\n", path.AsUTF8Unsafe().c_str()); |
| StringAppendF(&out, " title: %s\n", entry.title().c_str()); |
| StringAppendF(&out, " local_id: %s\n", entry.local_id().c_str()); |
| StringAppendF(&out, " resource_id: %s\n", entry.resource_id().c_str()); |
| StringAppendF(&out, " parent_local_id: %s\n", |
| entry.parent_local_id().c_str()); |
| StringAppendF(&out, " shared: %s\n", entry.shared() ? "true" : "false"); |
| StringAppendF(&out, " shared_with_me: %s\n", |
| entry.shared_with_me() ? "true" : "false"); |
| |
| const drive::PlatformFileInfoProto& file_info = entry.file_info(); |
| StringAppendF(&out, " file_info\n"); |
| StringAppendF(&out, " size: %" PRId64 "\n", file_info.size()); |
| StringAppendF(&out, " is_directory: %d\n", file_info.is_directory()); |
| StringAppendF(&out, " is_symbolic_link: %d\n", |
| file_info.is_symbolic_link()); |
| |
| const base::Time last_modified = base::Time::FromInternalValue( |
| file_info.last_modified()); |
| const base::Time last_accessed = base::Time::FromInternalValue( |
| file_info.last_accessed()); |
| const base::Time creation_time = base::Time::FromInternalValue( |
| file_info.creation_time()); |
| StringAppendF(&out, " last_modified: %s\n", |
| google_apis::util::FormatTimeAsString(last_modified).c_str()); |
| StringAppendF(&out, " last_accessed: %s\n", |
| google_apis::util::FormatTimeAsString(last_accessed).c_str()); |
| StringAppendF(&out, " creation_time: %s\n", |
| google_apis::util::FormatTimeAsString(creation_time).c_str()); |
| |
| if (entry.has_file_specific_info()) { |
| const drive::FileSpecificInfo& file_specific_info = |
| entry.file_specific_info(); |
| StringAppendF(&out, " alternate_url: %s\n", |
| file_specific_info.alternate_url().c_str()); |
| StringAppendF(&out, " content_mime_type: %s\n", |
| file_specific_info.content_mime_type().c_str()); |
| StringAppendF(&out, " file_md5: %s\n", |
| file_specific_info.md5().c_str()); |
| StringAppendF(&out, " document_extension: %s\n", |
| file_specific_info.document_extension().c_str()); |
| StringAppendF(&out, " is_hosted_document: %d\n", |
| file_specific_info.is_hosted_document()); |
| } |
| |
| if (entry.has_directory_specific_info()) { |
| StringAppendF(&out, " directory_info\n"); |
| const drive::DirectorySpecificInfo& directory_specific_info = |
| entry.directory_specific_info(); |
| StringAppendF(&out, " changestamp: %" PRId64 "\n", |
| directory_specific_info.changestamp()); |
| } |
| |
| return out; |
| } |
| |
| std::string SeverityToString(logging::LogSeverity severity) { |
| switch (severity) { |
| case logging::LOG_INFO: |
| return "info"; |
| case logging::LOG_WARNING: |
| return "warning"; |
| case logging::LOG_ERROR: |
| return "error"; |
| default: // Treat all other higher severities as ERROR. |
| return "error"; |
| } |
| } |
| |
| // Appends {'key': key, 'value': value} dictionary to the |list|. |
| void AppendKeyValue(base::ListValue* list, |
| const std::string& key, |
| const std::string& value) { |
| auto dict = base::MakeUnique<base::DictionaryValue>(); |
| dict->SetString("key", key); |
| dict->SetString("value", value); |
| list->Append(std::move(dict)); |
| } |
| |
| // Class to handle messages from chrome://drive-internals. |
| class DriveInternalsWebUIHandler : public content::WebUIMessageHandler { |
| public: |
| DriveInternalsWebUIHandler() |
| : last_sent_event_id_(-1), |
| weak_ptr_factory_(this) { |
| } |
| |
| ~DriveInternalsWebUIHandler() override {} |
| |
| private: |
| // WebUIMessageHandler override. |
| void RegisterMessages() override; |
| |
| // Returns a DriveIntegrationService. |
| drive::DriveIntegrationService* GetIntegrationService(); |
| |
| // Returns a DriveService instance. |
| drive::DriveServiceInterface* GetDriveService(); |
| |
| // Returns a DebugInfoCollector instance. |
| drive::DebugInfoCollector* GetDebugInfoCollector(); |
| |
| // Called when the page is first loaded. |
| void OnPageLoaded(const base::ListValue* args); |
| |
| // Updates respective sections. |
| void UpdateDriveRelatedPreferencesSection(); |
| void UpdateConnectionStatusSection( |
| drive::DriveServiceInterface* drive_service); |
| void UpdateAboutResourceSection( |
| drive::DriveServiceInterface* drive_service); |
| void UpdateAppListSection( |
| drive::DriveServiceInterface* drive_service); |
| void UpdateLocalMetadataSection( |
| drive::DebugInfoCollector* debug_info_collector); |
| void UpdateDeltaUpdateStatusSection( |
| drive::DebugInfoCollector* debug_info_collector); |
| void UpdateInFlightOperationsSection(drive::JobListInterface* job_list); |
| void UpdateGCacheContentsSection(); |
| void UpdateFileSystemContentsSection(); |
| void UpdateLocalStorageUsageSection(); |
| void UpdateCacheContentsSection( |
| drive::DebugInfoCollector* debug_info_collector); |
| void UpdateEventLogSection(); |
| void UpdatePathConfigurationsSection(); |
| |
| // Called when GetGCacheContents() is complete. |
| void OnGetGCacheContents(base::ListValue* gcache_contents, |
| base::DictionaryValue* cache_summary); |
| |
| // Called when GetResourceEntryByPath() is complete. |
| void OnGetResourceEntryByPath(const base::FilePath& path, |
| drive::FileError error, |
| std::unique_ptr<drive::ResourceEntry> entry); |
| |
| // Called when ReadDirectoryByPath() is complete. |
| void OnReadDirectoryByPath( |
| const base::FilePath& parent_path, |
| drive::FileError error, |
| std::unique_ptr<drive::ResourceEntryVector> entries); |
| |
| // Called as the iterator for DebugInfoCollector::IterateFileCache(). |
| void UpdateCacheEntry(const std::string& local_id, |
| const drive::FileCacheEntry& cache_entry); |
| |
| // Called when GetFreeDiskSpace() is complete. |
| void OnGetFreeDiskSpace(base::DictionaryValue* local_storage_summary); |
| |
| // Called when GetAboutResource() call to DriveService is complete. |
| void OnGetAboutResource( |
| google_apis::DriveApiErrorCode status, |
| std::unique_ptr<google_apis::AboutResource> about_resource); |
| |
| // Called when GetAppList() call to DriveService is complete. |
| void OnGetAppList(google_apis::DriveApiErrorCode status, |
| std::unique_ptr<google_apis::AppList> app_list); |
| |
| // Callback for DebugInfoCollector::GetMetadata for local update. |
| void OnGetFilesystemMetadataForLocal( |
| const drive::FileSystemMetadata& metadata); |
| |
| // Callback for DebugInfoCollector::GetMetadata for delta update. |
| void OnGetFilesystemMetadataForDeltaUpdate( |
| const drive::FileSystemMetadata& metadata); |
| |
| // Called when the page requests periodic update. |
| void OnPeriodicUpdate(const base::ListValue* args); |
| |
| // Called when the corresponding button on the page is pressed. |
| void ClearAccessToken(const base::ListValue* args); |
| void ClearRefreshToken(const base::ListValue* args); |
| void ResetDriveFileSystem(const base::ListValue* args); |
| void ListFileEntries(const base::ListValue* args); |
| |
| // Called after file system reset for ResetDriveFileSystem is done. |
| void ResetFinished(bool success); |
| |
| // The last event sent to the JavaScript side. |
| int last_sent_event_id_; |
| |
| base::WeakPtrFactory<DriveInternalsWebUIHandler> weak_ptr_factory_; |
| DISALLOW_COPY_AND_ASSIGN(DriveInternalsWebUIHandler); |
| }; |
| |
| void DriveInternalsWebUIHandler::OnGetAboutResource( |
| google_apis::DriveApiErrorCode status, |
| std::unique_ptr<google_apis::AboutResource> parsed_about_resource) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (status != google_apis::HTTP_SUCCESS) { |
| LOG(ERROR) << "Failed to get about resource"; |
| return; |
| } |
| DCHECK(parsed_about_resource); |
| |
| base::DictionaryValue about_resource; |
| about_resource.SetDouble("account-quota-total", |
| parsed_about_resource->quota_bytes_total()); |
| about_resource.SetDouble("account-quota-used", |
| parsed_about_resource->quota_bytes_used_aggregate()); |
| about_resource.SetDouble("account-largest-changestamp-remote", |
| parsed_about_resource->largest_change_id()); |
| about_resource.SetString("root-resource-id", |
| parsed_about_resource->root_folder_id()); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateAboutResource", about_resource); |
| } |
| |
| void DriveInternalsWebUIHandler::OnGetAppList( |
| google_apis::DriveApiErrorCode status, |
| std::unique_ptr<google_apis::AppList> parsed_app_list) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (status != google_apis::HTTP_SUCCESS) { |
| LOG(ERROR) << "Failed to get app list"; |
| return; |
| } |
| DCHECK(parsed_app_list); |
| |
| base::DictionaryValue app_list; |
| app_list.SetString("etag", parsed_app_list->etag()); |
| |
| base::ListValue* items = new base::ListValue(); |
| for (size_t i = 0; i < parsed_app_list->items().size(); ++i) { |
| const google_apis::AppResource* app = parsed_app_list->items()[i].get(); |
| auto app_data = base::MakeUnique<base::DictionaryValue>(); |
| app_data->SetString("name", app->name()); |
| app_data->SetString("application_id", app->application_id()); |
| app_data->SetString("object_type", app->object_type()); |
| app_data->SetBoolean("supports_create", app->supports_create()); |
| |
| items->Append(std::move(app_data)); |
| } |
| app_list.Set("items", items); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateAppList", app_list); |
| } |
| |
| void DriveInternalsWebUIHandler::RegisterMessages() { |
| web_ui()->RegisterMessageCallback( |
| "pageLoaded", |
| base::Bind(&DriveInternalsWebUIHandler::OnPageLoaded, |
| weak_ptr_factory_.GetWeakPtr())); |
| web_ui()->RegisterMessageCallback( |
| "periodicUpdate", |
| base::Bind(&DriveInternalsWebUIHandler::OnPeriodicUpdate, |
| weak_ptr_factory_.GetWeakPtr())); |
| web_ui()->RegisterMessageCallback( |
| "clearAccessToken", |
| base::Bind(&DriveInternalsWebUIHandler::ClearAccessToken, |
| weak_ptr_factory_.GetWeakPtr())); |
| web_ui()->RegisterMessageCallback( |
| "clearRefreshToken", |
| base::Bind(&DriveInternalsWebUIHandler::ClearRefreshToken, |
| weak_ptr_factory_.GetWeakPtr())); |
| web_ui()->RegisterMessageCallback( |
| "resetDriveFileSystem", |
| base::Bind(&DriveInternalsWebUIHandler::ResetDriveFileSystem, |
| weak_ptr_factory_.GetWeakPtr())); |
| web_ui()->RegisterMessageCallback( |
| "listFileEntries", |
| base::Bind(&DriveInternalsWebUIHandler::ListFileEntries, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| drive::DriveIntegrationService* |
| DriveInternalsWebUIHandler::GetIntegrationService() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| drive::DriveIntegrationService* service = |
| drive::DriveIntegrationServiceFactory::FindForProfile(profile); |
| if (!service || !service->is_enabled()) |
| return NULL; |
| return service; |
| } |
| |
| drive::DriveServiceInterface* DriveInternalsWebUIHandler::GetDriveService() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| return drive::util::GetDriveServiceByProfile(profile); |
| } |
| |
| drive::DebugInfoCollector* DriveInternalsWebUIHandler::GetDebugInfoCollector() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveIntegrationService* integration_service = GetIntegrationService(); |
| return integration_service ? |
| integration_service->debug_info_collector() : NULL; |
| } |
| |
| void DriveInternalsWebUIHandler::OnPageLoaded(const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveIntegrationService* integration_service = |
| GetIntegrationService(); |
| // |integration_service| may be NULL in the guest/incognito mode. |
| if (!integration_service) |
| return; |
| |
| drive::DriveServiceInterface* drive_service = |
| integration_service->drive_service(); |
| DCHECK(drive_service); |
| drive::DebugInfoCollector* debug_info_collector = |
| integration_service->debug_info_collector(); |
| DCHECK(debug_info_collector); |
| |
| UpdateDriveRelatedPreferencesSection(); |
| UpdateConnectionStatusSection(drive_service); |
| UpdateAboutResourceSection(drive_service); |
| UpdateAppListSection(drive_service); |
| UpdateLocalMetadataSection(debug_info_collector); |
| UpdateDeltaUpdateStatusSection(debug_info_collector); |
| UpdateInFlightOperationsSection(integration_service->job_list()); |
| UpdateGCacheContentsSection(); |
| UpdateCacheContentsSection(debug_info_collector); |
| UpdateLocalStorageUsageSection(); |
| UpdatePathConfigurationsSection(); |
| |
| // When the drive-internals page is reloaded by the reload key, the page |
| // content is recreated, but this WebUI object is not (instead, OnPageLoaded |
| // is called again). In that case, we have to forget the last sent ID here, |
| // and resent whole the logs to the page. |
| last_sent_event_id_ = -1; |
| UpdateEventLogSection(); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateDriveRelatedPreferencesSection() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| const char* kDriveRelatedPreferences[] = { |
| drive::prefs::kDisableDrive, |
| drive::prefs::kDisableDriveOverCellular, |
| drive::prefs::kDisableDriveHostedFiles, |
| }; |
| |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| PrefService* pref_service = profile->GetPrefs(); |
| |
| base::ListValue preferences; |
| for (size_t i = 0; i < arraysize(kDriveRelatedPreferences); ++i) { |
| const std::string key = kDriveRelatedPreferences[i]; |
| // As of now, all preferences are boolean. |
| const std::string value = |
| (pref_service->GetBoolean(key.c_str()) ? "true" : "false"); |
| AppendKeyValue(&preferences, key, value); |
| } |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateDriveRelatedPreferences", |
| preferences); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateConnectionStatusSection( |
| drive::DriveServiceInterface* drive_service) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(drive_service); |
| |
| std::string status; |
| switch (drive::util::GetDriveConnectionStatus(Profile::FromWebUI(web_ui()))) { |
| case drive::util::DRIVE_DISCONNECTED_NOSERVICE: |
| status = "no service"; |
| break; |
| case drive::util::DRIVE_DISCONNECTED_NONETWORK: |
| status = "no network"; |
| break; |
| case drive::util::DRIVE_DISCONNECTED_NOTREADY: |
| status = "not ready"; |
| break; |
| case drive::util::DRIVE_CONNECTED_METERED: |
| status = "metered"; |
| break; |
| case drive::util::DRIVE_CONNECTED: |
| status = "connected"; |
| break; |
| } |
| |
| base::DictionaryValue connection_status; |
| connection_status.SetString("status", status); |
| connection_status.SetBoolean("has-refresh-token", |
| drive_service->HasRefreshToken()); |
| connection_status.SetBoolean("has-access-token", |
| drive_service->HasAccessToken()); |
| web_ui()->CallJavascriptFunctionUnsafe("updateConnectionStatus", |
| connection_status); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateAboutResourceSection( |
| drive::DriveServiceInterface* drive_service) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(drive_service); |
| |
| drive_service->GetAboutResource( |
| base::Bind(&DriveInternalsWebUIHandler::OnGetAboutResource, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateAppListSection( |
| drive::DriveServiceInterface* drive_service) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(drive_service); |
| |
| drive_service->GetAppList( |
| base::Bind(&DriveInternalsWebUIHandler::OnGetAppList, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateLocalMetadataSection( |
| drive::DebugInfoCollector* debug_info_collector) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(debug_info_collector); |
| |
| debug_info_collector->GetMetadata( |
| base::Bind(&DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForLocal( |
| const drive::FileSystemMetadata& metadata) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| base::DictionaryValue local_metadata; |
| local_metadata.SetDouble("account-largest-changestamp-local", |
| metadata.largest_changestamp); |
| local_metadata.SetBoolean("account-metadata-refreshing", metadata.refreshing); |
| web_ui()->CallJavascriptFunctionUnsafe("updateLocalMetadata", local_metadata); |
| } |
| |
| void DriveInternalsWebUIHandler::ClearAccessToken(const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveServiceInterface* drive_service = GetDriveService(); |
| if (drive_service) |
| drive_service->ClearAccessToken(); |
| } |
| |
| void DriveInternalsWebUIHandler::ClearRefreshToken( |
| const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveServiceInterface* drive_service = GetDriveService(); |
| if (drive_service) |
| drive_service->ClearRefreshToken(); |
| } |
| |
| void DriveInternalsWebUIHandler::ResetDriveFileSystem( |
| const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveIntegrationService* integration_service = |
| GetIntegrationService(); |
| if (integration_service) { |
| integration_service->ClearCacheAndRemountFileSystem( |
| base::Bind(&DriveInternalsWebUIHandler::ResetFinished, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } |
| |
| void DriveInternalsWebUIHandler::ResetFinished(bool success) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateResetStatus", |
| base::Value(success)); |
| } |
| |
| void DriveInternalsWebUIHandler::ListFileEntries(const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| UpdateFileSystemContentsSection(); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateDeltaUpdateStatusSection( |
| drive::DebugInfoCollector* debug_info_collector) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(debug_info_collector); |
| |
| debug_info_collector->GetMetadata( |
| base::Bind( |
| &DriveInternalsWebUIHandler::OnGetFilesystemMetadataForDeltaUpdate, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DriveInternalsWebUIHandler::OnGetFilesystemMetadataForDeltaUpdate( |
| const drive::FileSystemMetadata& metadata) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| drive::DriveNotificationManager* drive_notification_manager = |
| drive::DriveNotificationManagerFactory::FindForBrowserContext(profile); |
| if (!drive_notification_manager) |
| return; |
| |
| base::DictionaryValue delta_update_status; |
| delta_update_status.SetBoolean( |
| "push-notification-enabled", |
| drive_notification_manager->push_notification_enabled()); |
| delta_update_status.SetString( |
| "last-update-check-time", |
| google_apis::util::FormatTimeAsStringLocaltime( |
| metadata.last_update_check_time)); |
| delta_update_status.SetString( |
| "last-update-check-error", |
| drive::FileErrorToString(metadata.last_update_check_error)); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateDeltaUpdateStatus", |
| delta_update_status); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateInFlightOperationsSection( |
| drive::JobListInterface* job_list) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(job_list); |
| |
| std::vector<drive::JobInfo> info_list = job_list->GetJobInfoList(); |
| |
| base::ListValue in_flight_operations; |
| for (size_t i = 0; i < info_list.size(); ++i) { |
| const drive::JobInfo& info = info_list[i]; |
| |
| auto dict = base::MakeUnique<base::DictionaryValue>(); |
| dict->SetInteger("id", info.job_id); |
| dict->SetString("type", drive::JobTypeToString(info.job_type)); |
| dict->SetString("file_path", info.file_path.AsUTF8Unsafe()); |
| dict->SetString("state", drive::JobStateToString(info.state)); |
| dict->SetDouble("progress_current", info.num_completed_bytes); |
| dict->SetDouble("progress_total", info.num_total_bytes); |
| in_flight_operations.Append(std::move(dict)); |
| } |
| web_ui()->CallJavascriptFunctionUnsafe("updateInFlightOperations", |
| in_flight_operations); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateGCacheContentsSection() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Start updating the GCache contents section. |
| Profile* profile = Profile::FromWebUI(web_ui()); |
| const base::FilePath root_path = drive::util::GetCacheRootPath(profile); |
| base::ListValue* gcache_contents = new base::ListValue; |
| base::DictionaryValue* gcache_summary = new base::DictionaryValue; |
| base::PostTaskWithTraitsAndReply( |
| FROM_HERE, base::TaskTraits().MayBlock().WithPriority( |
| base::TaskPriority::USER_VISIBLE), |
| base::Bind(&GetGCacheContents, root_path, gcache_contents, |
| gcache_summary), |
| base::Bind(&DriveInternalsWebUIHandler::OnGetGCacheContents, |
| weak_ptr_factory_.GetWeakPtr(), base::Owned(gcache_contents), |
| base::Owned(gcache_summary))); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateFileSystemContentsSection() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DebugInfoCollector* debug_info_collector = GetDebugInfoCollector(); |
| if (!debug_info_collector) |
| return; |
| |
| // Start rendering the file system tree as text. |
| const base::FilePath root_path = drive::util::GetDriveGrandRootPath(); |
| |
| debug_info_collector->GetResourceEntry( |
| root_path, |
| base::Bind(&DriveInternalsWebUIHandler::OnGetResourceEntryByPath, |
| weak_ptr_factory_.GetWeakPtr(), |
| root_path)); |
| |
| debug_info_collector->ReadDirectory( |
| root_path, |
| base::Bind(&DriveInternalsWebUIHandler::OnReadDirectoryByPath, |
| weak_ptr_factory_.GetWeakPtr(), |
| root_path)); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateLocalStorageUsageSection() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Propagate the amount of local free space in bytes. |
| base::FilePath home_path; |
| if (PathService::Get(base::DIR_HOME, &home_path)) { |
| base::DictionaryValue* local_storage_summary = new base::DictionaryValue; |
| base::PostTaskWithTraitsAndReply( |
| FROM_HERE, base::TaskTraits().MayBlock().WithPriority( |
| base::TaskPriority::USER_VISIBLE), |
| base::Bind(&GetFreeDiskSpace, home_path, local_storage_summary), |
| base::Bind(&DriveInternalsWebUIHandler::OnGetFreeDiskSpace, |
| weak_ptr_factory_.GetWeakPtr(), |
| base::Owned(local_storage_summary))); |
| } else { |
| LOG(ERROR) << "Home directory not found"; |
| } |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateCacheContentsSection( |
| drive::DebugInfoCollector* debug_info_collector) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(debug_info_collector); |
| |
| debug_info_collector->IterateFileCache( |
| base::Bind(&DriveInternalsWebUIHandler::UpdateCacheEntry, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Bind(&base::DoNothing)); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateEventLogSection() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveIntegrationService* integration_service = |
| GetIntegrationService(); |
| if (!integration_service) |
| return; |
| |
| const std::vector<drive::EventLogger::Event> log = |
| integration_service->event_logger()->GetHistory(); |
| |
| base::ListValue list; |
| for (size_t i = 0; i < log.size(); ++i) { |
| // Skip events which were already sent. |
| if (log[i].id <= last_sent_event_id_) |
| continue; |
| |
| std::string severity = SeverityToString(log[i].severity); |
| |
| auto dict = base::MakeUnique<base::DictionaryValue>(); |
| dict->SetString("key", |
| google_apis::util::FormatTimeAsStringLocaltime(log[i].when)); |
| dict->SetString("value", "[" + severity + "] " + log[i].what); |
| dict->SetString("class", "log-" + severity); |
| list.Append(std::move(dict)); |
| last_sent_event_id_ = log[i].id; |
| } |
| if (!list.empty()) |
| web_ui()->CallJavascriptFunctionUnsafe("updateEventLog", list); |
| } |
| |
| void DriveInternalsWebUIHandler::UpdatePathConfigurationsSection() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| Profile* const profile = Profile::FromWebUI(web_ui()); |
| |
| base::ListValue paths; |
| |
| AppendKeyValue( |
| &paths, "Downloads", |
| file_manager::util::GetDownloadsFolderForProfile(profile).AsUTF8Unsafe()); |
| AppendKeyValue( |
| &paths, "Drive", |
| drive::util::GetDriveMountPointPath(profile).AsUTF8Unsafe()); |
| |
| const char* kPathPreferences[] = { |
| prefs::kSelectFileLastDirectory, |
| prefs::kSaveFileDefaultDirectory, |
| prefs::kDownloadDefaultDirectory, |
| }; |
| for (size_t i = 0; i < arraysize(kPathPreferences); ++i) { |
| const char* const key = kPathPreferences[i]; |
| AppendKeyValue(&paths, key, |
| profile->GetPrefs()->GetFilePath(key).AsUTF8Unsafe()); |
| } |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updatePathConfigurations", paths); |
| } |
| |
| void DriveInternalsWebUIHandler::OnGetGCacheContents( |
| base::ListValue* gcache_contents, |
| base::DictionaryValue* gcache_summary) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(gcache_contents); |
| DCHECK(gcache_summary); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateGCacheContents", |
| *gcache_contents, *gcache_summary); |
| } |
| |
| void DriveInternalsWebUIHandler::OnGetResourceEntryByPath( |
| const base::FilePath& path, |
| drive::FileError error, |
| std::unique_ptr<drive::ResourceEntry> entry) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (error == drive::FILE_ERROR_OK) { |
| DCHECK(entry.get()); |
| const base::Value value(FormatEntry(path, *entry) + "\n"); |
| web_ui()->CallJavascriptFunctionUnsafe("updateFileSystemContents", value); |
| } |
| } |
| |
| void DriveInternalsWebUIHandler::OnReadDirectoryByPath( |
| const base::FilePath& parent_path, |
| drive::FileError error, |
| std::unique_ptr<drive::ResourceEntryVector> entries) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (error == drive::FILE_ERROR_OK) { |
| DCHECK(entries.get()); |
| |
| drive::DebugInfoCollector* debug_info_collector = GetDebugInfoCollector(); |
| std::string file_system_as_text; |
| for (size_t i = 0; i < entries->size(); ++i) { |
| const drive::ResourceEntry& entry = (*entries)[i]; |
| const base::FilePath current_path = parent_path.Append( |
| base::FilePath::FromUTF8Unsafe(entry.base_name())); |
| |
| file_system_as_text.append(FormatEntry(current_path, entry) + "\n"); |
| |
| if (entry.file_info().is_directory()) { |
| debug_info_collector->ReadDirectory( |
| current_path, |
| base::Bind(&DriveInternalsWebUIHandler::OnReadDirectoryByPath, |
| weak_ptr_factory_.GetWeakPtr(), |
| current_path)); |
| } |
| } |
| |
| // There may be pending ReadDirectoryByPath() calls, but we can update |
| // the page with what we have now. This results in progressive |
| // updates, which is good for a large file system. |
| const base::Value value(file_system_as_text); |
| web_ui()->CallJavascriptFunctionUnsafe("updateFileSystemContents", value); |
| } |
| } |
| |
| void DriveInternalsWebUIHandler::UpdateCacheEntry( |
| const std::string& local_id, |
| const drive::FileCacheEntry& cache_entry) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| // Convert |cache_entry| into a dictionary. |
| base::DictionaryValue value; |
| value.SetString("local_id", local_id); |
| value.SetString("md5", cache_entry.md5()); |
| value.SetBoolean("is_present", cache_entry.is_present()); |
| value.SetBoolean("is_pinned", cache_entry.is_pinned()); |
| value.SetBoolean("is_dirty", cache_entry.is_dirty()); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateCacheContents", value); |
| } |
| |
| void DriveInternalsWebUIHandler::OnGetFreeDiskSpace( |
| base::DictionaryValue* local_storage_summary) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(local_storage_summary); |
| |
| web_ui()->CallJavascriptFunctionUnsafe("updateLocalStorageUsage", |
| *local_storage_summary); |
| } |
| |
| void DriveInternalsWebUIHandler::OnPeriodicUpdate(const base::ListValue* args) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| drive::DriveIntegrationService* integration_service = |
| GetIntegrationService(); |
| // |integration_service| may be NULL in the guest/incognito mode. |
| if (!integration_service) |
| return; |
| |
| UpdateInFlightOperationsSection(integration_service->job_list()); |
| UpdateEventLogSection(); |
| } |
| |
| } // namespace |
| |
| DriveInternalsUI::DriveInternalsUI(content::WebUI* web_ui) |
| : WebUIController(web_ui) { |
| web_ui->AddMessageHandler(base::MakeUnique<DriveInternalsWebUIHandler>()); |
| |
| content::WebUIDataSource* source = |
| content::WebUIDataSource::Create(chrome::kChromeUIDriveInternalsHost); |
| source->AddResourcePath("drive_internals.css", IDR_DRIVE_INTERNALS_CSS); |
| source->AddResourcePath("drive_internals.js", IDR_DRIVE_INTERNALS_JS); |
| source->SetDefaultResource(IDR_DRIVE_INTERNALS_HTML); |
| |
| Profile* profile = Profile::FromWebUI(web_ui); |
| content::WebUIDataSource::Add(profile, source); |
| } |
| |
| } // namespace chromeos |