e5398f54a4ea7748bf11bb06328443ac1e2df20b
[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/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