Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(463)

Side by Side Diff: chrome/browser/chromeos/extensions/file_manager/file_manager_event_router.cc

Issue 22150005: file_manager: Rename file_manager_event_router.h to event_router.h (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebase Created 7 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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/chromeos/extensions/file_manager/file_manager_event_rou ter.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "base/values.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
17 #include "chrome/browser/chromeos/drive/file_system_interface.h"
18 #include "chrome/browser/chromeos/drive/file_system_util.h"
19 #include "chrome/browser/chromeos/extensions/file_manager/desktop_notifications. h"
20 #include "chrome/browser/chromeos/extensions/file_manager/file_manager_util.h"
21 #include "chrome/browser/chromeos/extensions/file_manager/mounted_disk_monitor.h "
22 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
23 #include "chrome/browser/chromeos/login/screen_locker.h"
24 #include "chrome/browser/chromeos/net/connectivity_state_helper.h"
25 #include "chrome/browser/drive/drive_service_interface.h"
26 #include "chrome/browser/extensions/event_names.h"
27 #include "chrome/browser/extensions/event_router.h"
28 #include "chrome/browser/extensions/extension_service.h"
29 #include "chrome/browser/extensions/extension_system.h"
30 #include "chrome/browser/profiles/profile.h"
31 #include "chrome/common/pref_names.h"
32 #include "chromeos/login/login_state.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_source.h"
35 #include "webkit/common/fileapi/file_system_types.h"
36 #include "webkit/common/fileapi/file_system_util.h"
37
38 using chromeos::disks::DiskMountManager;
39 using content::BrowserThread;
40 using drive::DriveIntegrationService;
41 using drive::DriveIntegrationServiceFactory;
42
43 namespace file_manager {
44 namespace {
45
46 const char kPathChanged[] = "changed";
47 const char kPathWatchError[] = "error";
48
49 // Used as a callback for FileSystem::MarkCacheFileAsUnmounted().
50 void OnMarkAsUnmounted(drive::FileError error) {
51 // Do nothing.
52 }
53
54 const char* MountErrorToString(chromeos::MountError error) {
55 switch (error) {
56 case chromeos::MOUNT_ERROR_NONE:
57 return "success";
58 case chromeos::MOUNT_ERROR_UNKNOWN:
59 return "error_unknown";
60 case chromeos::MOUNT_ERROR_INTERNAL:
61 return "error_internal";
62 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
63 return "error_invalid_argument";
64 case chromeos::MOUNT_ERROR_INVALID_PATH:
65 return "error_invalid_path";
66 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
67 return "error_path_already_mounted";
68 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
69 return "error_path_not_mounted";
70 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
71 return "error_directory_creation_failed";
72 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
73 return "error_invalid_mount_options";
74 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
75 return "error_invalid_unmount_options";
76 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
77 return "error_insufficient_permissions";
78 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
79 return "error_mount_program_not_found";
80 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
81 return "error_mount_program_failed";
82 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
83 return "error_invalid_device_path";
84 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
85 return "error_unknown_filesystem";
86 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
87 return "error_unsuported_filesystem";
88 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
89 return "error_invalid_archive";
90 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
91 return "error_authentication";
92 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
93 return "error_path_unmounted";
94 }
95 NOTREACHED();
96 return "";
97 }
98
99 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
100 const base::Closure& success_callback,
101 const base::Closure& failure_callback) {
102 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
103
104 if (base::DirectoryExists(directory_path))
105 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
106 else
107 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
108 };
109
110 void DirectoryExistsOnUIThread(const base::FilePath& directory_path,
111 const base::Closure& success_callback,
112 const base::Closure& failure_callback) {
113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
114
115 content::BrowserThread::PostBlockingPoolTask(
116 FROM_HERE,
117 base::Bind(&DirectoryExistsOnBlockingPool,
118 directory_path,
119 success_callback,
120 failure_callback));
121 };
122
123 // Creates a base::FilePathWatcher and starts watching at |watch_path| with
124 // |callback|. Returns NULL on failure.
125 base::FilePathWatcher* CreateAndStartFilePathWatcher(
126 const base::FilePath& watch_path,
127 const base::FilePathWatcher::Callback& callback) {
128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
129 DCHECK(!callback.is_null());
130
131 base::FilePathWatcher* watcher(new base::FilePathWatcher);
132 if (!watcher->Watch(watch_path, false /* recursive */, callback)) {
133 delete watcher;
134 return NULL;
135 }
136
137 return watcher;
138 }
139
140 // Constants for the "transferState" field of onFileTransferUpdated event.
141 const char kFileTransferStateStarted[] = "started";
142 const char kFileTransferStateInProgress[] = "in_progress";
143 const char kFileTransferStateCompleted[] = "completed";
144 const char kFileTransferStateFailed[] = "failed";
145
146 // Frequency of sending onFileTransferUpdated.
147 const int64 kFileTransferEventFrequencyInMilliseconds = 1000;
148
149 // Utility function to check if |job_info| is a file uploading job.
150 bool IsUploadJob(drive::JobType type) {
151 return type == drive::TYPE_UPLOAD_NEW_FILE ||
152 type == drive::TYPE_UPLOAD_EXISTING_FILE;
153 }
154
155 // Utility function to check if |job_info| is a file downloading job.
156 bool IsDownloadJob(drive::JobType type) {
157 return type == drive::TYPE_DOWNLOAD_FILE;
158 }
159
160 // Converts the job info to its JSON (Value) form.
161 scoped_ptr<base::DictionaryValue> JobInfoToDictionaryValue(
162 const std::string& extension_id,
163 const std::string& job_status,
164 const drive::JobInfo& job_info) {
165 DCHECK(IsActiveFileTransferJobInfo(job_info));
166
167 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
168 GURL url = util::ConvertRelativePathToFileSystemUrl(
169 job_info.file_path, extension_id);
170 result->SetString("fileUrl", url.spec());
171 result->SetString("transferState", job_status);
172 result->SetString("transferType",
173 IsUploadJob(job_info.job_type) ? "upload" : "download");
174 result->SetInteger("processed",
175 static_cast<int>(job_info.num_completed_bytes));
176 result->SetInteger("total", static_cast<int>(job_info.num_total_bytes));
177 return result.Pass();
178 }
179
180 // Checks for availability of the Google+ Photos app.
181 bool IsGooglePhotosInstalled(Profile *profile) {
182 ExtensionService* service =
183 extensions::ExtensionSystem::Get(profile)->extension_service();
184 if (!service)
185 return false;
186
187 // Google+ Photos uses several ids for different channels. Therefore, all of
188 // them should be checked.
189 const std::string kGooglePlusPhotosIds[] = {
190 "ebpbnabdhheoknfklmpddcdijjkmklkp", // G+ Photos staging
191 "efjnaogkjbogokcnohkmnjdojkikgobo", // G+ Photos prod
192 "ejegoaikibpmikoejfephaneibodccma" // G+ Photos dev
193 };
194
195 for (size_t i = 0; i < arraysize(kGooglePlusPhotosIds); ++i) {
196 if (service->GetExtensionById(kGooglePlusPhotosIds[i],
197 false /* include_disable */) != NULL)
198 return true;
199 }
200
201 return false;
202 }
203
204 } // namespace
205
206 // Pass dummy value to JobInfo's constructor for make it default constructible.
207 FileManagerEventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
208 : job_info(drive::TYPE_DOWNLOAD_FILE) {
209 }
210
211 FileManagerEventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
212 const drive::JobInfo& info, const std::string& status)
213 : job_info(info), status(status) {
214 }
215
216 FileManagerEventRouter::FileManagerEventRouter(
217 Profile* profile)
218 : notifications_(new DesktopNotifications(profile)),
219 pref_change_registrar_(new PrefChangeRegistrar),
220 profile_(profile),
221 weak_factory_(this) {
222 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
223
224 file_watcher_callback_ =
225 base::Bind(&FileManagerEventRouter::HandleFileWatchNotification,
226 weak_factory_.GetWeakPtr());
227 }
228
229 FileManagerEventRouter::~FileManagerEventRouter() {
230 }
231
232 void FileManagerEventRouter::Shutdown() {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234
235 DLOG_IF(WARNING, !file_watchers_.empty()) << "Not all file watchers are "
236 << "removed. This can happen when Files.app is open during shutdown.";
237 STLDeleteValues(&file_watchers_);
238 if (!profile_) {
239 NOTREACHED();
240 return;
241 }
242
243 DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
244 if (disk_mount_manager)
245 disk_mount_manager->RemoveObserver(this);
246
247 DriveIntegrationService* integration_service =
248 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
249 profile_);
250 if (integration_service) {
251 integration_service->RemoveObserver(this);
252 integration_service->file_system()->RemoveObserver(this);
253 integration_service->drive_service()->RemoveObserver(this);
254 integration_service->job_list()->RemoveObserver(this);
255 }
256
257 if (chromeos::ConnectivityStateHelper::IsInitialized()) {
258 chromeos::ConnectivityStateHelper::Get()->
259 RemoveNetworkManagerObserver(this);
260 }
261 profile_ = NULL;
262 }
263
264 void FileManagerEventRouter::ObserveFileSystemEvents() {
265 if (!profile_) {
266 NOTREACHED();
267 return;
268 }
269 if (!chromeos::LoginState::IsInitialized() ||
270 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
271 return;
272 }
273
274 DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
275 if (disk_mount_manager) {
276 disk_mount_manager->RemoveObserver(this);
277 disk_mount_manager->AddObserver(this);
278 disk_mount_manager->RequestMountInfoRefresh();
279 }
280
281 DriveIntegrationService* integration_service =
282 DriveIntegrationServiceFactory::GetForProfileRegardlessOfStates(
283 profile_);
284 if (integration_service) {
285 integration_service->AddObserver(this);
286 integration_service->drive_service()->AddObserver(this);
287 integration_service->file_system()->AddObserver(this);
288 integration_service->job_list()->AddObserver(this);
289 }
290
291 if (chromeos::ConnectivityStateHelper::IsInitialized()) {
292 chromeos::ConnectivityStateHelper::Get()->
293 AddNetworkManagerObserver(this);
294 }
295
296 mounted_disk_monitor_.reset(new MountedDiskMonitor());
297
298 pref_change_registrar_->Init(profile_->GetPrefs());
299
300 pref_change_registrar_->Add(
301 prefs::kExternalStorageDisabled,
302 base::Bind(&FileManagerEventRouter::OnExternalStorageDisabledChanged,
303 weak_factory_.GetWeakPtr()));
304
305 base::Closure callback =
306 base::Bind(&FileManagerEventRouter::OnFileManagerPrefsChanged,
307 weak_factory_.GetWeakPtr());
308 pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
309 pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
310 pref_change_registrar_->Add(prefs::kDisableDrive, callback);
311 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
312 }
313
314 // File watch setup routines.
315 void FileManagerEventRouter::AddFileWatch(
316 const base::FilePath& local_path,
317 const base::FilePath& virtual_path,
318 const std::string& extension_id,
319 const BoolCallback& callback) {
320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
321 DCHECK(!callback.is_null());
322
323 base::FilePath watch_path = local_path;
324 bool is_remote_watch = false;
325 // Tweak watch path for remote sources - we need to drop leading /special
326 // directory from there in order to be able to pair these events with
327 // their change notifications.
328 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
329 watch_path = drive::util::ExtractDrivePath(watch_path);
330 is_remote_watch = true;
331 }
332
333 WatcherMap::iterator iter = file_watchers_.find(watch_path);
334 if (iter == file_watchers_.end()) {
335 scoped_ptr<FileWatcherExtensions>
336 watch(new FileWatcherExtensions(virtual_path,
337 extension_id,
338 is_remote_watch));
339 watch->Watch(watch_path,
340 file_watcher_callback_,
341 callback);
342 file_watchers_[watch_path] = watch.release();
343 } else {
344 iter->second->AddExtension(extension_id);
345 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
346 base::Bind(callback, true));
347 }
348 }
349
350 void FileManagerEventRouter::RemoveFileWatch(
351 const base::FilePath& local_path,
352 const std::string& extension_id) {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354
355 base::FilePath watch_path = local_path;
356 // Tweak watch path for remote sources - we need to drop leading /special
357 // directory from there in order to be able to pair these events with
358 // their change notifications.
359 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
360 watch_path = drive::util::ExtractDrivePath(watch_path);
361 }
362 WatcherMap::iterator iter = file_watchers_.find(watch_path);
363 if (iter == file_watchers_.end())
364 return;
365 // Remove the renderer process for this watch.
366 iter->second->RemoveExtension(extension_id);
367 if (iter->second->ref_count() == 0) {
368 delete iter->second;
369 file_watchers_.erase(iter);
370 }
371 }
372
373 void FileManagerEventRouter::OnDiskEvent(
374 DiskMountManager::DiskEvent event,
375 const DiskMountManager::Disk* disk) {
376 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
377
378 // Disregard hidden devices.
379 if (disk->is_hidden())
380 return;
381 if (event == DiskMountManager::DISK_ADDED) {
382 OnDiskAdded(disk);
383 } else if (event == DiskMountManager::DISK_REMOVED) {
384 OnDiskRemoved(disk);
385 }
386 }
387
388 void FileManagerEventRouter::OnDeviceEvent(
389 DiskMountManager::DeviceEvent event,
390 const std::string& device_path) {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
392
393 if (event == DiskMountManager::DEVICE_ADDED) {
394 OnDeviceAdded(device_path);
395 } else if (event == DiskMountManager::DEVICE_REMOVED) {
396 OnDeviceRemoved(device_path);
397 } else if (event == DiskMountManager::DEVICE_SCANNED) {
398 OnDeviceScanned(device_path);
399 }
400 }
401
402 void FileManagerEventRouter::OnMountEvent(
403 DiskMountManager::MountEvent event,
404 chromeos::MountError error_code,
405 const DiskMountManager::MountPointInfo& mount_info) {
406 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
407 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
408 // happen at shutdown.
409 if (!profile_)
410 return;
411
412 DCHECK(mount_info.mount_type != chromeos::MOUNT_TYPE_INVALID);
413
414 DispatchMountEvent(event, error_code, mount_info);
415
416 if (mount_info.mount_type == chromeos::MOUNT_TYPE_DEVICE &&
417 event == DiskMountManager::MOUNTING) {
418 DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
419 const DiskMountManager::Disk* disk =
420 disk_mount_manager->FindDiskBySourcePath(mount_info.source_path);
421 if (!disk || mounted_disk_monitor_->DiskIsRemounting(*disk))
422 return;
423
424 notifications_->ManageNotificationsOnMountCompleted(
425 disk->system_path_prefix(), disk->drive_label(), disk->is_parent(),
426 error_code == chromeos::MOUNT_ERROR_NONE,
427 error_code == chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM);
428
429 // If a new device was mounted, a new File manager window may need to be
430 // opened.
431 if (error_code == chromeos::MOUNT_ERROR_NONE)
432 ShowRemovableDeviceInFileManager(
433 *disk,
434 base::FilePath::FromUTF8Unsafe(mount_info.mount_path));
435 } else if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
436 // Clear the "mounted" state for archive files in drive cache
437 // when mounting failed or unmounting succeeded.
438 if ((event == DiskMountManager::MOUNTING) !=
439 (error_code == chromeos::MOUNT_ERROR_NONE)) {
440 DriveIntegrationService* integration_service =
441 DriveIntegrationServiceFactory::GetForProfile(profile_);
442 drive::FileSystemInterface* file_system =
443 integration_service ? integration_service->file_system() : NULL;
444 if (file_system) {
445 file_system->MarkCacheFileAsUnmounted(
446 base::FilePath(mount_info.source_path),
447 base::Bind(&OnMarkAsUnmounted));
448 }
449 }
450 }
451 }
452
453 void FileManagerEventRouter::OnFormatEvent(
454 DiskMountManager::FormatEvent event,
455 chromeos::FormatError error_code,
456 const std::string& device_path) {
457 if (event == DiskMountManager::FORMAT_STARTED) {
458 OnFormatStarted(device_path, error_code == chromeos::FORMAT_ERROR_NONE);
459 } else if (event == DiskMountManager::FORMAT_COMPLETED) {
460 OnFormatCompleted(device_path, error_code == chromeos::FORMAT_ERROR_NONE);
461 }
462 }
463
464 void FileManagerEventRouter::NetworkManagerChanged() {
465 if (!profile_ ||
466 !extensions::ExtensionSystem::Get(profile_)->event_router()) {
467 NOTREACHED();
468 return;
469 }
470 scoped_ptr<extensions::Event> event(new extensions::Event(
471 extensions::event_names::kOnFileBrowserDriveConnectionStatusChanged,
472 scoped_ptr<ListValue>(new ListValue())));
473 extensions::ExtensionSystem::Get(profile_)->event_router()->
474 BroadcastEvent(event.Pass());
475 }
476
477 void FileManagerEventRouter::DefaultNetworkChanged() {
478 NetworkManagerChanged();
479 }
480
481 void FileManagerEventRouter::OnExternalStorageDisabledChanged() {
482 // If the policy just got disabled we have to unmount every device currently
483 // mounted. The opposite is fine - we can let the user re-plug her device to
484 // make it available.
485 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
486 DiskMountManager* manager = DiskMountManager::GetInstance();
487 DiskMountManager::MountPointMap mounts(manager->mount_points());
488 for (DiskMountManager::MountPointMap::const_iterator it = mounts.begin();
489 it != mounts.end(); ++it) {
490 LOG(INFO) << "Unmounting " << it->second.mount_path
491 << " because of policy.";
492 manager->UnmountPath(it->second.mount_path,
493 chromeos::UNMOUNT_OPTIONS_NONE,
494 DiskMountManager::UnmountPathCallback());
495 }
496 }
497 }
498
499 void FileManagerEventRouter::OnFileManagerPrefsChanged() {
500 if (!profile_ ||
501 !extensions::ExtensionSystem::Get(profile_)->event_router()) {
502 NOTREACHED();
503 return;
504 }
505
506 scoped_ptr<extensions::Event> event(new extensions::Event(
507 extensions::event_names::kOnFileBrowserPreferencesChanged,
508 scoped_ptr<ListValue>(new ListValue())));
509 extensions::ExtensionSystem::Get(profile_)->event_router()->
510 BroadcastEvent(event.Pass());
511 }
512
513 void FileManagerEventRouter::OnJobAdded(const drive::JobInfo& job_info) {
514 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
515 OnJobUpdated(job_info);
516 }
517
518 void FileManagerEventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
520 if (!drive::IsActiveFileTransferJobInfo(job_info))
521 return;
522
523 bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
524
525 // Replace with the latest job info.
526 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
527 job_info,
528 is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress);
529
530 // Fire event if needed.
531 bool always = is_new_job;
532 SendDriveFileTransferEvent(always);
533 }
534
535 void FileManagerEventRouter::OnJobDone(const drive::JobInfo& job_info,
536 drive::FileError error) {
537 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
538 if (!drive::IsActiveFileTransferJobInfo(job_info))
539 return;
540
541 // Replace with the latest job info.
542 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
543 job_info,
544 error == drive::FILE_ERROR_OK ? kFileTransferStateCompleted
545 : kFileTransferStateFailed);
546
547 // Fire event if needed.
548 bool always = true;
549 SendDriveFileTransferEvent(always);
550
551 // Forget about the job.
552 drive_jobs_.erase(job_info.job_id);
553 }
554
555 void FileManagerEventRouter::SendDriveFileTransferEvent(bool always) {
556 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
557
558 const base::Time now = base::Time::Now();
559
560 // When |always| flag is not set, we don't send the event until certain
561 // amount of time passes after the previous one. This is to avoid
562 // flooding the IPC between extensions by many onFileTransferUpdated events.
563 if (!always) {
564 const int64 delta = (now - last_file_transfer_event_).InMilliseconds();
565 // delta < 0 may rarely happen if system clock is synced and rewinded.
566 // To be conservative, we don't skip in that case.
567 if (0 <= delta && delta < kFileTransferEventFrequencyInMilliseconds)
568 return;
569 }
570
571 // Convert the current |drive_jobs_| to a JSON value.
572 scoped_ptr<base::ListValue> event_list(new base::ListValue);
573 for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
574 iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
575
576 scoped_ptr<base::DictionaryValue> job_info_dict(
577 JobInfoToDictionaryValue(kFileBrowserDomain,
578 iter->second.status,
579 iter->second.job_info));
580 event_list->Append(job_info_dict.release());
581 }
582
583 scoped_ptr<ListValue> args(new ListValue());
584 args->Append(event_list.release());
585 scoped_ptr<extensions::Event> event(new extensions::Event(
586 extensions::event_names::kOnFileTransfersUpdated, args.Pass()));
587 extensions::ExtensionSystem::Get(profile_)->event_router()->
588 DispatchEventToExtension(kFileBrowserDomain, event.Pass());
589
590 last_file_transfer_event_ = now;
591 }
592
593 void FileManagerEventRouter::OnDirectoryChanged(
594 const base::FilePath& directory_path) {
595 HandleFileWatchNotification(directory_path, false);
596 }
597
598 void FileManagerEventRouter::OnFileSystemMounted() {
599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
600
601 const std::string& drive_path = drive::util::GetDriveMountPointPathAsString();
602 DiskMountManager::MountPointInfo mount_info(
603 drive_path,
604 drive_path,
605 chromeos::MOUNT_TYPE_GOOGLE_DRIVE,
606 chromeos::disks::MOUNT_CONDITION_NONE);
607
608 // Raise mount event.
609 // We can pass chromeos::MOUNT_ERROR_NONE even when authentication is failed
610 // or network is unreachable. These two errors will be handled later.
611 OnMountEvent(DiskMountManager::MOUNTING, chromeos::MOUNT_ERROR_NONE,
612 mount_info);
613 }
614
615 void FileManagerEventRouter::OnFileSystemBeingUnmounted() {
616 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
617
618 // Raise a mount event to notify the File Manager.
619 const std::string& drive_path = drive::util::GetDriveMountPointPathAsString();
620 DiskMountManager::MountPointInfo mount_info(
621 drive_path,
622 drive_path,
623 chromeos::MOUNT_TYPE_GOOGLE_DRIVE,
624 chromeos::disks::MOUNT_CONDITION_NONE);
625 OnMountEvent(DiskMountManager::UNMOUNTING, chromeos::MOUNT_ERROR_NONE,
626 mount_info);
627 }
628
629 void FileManagerEventRouter::OnRefreshTokenInvalid() {
630 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
631
632 // Raise a DriveConnectionStatusChanged event to notify the status offline.
633 scoped_ptr<extensions::Event> event(new extensions::Event(
634 extensions::event_names::kOnFileBrowserDriveConnectionStatusChanged,
635 scoped_ptr<ListValue>(new ListValue())));
636 extensions::ExtensionSystem::Get(profile_)->event_router()->
637 BroadcastEvent(event.Pass());
638 }
639
640 void FileManagerEventRouter::HandleFileWatchNotification(
641 const base::FilePath& local_path, bool got_error) {
642 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
643
644 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
645 if (iter == file_watchers_.end()) {
646 return;
647 }
648 DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
649 iter->second->extensions());
650 }
651
652 void FileManagerEventRouter::DispatchDirectoryChangeEvent(
653 const base::FilePath& virtual_path,
654 bool got_error,
655 const FileWatcherExtensions::ExtensionUsageRegistry& extensions) {
656 if (!profile_) {
657 NOTREACHED();
658 return;
659 }
660
661 for (FileWatcherExtensions::ExtensionUsageRegistry::const_iterator iter =
662 extensions.begin(); iter != extensions.end(); ++iter) {
663 GURL target_origin_url(extensions::Extension::GetBaseURLFromExtensionId(
664 iter->first));
665 GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url,
666 fileapi::kFileSystemTypeExternal);
667 GURL target_directory_url = GURL(base_url.spec() + virtual_path.value());
668 scoped_ptr<ListValue> args(new ListValue());
669 DictionaryValue* watch_info = new DictionaryValue();
670 args->Append(watch_info);
671 watch_info->SetString("directoryUrl", target_directory_url.spec());
672 watch_info->SetString("eventType",
673 got_error ? kPathWatchError : kPathChanged);
674
675 // TODO(mtomasz): Pass set of entries. http://crbug.com/157834
676 ListValue* watch_info_entries = new ListValue();
677 watch_info->Set("changedEntries", watch_info_entries);
678
679 scoped_ptr<extensions::Event> event(new extensions::Event(
680 extensions::event_names::kOnDirectoryChanged, args.Pass()));
681 extensions::ExtensionSystem::Get(profile_)->event_router()->
682 DispatchEventToExtension(iter->first, event.Pass());
683 }
684 }
685
686 void FileManagerEventRouter::DispatchMountEvent(
687 DiskMountManager::MountEvent event,
688 chromeos::MountError error_code,
689 const DiskMountManager::MountPointInfo& mount_info) {
690 scoped_ptr<ListValue> args(new ListValue());
691 DictionaryValue* mount_info_value = new DictionaryValue();
692 args->Append(mount_info_value);
693 mount_info_value->SetString("eventType",
694 event == DiskMountManager::MOUNTING ? "mount" : "unmount");
695 mount_info_value->SetString("status", MountErrorToString(error_code));
696 mount_info_value->SetString(
697 "mountType",
698 DiskMountManager::MountTypeToString(mount_info.mount_type));
699
700 // Add sourcePath to the event.
701 mount_info_value->SetString("sourcePath", mount_info.source_path);
702
703 base::FilePath relative_mount_path;
704
705 // If there were no error or some special conditions occurred, add mountPath
706 // to the event.
707 if (event == DiskMountManager::UNMOUNTING ||
708 error_code == chromeos::MOUNT_ERROR_NONE ||
709 mount_info.mount_condition) {
710 // Convert mount point path to relative path with the external file system
711 // exposed within File API.
712 if (util::ConvertFileToRelativeFileSystemPath(
713 profile_,
714 kFileBrowserDomain,
715 base::FilePath(mount_info.mount_path),
716 &relative_mount_path)) {
717 mount_info_value->SetString("mountPath",
718 "/" + relative_mount_path.value());
719 } else {
720 mount_info_value->SetString("status",
721 MountErrorToString(chromeos::MOUNT_ERROR_PATH_UNMOUNTED));
722 }
723 }
724
725 scoped_ptr<extensions::Event> extension_event(new extensions::Event(
726 extensions::event_names::kOnFileBrowserMountCompleted, args.Pass()));
727 extensions::ExtensionSystem::Get(profile_)->event_router()->
728 BroadcastEvent(extension_event.Pass());
729 }
730
731 void FileManagerEventRouter::ShowRemovableDeviceInFileManager(
732 const DiskMountManager::Disk& disk, const base::FilePath& mount_path) {
733 // Do not attempt to open File Manager while the login is in progress or
734 // the screen is locked.
735 if (chromeos::LoginDisplayHostImpl::default_host() ||
736 chromeos::ScreenLocker::default_screen_locker())
737 return;
738
739 // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
740 // cameras should have pictures located in the DCIM root directory.
741 const base::FilePath dcim_path = mount_path.Append(
742 FILE_PATH_LITERAL("DCIM"));
743
744 // If there is no DCIM folder or an external photo importer is not available,
745 // then launch Files.app.
746 DirectoryExistsOnUIThread(
747 dcim_path,
748 IsGooglePhotosInstalled(profile_) ?
749 base::Bind(&base::DoNothing) :
750 base::Bind(&util::ViewRemovableDrive, mount_path),
751 base::Bind(&util::ViewRemovableDrive, mount_path));
752 }
753
754 void FileManagerEventRouter::OnDiskAdded(
755 const DiskMountManager::Disk* disk) {
756 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
757
758 VLOG(1) << "Disk added: " << disk->device_path();
759 if (disk->device_path().empty()) {
760 VLOG(1) << "Empty system path for " << disk->device_path();
761 return;
762 }
763
764 // If disk is not mounted yet and it has media and there is no policy
765 // forbidding external storage, give it a try.
766 if (disk->mount_path().empty() && disk->has_media() &&
767 !profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
768 // Initiate disk mount operation. MountPath auto-detects the filesystem
769 // format if the second argument is empty. The third argument (mount label)
770 // is not used in a disk mount operation.
771 DiskMountManager::GetInstance()->MountPath(
772 disk->device_path(), std::string(), std::string(),
773 chromeos::MOUNT_TYPE_DEVICE);
774 } else {
775 // Either the disk was mounted or it has no media. In both cases we don't
776 // want the Scanning notification to persist.
777 notifications_->HideNotification(DesktopNotifications::DEVICE,
778 disk->system_path_prefix());
779 }
780 }
781
782 void FileManagerEventRouter::OnDiskRemoved(
783 const DiskMountManager::Disk* disk) {
784 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
785
786 VLOG(1) << "Disk removed: " << disk->device_path();
787
788 if (!disk->mount_path().empty()) {
789 DiskMountManager::GetInstance()->UnmountPath(
790 disk->mount_path(),
791 chromeos::UNMOUNT_OPTIONS_LAZY,
792 DiskMountManager::UnmountPathCallback());
793 }
794 }
795
796 void FileManagerEventRouter::OnDeviceAdded(
797 const std::string& device_path) {
798 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
799
800 VLOG(1) << "Device added : " << device_path;
801
802 // If the policy is set instead of showing the new device notification we show
803 // a notification that the operation is not permitted.
804 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
805 notifications_->ShowNotification(
806 DesktopNotifications::DEVICE_EXTERNAL_STORAGE_DISABLED,
807 device_path);
808 return;
809 }
810
811 notifications_->RegisterDevice(device_path);
812 notifications_->ShowNotificationDelayed(DesktopNotifications::DEVICE,
813 device_path,
814 base::TimeDelta::FromSeconds(5));
815 }
816
817 void FileManagerEventRouter::OnDeviceRemoved(
818 const std::string& device_path) {
819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
820
821 VLOG(1) << "Device removed : " << device_path;
822 notifications_->HideNotification(DesktopNotifications::DEVICE,
823 device_path);
824 notifications_->HideNotification(DesktopNotifications::DEVICE_FAIL,
825 device_path);
826 notifications_->UnregisterDevice(device_path);
827 }
828
829 void FileManagerEventRouter::OnDeviceScanned(
830 const std::string& device_path) {
831 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
832 VLOG(1) << "Device scanned : " << device_path;
833 }
834
835 void FileManagerEventRouter::OnFormatStarted(
836 const std::string& device_path, bool success) {
837 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
838
839 if (success) {
840 notifications_->ShowNotification(DesktopNotifications::FORMAT_START,
841 device_path);
842 } else {
843 notifications_->ShowNotification(
844 DesktopNotifications::FORMAT_START_FAIL, device_path);
845 }
846 }
847
848 void FileManagerEventRouter::OnFormatCompleted(
849 const std::string& device_path, bool success) {
850 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
851
852 if (success) {
853 notifications_->HideNotification(DesktopNotifications::FORMAT_START,
854 device_path);
855 notifications_->ShowNotification(DesktopNotifications::FORMAT_SUCCESS,
856 device_path);
857 // Hide it after a couple of seconds.
858 notifications_->HideNotificationDelayed(
859 DesktopNotifications::FORMAT_SUCCESS,
860 device_path,
861 base::TimeDelta::FromSeconds(4));
862 // MountPath auto-detects filesystem format if second argument is empty.
863 // The third argument (mount label) is not used in a disk mount operation.
864 DiskMountManager::GetInstance()->MountPath(device_path, std::string(),
865 std::string(),
866 chromeos::MOUNT_TYPE_DEVICE);
867 } else {
868 notifications_->HideNotification(DesktopNotifications::FORMAT_START,
869 device_path);
870 notifications_->ShowNotification(DesktopNotifications::FORMAT_FAIL,
871 device_path);
872 }
873 }
874
875 } // namespace file_manager
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698