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