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