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.
5 #include "chrome/browser/chromeos/extensions/file_manager/event_router.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/private_api_util.h"
20 #include "chrome/browser/chromeos/file_manager/app_id.h"
21 #include "chrome/browser/chromeos/file_manager/desktop_notifications.h"
22 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
23 #include "chrome/browser/chromeos/file_manager/open_util.h"
24 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
25 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
26 #include "chrome/browser/chromeos/login/screen_locker.h"
27 #include "chrome/browser/drive/drive_service_interface.h"
28 #include "chrome/browser/extensions/event_names.h"
29 #include "chrome/browser/extensions/event_router.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/extension_system.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/common/extensions/api/file_browser_private.h"
34 #include "chrome/common/pref_names.h"
35 #include "chromeos/login/login_state.h"
36 #include "chromeos/network/network_handler.h"
37 #include "chromeos/network/network_state_handler.h"
38 #include "content/public/browser/browser_thread.h"
39 #include "content/public/browser/notification_source.h"
40 #include "webkit/common/fileapi/file_system_types.h"
41 #include "webkit/common/fileapi/file_system_util.h"
43 using chromeos::disks::DiskMountManager
;
44 using chromeos::NetworkHandler
;
45 using content::BrowserThread
;
46 using drive::DriveIntegrationService
;
47 using drive::DriveIntegrationServiceFactory
;
49 namespace file_browser_private
= extensions::api::file_browser_private
;
51 namespace file_manager
{
54 const char kPathChanged
[] = "changed";
55 const char kPathWatchError
[] = "error";
57 // Used as a callback for FileSystem::MarkCacheFileAsUnmounted().
58 void OnMarkAsUnmounted(drive::FileError error
) {
61 void DirectoryExistsOnBlockingPool(const base::FilePath
& directory_path
,
62 const base::Closure
& success_callback
,
63 const base::Closure
& failure_callback
) {
64 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
66 if (base::DirectoryExists(directory_path
))
67 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, success_callback
);
69 BrowserThread::PostTask(BrowserThread::UI
, FROM_HERE
, failure_callback
);
72 void DirectoryExistsOnUIThread(const base::FilePath
& directory_path
,
73 const base::Closure
& success_callback
,
74 const base::Closure
& failure_callback
) {
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
77 content::BrowserThread::PostBlockingPoolTask(
79 base::Bind(&DirectoryExistsOnBlockingPool
,
85 // Creates a base::FilePathWatcher and starts watching at |watch_path| with
86 // |callback|. Returns NULL on failure.
87 base::FilePathWatcher
* CreateAndStartFilePathWatcher(
88 const base::FilePath
& watch_path
,
89 const base::FilePathWatcher::Callback
& callback
) {
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
91 DCHECK(!callback
.is_null());
93 base::FilePathWatcher
* watcher(new base::FilePathWatcher
);
94 if (!watcher
->Watch(watch_path
, false /* recursive */, callback
)) {
102 // Constants for the "transferState" field of onFileTransferUpdated event.
103 const char kFileTransferStateStarted
[] = "started";
104 const char kFileTransferStateInProgress
[] = "in_progress";
105 const char kFileTransferStateCompleted
[] = "completed";
106 const char kFileTransferStateFailed
[] = "failed";
108 // Frequency of sending onFileTransferUpdated.
109 const int64 kFileTransferEventFrequencyInMilliseconds
= 1000;
111 // Utility function to check if |job_info| is a file uploading job.
112 bool IsUploadJob(drive::JobType type
) {
113 return (type
== drive::TYPE_UPLOAD_NEW_FILE
||
114 type
== drive::TYPE_UPLOAD_EXISTING_FILE
);
117 // Utility function to check if |job_info| is a file downloading job.
118 bool IsDownloadJob(drive::JobType type
) {
119 return type
== drive::TYPE_DOWNLOAD_FILE
;
122 // Converts the job info to its JSON (Value) form.
123 scoped_ptr
<base::DictionaryValue
> JobInfoToDictionaryValue(
124 const std::string
& extension_id
,
125 const std::string
& job_status
,
126 const drive::JobInfo
& job_info
) {
127 DCHECK(IsActiveFileTransferJobInfo(job_info
));
129 scoped_ptr
<base::DictionaryValue
> result(new base::DictionaryValue
);
130 GURL url
= util::ConvertRelativeFilePathToFileSystemUrl(
131 job_info
.file_path
, extension_id
);
132 result
->SetString("fileUrl", url
.spec());
133 result
->SetString("transferState", job_status
);
134 result
->SetString("transferType",
135 IsUploadJob(job_info
.job_type
) ? "upload" : "download");
136 // JavaScript does not have 64-bit integers. Instead we use double, which
137 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
138 // in C++. Larger values are rounded.
139 result
->SetDouble("processed",
140 static_cast<double>(job_info
.num_completed_bytes
));
141 result
->SetDouble("total", static_cast<double>(job_info
.num_total_bytes
));
142 return result
.Pass();
145 // Checks for availability of the Google+ Photos app.
146 bool IsGooglePhotosInstalled(Profile
*profile
) {
147 ExtensionService
* service
=
148 extensions::ExtensionSystem::Get(profile
)->extension_service();
152 // Google+ Photos uses several ids for different channels. Therefore, all of
153 // them should be checked.
154 const std::string kGooglePlusPhotosIds
[] = {
155 "ebpbnabdhheoknfklmpddcdijjkmklkp", // G+ Photos staging
156 "efjnaogkjbogokcnohkmnjdojkikgobo", // G+ Photos prod
157 "ejegoaikibpmikoejfephaneibodccma" // G+ Photos dev
160 for (size_t i
= 0; i
< arraysize(kGooglePlusPhotosIds
); ++i
) {
161 if (service
->GetExtensionById(kGooglePlusPhotosIds
[i
],
162 false /* include_disable */) != NULL
)
169 // This method is temporarily introduced to make a change step by step.
170 // After mounting logic is moved to VolumeManager, this should be removed.
171 std::string
MountTypeToString(chromeos::MountType type
) {
173 case chromeos::MOUNT_TYPE_INVALID
:
175 case chromeos::MOUNT_TYPE_DEVICE
:
176 return util::VolumeTypeToStringEnum(VOLUME_TYPE_REMOVABLE_DISK_PARTITION
);
177 case chromeos::MOUNT_TYPE_ARCHIVE
:
178 return util::VolumeTypeToStringEnum(VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
);
179 case chromeos::MOUNT_TYPE_GOOGLE_DRIVE
:
180 return util::VolumeTypeToStringEnum(VOLUME_TYPE_GOOGLE_DRIVE
);
186 // Sends an event named |event_name| with arguments |event_args| to extensions.
187 void BroadcastEvent(Profile
* profile
,
188 const std::string
& event_name
,
189 scoped_ptr
<base::ListValue
> event_args
) {
190 extensions::ExtensionSystem::Get(profile
)->event_router()->
191 BroadcastEvent(make_scoped_ptr(
192 new extensions::Event(event_name
, event_args
.Pass())));
195 file_browser_private::MountCompletedEvent::Status
196 MountErrorToMountCompletedStatus(chromeos::MountError error
) {
197 using file_browser_private::MountCompletedEvent
;
200 case chromeos::MOUNT_ERROR_NONE
:
201 return MountCompletedEvent::STATUS_SUCCESS
;
202 case chromeos::MOUNT_ERROR_UNKNOWN
:
203 return MountCompletedEvent::STATUS_ERROR_UNKNOWN
;
204 case chromeos::MOUNT_ERROR_INTERNAL
:
205 return MountCompletedEvent::STATUS_ERROR_INTERNAL
;
206 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT
:
207 return MountCompletedEvent::STATUS_ERROR_INVALID_ARGUMENT
;
208 case chromeos::MOUNT_ERROR_INVALID_PATH
:
209 return MountCompletedEvent::STATUS_ERROR_INVALID_PATH
;
210 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED
:
211 return MountCompletedEvent::STATUS_ERROR_PATH_ALREADY_MOUNTED
;
212 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED
:
213 return MountCompletedEvent::STATUS_ERROR_PATH_NOT_MOUNTED
;
214 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED
:
215 return MountCompletedEvent::STATUS_ERROR_DIRECTORY_CREATION_FAILED
;
216 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS
:
217 return MountCompletedEvent::STATUS_ERROR_INVALID_MOUNT_OPTIONS
;
218 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS
:
219 return MountCompletedEvent::STATUS_ERROR_INVALID_UNMOUNT_OPTIONS
;
220 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS
:
221 return MountCompletedEvent::STATUS_ERROR_INSUFFICIENT_PERMISSIONS
;
222 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND
:
223 return MountCompletedEvent::STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND
;
224 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED
:
225 return MountCompletedEvent::STATUS_ERROR_MOUNT_PROGRAM_FAILED
;
226 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH
:
227 return MountCompletedEvent::STATUS_ERROR_INVALID_DEVICE_PATH
;
228 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM
:
229 return MountCompletedEvent::STATUS_ERROR_UNKNOWN_FILESYSTEM
;
230 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM
:
231 return MountCompletedEvent::STATUS_ERROR_UNSUPORTED_FILESYSTEM
;
232 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE
:
233 return MountCompletedEvent::STATUS_ERROR_INVALID_ARCHIVE
;
234 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED
:
235 return MountCompletedEvent::STATUS_ERROR_AUTHENTICATION
;
236 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED
:
237 return MountCompletedEvent::STATUS_ERROR_PATH_UNMOUNTED
;
240 return MountCompletedEvent::STATUS_NONE
;
243 file_browser_private::MountCompletedEvent::VolumeType
244 VolumeTypeToMountCompletedVolumeType(VolumeType volume_type
) {
245 using file_browser_private::MountCompletedEvent
;
247 switch (volume_type
) {
248 case VOLUME_TYPE_GOOGLE_DRIVE
:
249 return MountCompletedEvent::VOLUME_TYPE_DRIVE
;
250 case VOLUME_TYPE_DOWNLOADS_DIRECTORY
:
251 return MountCompletedEvent::VOLUME_TYPE_DOWNLOADS
;
252 case VOLUME_TYPE_REMOVABLE_DISK_PARTITION
:
253 return MountCompletedEvent::VOLUME_TYPE_REMOVABLE
;
254 case VOLUME_TYPE_MOUNTED_ARCHIVE_FILE
:
255 return MountCompletedEvent::VOLUME_TYPE_ARCHIVE
;
258 return MountCompletedEvent::VOLUME_TYPE_NONE
;
261 void BroadcastMountCompletedEvent(
263 file_browser_private::MountCompletedEvent::EventType event_type
,
264 chromeos::MountError error
,
265 const VolumeInfo
& volume_info
) {
266 file_browser_private::MountCompletedEvent event
;
267 event
.event_type
= event_type
;
268 event
.status
= MountErrorToMountCompletedStatus(error
);
269 event
.source_path
= volume_info
.source_path
.AsUTF8Unsafe();
270 event
.volume_type
= VolumeTypeToMountCompletedVolumeType(volume_info
.type
);
272 if (!volume_info
.mount_path
.empty()) {
273 // Convert mount point path to relative path with the external file system
274 // exposed within File API.
275 base::FilePath relative_mount_path
;
276 if (util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
277 profile
, kFileManagerAppId
, base::FilePath(volume_info
.mount_path
),
278 &relative_mount_path
)) {
279 event
.mount_path
.reset(
280 new std::string("/" + relative_mount_path
.AsUTF8Unsafe()));
282 event
.status
= file_browser_private::MountCompletedEvent::
283 STATUS_ERROR_PATH_UNMOUNTED
;
289 extensions::event_names::kOnFileBrowserMountCompleted
,
290 file_browser_private::OnMountCompleted::Create(event
));
293 file_browser_private::CopyProgressStatus::Type
294 CopyProgressTypeToCopyProgressStatusType(
295 fileapi::FileSystemOperation::CopyProgressType type
) {
296 using file_browser_private::CopyProgressStatus
;
299 case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY
:
300 return CopyProgressStatus::TYPE_BEGIN_ENTRY_COPY
;
301 case fileapi::FileSystemOperation::END_COPY_ENTRY
:
302 return CopyProgressStatus::TYPE_END_ENTRY_COPY
;
303 case fileapi::FileSystemOperation::PROGRESS
:
304 return CopyProgressStatus::TYPE_PROGRESS
;
307 return CopyProgressStatus::TYPE_NONE
;
312 // Pass dummy value to JobInfo's constructor for make it default constructible.
313 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
314 : job_info(drive::TYPE_DOWNLOAD_FILE
) {
317 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
318 const drive::JobInfo
& info
, const std::string
& status
)
319 : job_info(info
), status(status
) {
322 EventRouter::EventRouter(Profile
* profile
)
323 : notifications_(new DesktopNotifications(profile
)),
324 pref_change_registrar_(new PrefChangeRegistrar
),
326 weak_factory_(this) {
327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
330 EventRouter::~EventRouter() {
333 void EventRouter::Shutdown() {
334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
336 DLOG_IF(WARNING
, !file_watchers_
.empty())
337 << "Not all file watchers are "
338 << "removed. This can happen when Files.app is open during shutdown.";
339 STLDeleteValues(&file_watchers_
);
345 pref_change_registrar_
->RemoveAll();
347 if (NetworkHandler::IsInitialized()) {
348 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
352 DriveIntegrationService
* integration_service
=
353 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
355 if (integration_service
) {
356 integration_service
->file_system()->RemoveObserver(this);
357 integration_service
->drive_service()->RemoveObserver(this);
358 integration_service
->job_list()->RemoveObserver(this);
361 VolumeManager
* volume_manager
= VolumeManager::Get(profile_
);
363 volume_manager
->RemoveObserver(this);
368 void EventRouter::ObserveFileSystemEvents() {
373 if (!chromeos::LoginState::IsInitialized() ||
374 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
378 // VolumeManager's construction triggers DriveIntegrationService's
379 // construction, so it is necessary to call VolumeManager's Get before
380 // accessing DriveIntegrationService.
381 VolumeManager
* volume_manager
= VolumeManager::Get(profile_
);
383 volume_manager
->AddObserver(this);
385 DriveIntegrationService
* integration_service
=
386 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
388 if (integration_service
) {
389 integration_service
->drive_service()->AddObserver(this);
390 integration_service
->file_system()->AddObserver(this);
391 integration_service
->job_list()->AddObserver(this);
394 if (NetworkHandler::IsInitialized()) {
395 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
399 pref_change_registrar_
->Init(profile_
->GetPrefs());
400 base::Closure callback
=
401 base::Bind(&EventRouter::OnFileManagerPrefsChanged
,
402 weak_factory_
.GetWeakPtr());
403 pref_change_registrar_
->Add(prefs::kDisableDriveOverCellular
, callback
);
404 pref_change_registrar_
->Add(prefs::kDisableDriveHostedFiles
, callback
);
405 pref_change_registrar_
->Add(prefs::kDisableDrive
, callback
);
406 pref_change_registrar_
->Add(prefs::kUse24HourClock
, callback
);
409 // File watch setup routines.
410 void EventRouter::AddFileWatch(const base::FilePath
& local_path
,
411 const base::FilePath
& virtual_path
,
412 const std::string
& extension_id
,
413 const BoolCallback
& callback
) {
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
415 DCHECK(!callback
.is_null());
417 base::FilePath watch_path
= local_path
;
418 bool is_on_drive
= drive::util::IsUnderDriveMountPoint(watch_path
);
419 // Tweak watch path for remote sources - we need to drop leading /special
420 // directory from there in order to be able to pair these events with
421 // their change notifications.
423 watch_path
= drive::util::ExtractDrivePath(watch_path
);
425 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
426 if (iter
== file_watchers_
.end()) {
427 scoped_ptr
<FileWatcher
> watcher(new FileWatcher(virtual_path
));
428 watcher
->AddExtension(extension_id
);
431 // For Drive, file watching is done via OnDirectoryChanged().
432 base::MessageLoopProxy::current()->PostTask(FROM_HERE
,
433 base::Bind(callback
, true));
435 // For local files, start watching using FileWatcher.
436 watcher
->WatchLocalFile(
438 base::Bind(&EventRouter::HandleFileWatchNotification
,
439 weak_factory_
.GetWeakPtr()),
443 file_watchers_
[watch_path
] = watcher
.release();
445 iter
->second
->AddExtension(extension_id
);
446 base::MessageLoopProxy::current()->PostTask(FROM_HERE
,
447 base::Bind(callback
, true));
451 void EventRouter::RemoveFileWatch(const base::FilePath
& local_path
,
452 const std::string
& extension_id
) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
455 base::FilePath watch_path
= local_path
;
456 // Tweak watch path for remote sources - we need to drop leading /special
457 // directory from there in order to be able to pair these events with
458 // their change notifications.
459 if (drive::util::IsUnderDriveMountPoint(watch_path
)) {
460 watch_path
= drive::util::ExtractDrivePath(watch_path
);
462 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
463 if (iter
== file_watchers_
.end())
465 // Remove the watcher if |watch_path| is no longer watched by any extensions.
466 iter
->second
->RemoveExtension(extension_id
);
467 if (iter
->second
->GetExtensionIds().empty()) {
469 file_watchers_
.erase(iter
);
473 void EventRouter::OnCopyCompleted(
474 int copy_id
, const GURL
& url
, base::PlatformFileError error
) {
475 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
477 file_browser_private::CopyProgressStatus status
;
478 if (error
== base::PLATFORM_FILE_OK
) {
479 // Send success event.
480 status
.type
= file_browser_private::CopyProgressStatus::TYPE_SUCCESS
;
481 status
.url
.reset(new std::string(url
.spec()));
484 status
.type
= file_browser_private::CopyProgressStatus::TYPE_ERROR
;
486 new int(fileapi::PlatformFileErrorToWebFileError(error
)));
491 extensions::event_names::kOnFileBrowserCopyProgress
,
492 file_browser_private::OnCopyProgress::Create(copy_id
, status
));
495 void EventRouter::OnCopyProgress(
497 fileapi::FileSystemOperation::CopyProgressType type
,
500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
502 file_browser_private::CopyProgressStatus status
;
503 status
.type
= CopyProgressTypeToCopyProgressStatusType(type
);
504 status
.url
.reset(new std::string(url
.spec()));
505 if (type
== fileapi::FileSystemOperation::PROGRESS
)
506 status
.size
.reset(new double(size
));
510 extensions::event_names::kOnFileBrowserCopyProgress
,
511 file_browser_private::OnCopyProgress::Create(copy_id
, status
));
514 void EventRouter::NetworkManagerChanged() {
516 !extensions::ExtensionSystem::Get(profile_
)->event_router()) {
523 extensions::event_names::kOnFileBrowserDriveConnectionStatusChanged
,
524 make_scoped_ptr(new ListValue
));
527 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState
* network
) {
528 NetworkManagerChanged();
531 void EventRouter::OnFileManagerPrefsChanged() {
533 !extensions::ExtensionSystem::Get(profile_
)->event_router()) {
540 extensions::event_names::kOnFileBrowserPreferencesChanged
,
541 make_scoped_ptr(new ListValue
));
544 void EventRouter::OnJobAdded(const drive::JobInfo
& job_info
) {
545 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
546 OnJobUpdated(job_info
);
549 void EventRouter::OnJobUpdated(const drive::JobInfo
& job_info
) {
550 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
551 if (!drive::IsActiveFileTransferJobInfo(job_info
))
554 bool is_new_job
= (drive_jobs_
.find(job_info
.job_id
) == drive_jobs_
.end());
556 // Replace with the latest job info.
557 drive_jobs_
[job_info
.job_id
] = DriveJobInfoWithStatus(
559 is_new_job
? kFileTransferStateStarted
: kFileTransferStateInProgress
);
561 // Fire event if needed.
562 bool always
= is_new_job
;
563 SendDriveFileTransferEvent(always
);
566 void EventRouter::OnJobDone(const drive::JobInfo
& job_info
,
567 drive::FileError error
) {
568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
569 if (!drive::IsActiveFileTransferJobInfo(job_info
))
572 // Replace with the latest job info.
573 drive_jobs_
[job_info
.job_id
] = DriveJobInfoWithStatus(
575 error
== drive::FILE_ERROR_OK
? kFileTransferStateCompleted
576 : kFileTransferStateFailed
);
578 // Fire event if needed.
580 SendDriveFileTransferEvent(always
);
582 // Forget about the job.
583 drive_jobs_
.erase(job_info
.job_id
);
586 void EventRouter::SendDriveFileTransferEvent(bool always
) {
587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
589 const base::Time now
= base::Time::Now();
591 // When |always| flag is not set, we don't send the event until certain
592 // amount of time passes after the previous one. This is to avoid
593 // flooding the IPC between extensions by many onFileTransferUpdated events.
595 const int64 delta
= (now
- last_file_transfer_event_
).InMilliseconds();
596 // delta < 0 may rarely happen if system clock is synced and rewinded.
597 // To be conservative, we don't skip in that case.
598 if (0 <= delta
&& delta
< kFileTransferEventFrequencyInMilliseconds
)
602 // Convert the current |drive_jobs_| to a JSON value.
603 scoped_ptr
<base::ListValue
> event_list(new base::ListValue
);
604 for (std::map
<drive::JobID
, DriveJobInfoWithStatus
>::iterator
605 iter
= drive_jobs_
.begin(); iter
!= drive_jobs_
.end(); ++iter
) {
607 scoped_ptr
<base::DictionaryValue
> job_info_dict(
608 JobInfoToDictionaryValue(kFileManagerAppId
,
610 iter
->second
.job_info
));
611 event_list
->Append(job_info_dict
.release());
614 scoped_ptr
<ListValue
> args(new ListValue());
615 args
->Append(event_list
.release());
616 scoped_ptr
<extensions::Event
> event(new extensions::Event(
617 extensions::event_names::kOnFileTransfersUpdated
, args
.Pass()));
618 extensions::ExtensionSystem::Get(profile_
)->event_router()->
619 DispatchEventToExtension(kFileManagerAppId
, event
.Pass());
621 last_file_transfer_event_
= now
;
624 void EventRouter::OnDirectoryChanged(const base::FilePath
& directory_path
) {
625 HandleFileWatchNotification(directory_path
, false);
628 void EventRouter::OnRefreshTokenInvalid() {
629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
631 // Raise a DriveConnectionStatusChanged event to notify the status offline.
634 extensions::event_names::kOnFileBrowserDriveConnectionStatusChanged
,
635 make_scoped_ptr(new ListValue
));
638 void EventRouter::HandleFileWatchNotification(const base::FilePath
& local_path
,
640 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
642 WatcherMap::const_iterator iter
= file_watchers_
.find(local_path
);
643 if (iter
== file_watchers_
.end()) {
646 DispatchDirectoryChangeEvent(iter
->second
->virtual_path(), got_error
,
647 iter
->second
->GetExtensionIds());
650 void EventRouter::DispatchDirectoryChangeEvent(
651 const base::FilePath
& virtual_path
,
653 const std::vector
<std::string
>& extension_ids
) {
659 for (size_t i
= 0; i
< extension_ids
.size(); ++i
) {
660 const std::string
& extension_id
= extension_ids
[i
];
662 GURL
target_origin_url(extensions::Extension::GetBaseURLFromExtensionId(
664 GURL base_url
= fileapi::GetFileSystemRootURI(
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
);
675 scoped_ptr
<extensions::Event
> event(new extensions::Event(
676 extensions::event_names::kOnDirectoryChanged
, args
.Pass()));
677 extensions::ExtensionSystem::Get(profile_
)->event_router()->
678 DispatchEventToExtension(extension_id
, event
.Pass());
682 void EventRouter::ShowRemovableDeviceInFileManager(
683 const base::FilePath
& mount_path
) {
684 // Do not attempt to open File Manager while the login is in progress or
685 // the screen is locked.
686 if (chromeos::LoginDisplayHostImpl::default_host() ||
687 chromeos::ScreenLocker::default_screen_locker())
690 // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
691 // cameras should have pictures located in the DCIM root directory.
692 const base::FilePath dcim_path
= mount_path
.Append(
693 FILE_PATH_LITERAL("DCIM"));
695 // If there is no DCIM folder or an external photo importer is not available,
696 // then launch Files.app.
697 DirectoryExistsOnUIThread(
699 IsGooglePhotosInstalled(profile_
) ?
700 base::Bind(&base::DoNothing
) :
701 base::Bind(&util::OpenRemovableDrive
, mount_path
),
702 base::Bind(&util::OpenRemovableDrive
, mount_path
));
705 void EventRouter::OnDiskAdded(
706 const DiskMountManager::Disk
& disk
, bool mounting
) {
707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
710 // If the disk is not being mounted, we don't want the Scanning
711 // notification to persist.
712 notifications_
->HideNotification(DesktopNotifications::DEVICE
,
713 disk
.system_path_prefix());
717 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk
& disk
) {
718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
722 void EventRouter::OnDeviceAdded(const std::string
& device_path
) {
723 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
725 // If the policy is set instead of showing the new device notification,
726 // we show a notification that the operation is not permitted.
727 if (profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
)) {
728 notifications_
->ShowNotification(
729 DesktopNotifications::DEVICE_EXTERNAL_STORAGE_DISABLED
,
734 notifications_
->RegisterDevice(device_path
);
735 notifications_
->ShowNotificationDelayed(DesktopNotifications::DEVICE
,
737 base::TimeDelta::FromSeconds(5));
740 void EventRouter::OnDeviceRemoved(const std::string
& device_path
) {
741 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
743 notifications_
->HideNotification(DesktopNotifications::DEVICE
,
745 notifications_
->HideNotification(DesktopNotifications::DEVICE_FAIL
,
747 notifications_
->UnregisterDevice(device_path
);
750 void EventRouter::OnVolumeMounted(chromeos::MountError error_code
,
751 const VolumeInfo
& volume_info
,
752 bool is_remounting
) {
753 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
754 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
755 // happen at shutdown. This should be removed after removing Drive mounting
756 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
757 // the only path to come here after Shutdown is called).
761 BroadcastMountCompletedEvent(
763 file_browser_private::MountCompletedEvent::EVENT_TYPE_MOUNT
,
764 error_code
, volume_info
);
766 if (volume_info
.type
== VOLUME_TYPE_REMOVABLE_DISK_PARTITION
&&
768 notifications_
->ManageNotificationsOnMountCompleted(
769 volume_info
.system_path_prefix
.AsUTF8Unsafe(),
770 volume_info
.drive_label
,
771 volume_info
.is_parent
,
772 error_code
== chromeos::MOUNT_ERROR_NONE
,
773 error_code
== chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM
);
775 // If a new device was mounted, a new File manager window may need to be
777 if (error_code
== chromeos::MOUNT_ERROR_NONE
)
778 ShowRemovableDeviceInFileManager(volume_info
.mount_path
);
782 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code
,
783 const VolumeInfo
& volume_info
) {
784 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
785 BroadcastMountCompletedEvent(
787 file_browser_private::MountCompletedEvent::EVENT_TYPE_UNMOUNT
,
788 error_code
, volume_info
);
791 void EventRouter::OnFormatStarted(const std::string
& device_path
,
793 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
796 notifications_
->ShowNotification(DesktopNotifications::FORMAT_START
,
799 notifications_
->ShowNotification(
800 DesktopNotifications::FORMAT_START_FAIL
, device_path
);
804 void EventRouter::OnFormatCompleted(const std::string
& device_path
,
806 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
809 notifications_
->HideNotification(DesktopNotifications::FORMAT_START
,
811 notifications_
->ShowNotification(DesktopNotifications::FORMAT_SUCCESS
,
813 // Hide it after a couple of seconds.
814 notifications_
->HideNotificationDelayed(
815 DesktopNotifications::FORMAT_SUCCESS
,
817 base::TimeDelta::FromSeconds(4));
819 notifications_
->HideNotification(DesktopNotifications::FORMAT_START
,
821 notifications_
->ShowNotification(DesktopNotifications::FORMAT_FAIL
,
826 } // namespace file_manager