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