Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / webui / chromeos / drive_internals_ui.cc
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