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