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