1 // Copyright 2014 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/command_line.h"
9 #include "base/files/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/prefs/pref_change_registrar.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/values.h"
17 #include "chrome/browser/app_mode/app_mode_utils.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
20 #include "chrome/browser/chromeos/drive/file_change.h"
21 #include "chrome/browser/chromeos/drive/file_system_interface.h"
22 #include "chrome/browser/chromeos/drive/file_system_util.h"
23 #include "chrome/browser/chromeos/extensions/file_manager/device_event_router.h"
24 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
25 #include "chrome/browser/chromeos/file_manager/app_id.h"
26 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
27 #include "chrome/browser/chromeos/file_manager/open_util.h"
28 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
29 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
30 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
31 #include "chrome/browser/drive/drive_service_interface.h"
32 #include "chrome/browser/extensions/extension_service.h"
33 #include "chrome/browser/extensions/extension_util.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/profiles/profile_manager.h"
36 #include "chrome/common/chrome_switches.h"
37 #include "chrome/common/pref_names.h"
38 #include "chromeos/dbus/dbus_thread_manager.h"
39 #include "chromeos/login/login_state.h"
40 #include "chromeos/network/network_handler.h"
41 #include "chromeos/network/network_state_handler.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/render_process_host.h"
44 #include "content/public/browser/storage_partition.h"
45 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_host.h"
47 #include "extensions/browser/extension_prefs.h"
48 #include "storage/common/fileapi/file_system_types.h"
49 #include "storage/common/fileapi/file_system_util.h"
51 using chromeos::disks::DiskMountManager
;
52 using chromeos::NetworkHandler
;
53 using content::BrowserThread
;
54 using drive::DriveIntegrationService
;
55 using drive::DriveIntegrationServiceFactory
;
56 using file_manager::util::EntryDefinition
;
57 using file_manager::util::FileDefinition
;
59 namespace file_manager_private
= extensions::api::file_manager_private
;
61 namespace file_manager
{
63 // Constants for the "transferState" field of onFileTransferUpdated event.
64 const char kFileTransferStateAdded
[] = "added";
65 const char kFileTransferStateStarted
[] = "started";
66 const char kFileTransferStateInProgress
[] = "in_progress";
67 const char kFileTransferStateCompleted
[] = "completed";
68 const char kFileTransferStateFailed
[] = "failed";
70 // Frequency of sending onFileTransferUpdated.
71 const int64 kProgressEventFrequencyInMilliseconds
= 1000;
73 // Maximim size of detailed change info on directory change event. If the size
74 // exceeds the maximum size, the detailed info is omitted and the force refresh
76 const size_t kDirectoryChangeEventMaxDetailInfoSize
= 1000;
78 // This time(millisecond) is used for confirm following event exists.
79 const int64 kFileTransferEventDelayTimeInMilliseconds
= 300;
81 // Utility function to check if |job_info| is a file uploading job.
82 bool IsUploadJob(drive::JobType type
) {
83 return (type
== drive::TYPE_UPLOAD_NEW_FILE
||
84 type
== drive::TYPE_UPLOAD_EXISTING_FILE
);
87 size_t CountActiveFileTransferJobInfo(
88 const std::vector
<drive::JobInfo
>& job_info_list
) {
89 size_t num_active_file_transfer_job_info
= 0;
90 for (size_t i
= 0; i
< job_info_list
.size(); ++i
) {
91 if (IsActiveFileTransferJobInfo(job_info_list
[i
]))
92 ++num_active_file_transfer_job_info
;
94 return num_active_file_transfer_job_info
;
97 // Converts the job info to a IDL generated type.
98 void JobInfoToTransferStatus(
100 const std::string
& extension_id
,
101 const std::string
& job_status
,
102 const drive::JobInfo
& job_info
,
103 file_manager_private::FileTransferStatus
* status
) {
104 DCHECK(IsActiveFileTransferJobInfo(job_info
));
106 scoped_ptr
<base::DictionaryValue
> result(new base::DictionaryValue
);
107 GURL url
= util::ConvertDrivePathToFileSystemUrl(
108 profile
, job_info
.file_path
, extension_id
);
109 status
->file_url
= url
.spec();
110 status
->transfer_state
= file_manager_private::ParseTransferState(job_status
);
111 status
->transfer_type
=
112 IsUploadJob(job_info
.job_type
) ?
113 file_manager_private::TRANSFER_TYPE_UPLOAD
:
114 file_manager_private::TRANSFER_TYPE_DOWNLOAD
;
115 DriveIntegrationService
* const integration_service
=
116 DriveIntegrationServiceFactory::FindForProfile(profile
);
117 status
->num_total_jobs
= CountActiveFileTransferJobInfo(
118 integration_service
->job_list()->GetJobInfoList());
119 // JavaScript does not have 64-bit integers. Instead we use double, which
120 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
121 // in C++. Larger values are rounded.
122 status
->processed
.reset(
123 new double(static_cast<double>(job_info
.num_completed_bytes
)));
125 new double(static_cast<double>(job_info
.num_total_bytes
)));
128 // Checks if the Recovery Tool is running. This is a temporary solution.
129 // TODO(mtomasz): Replace with crbug.com/341902 solution.
130 bool IsRecoveryToolRunning(Profile
* profile
) {
131 extensions::ExtensionPrefs
* extension_prefs
=
132 extensions::ExtensionPrefs::Get(profile
);
133 if (!extension_prefs
)
136 const std::string kRecoveryToolIds
[] = {
137 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging
138 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
141 for (size_t i
= 0; i
< arraysize(kRecoveryToolIds
); ++i
) {
142 const std::string extension_id
= kRecoveryToolIds
[i
];
143 if (extension_prefs
->IsExtensionRunning(extension_id
))
150 // Sends an event named |event_name| with arguments |event_args| to extensions.
151 void BroadcastEvent(Profile
* profile
,
152 const std::string
& event_name
,
153 scoped_ptr
<base::ListValue
> event_args
) {
154 extensions::EventRouter::Get(profile
)->BroadcastEvent(
155 make_scoped_ptr(new extensions::Event(event_name
, event_args
.Pass())));
158 // Sends an event named |event_name| with arguments |event_args| to an extension
159 // of |extention_id|.
160 void DispatchEventToExtension(Profile
* profile
,
161 const std::string
& extension_id
,
162 const std::string
& event_name
,
163 scoped_ptr
<base::ListValue
> event_args
) {
164 extensions::EventRouter::Get(profile
)->DispatchEventToExtension(
166 make_scoped_ptr(new extensions::Event(event_name
, event_args
.Pass())));
169 file_manager_private::MountCompletedStatus
170 MountErrorToMountCompletedStatus(chromeos::MountError error
) {
172 case chromeos::MOUNT_ERROR_NONE
:
173 return file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS
;
174 case chromeos::MOUNT_ERROR_UNKNOWN
:
175 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN
;
176 case chromeos::MOUNT_ERROR_INTERNAL
:
177 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL
;
178 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT
:
179 return file_manager_private::
180 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT
;
181 case chromeos::MOUNT_ERROR_INVALID_PATH
:
182 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH
;
183 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED
:
184 return file_manager_private::
185 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED
;
186 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED
:
187 return file_manager_private::
188 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED
;
189 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED
:
190 return file_manager_private
191 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED
;
192 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS
:
193 return file_manager_private
194 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS
;
195 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS
:
196 return file_manager_private::
197 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS
;
198 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS
:
199 return file_manager_private::
200 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS
;
201 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND
:
202 return file_manager_private::
203 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND
;
204 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED
:
205 return file_manager_private::
206 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED
;
207 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH
:
208 return file_manager_private::
209 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH
;
210 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM
:
211 return file_manager_private::
212 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM
;
213 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM
:
214 return file_manager_private::
215 MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM
;
216 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE
:
217 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE
;
218 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED
:
219 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION
;
220 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED
:
221 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED
;
224 return file_manager_private::MOUNT_COMPLETED_STATUS_NONE
;
227 file_manager_private::CopyProgressStatusType
228 CopyProgressTypeToCopyProgressStatusType(
229 storage::FileSystemOperation::CopyProgressType type
) {
231 case storage::FileSystemOperation::BEGIN_COPY_ENTRY
:
232 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY
;
233 case storage::FileSystemOperation::END_COPY_ENTRY
:
234 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY
;
235 case storage::FileSystemOperation::PROGRESS
:
236 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS
;
239 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_NONE
;
242 file_manager_private::ChangeType
ConvertChangeTypeFromDriveToApi(
243 drive::FileChange::ChangeType type
) {
245 case drive::FileChange::ADD_OR_UPDATE
:
246 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE
;
247 case drive::FileChange::DELETE
:
248 return file_manager_private::CHANGE_TYPE_DELETE
;
251 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE
;
254 std::string
FileErrorToErrorName(base::File::Error error_code
) {
255 namespace js
= extensions::api::file_manager_private
;
256 switch (error_code
) {
257 case base::File::FILE_ERROR_NOT_FOUND
:
258 return "NotFoundError";
259 case base::File::FILE_ERROR_INVALID_OPERATION
:
260 case base::File::FILE_ERROR_EXISTS
:
261 case base::File::FILE_ERROR_NOT_EMPTY
:
262 return "InvalidModificationError";
263 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
264 case base::File::FILE_ERROR_NOT_A_FILE
:
265 return "TypeMismatchError";
266 case base::File::FILE_ERROR_ACCESS_DENIED
:
267 return "NoModificationAllowedError";
268 case base::File::FILE_ERROR_FAILED
:
269 return "InvalidStateError";
270 case base::File::FILE_ERROR_ABORT
:
272 case base::File::FILE_ERROR_SECURITY
:
273 return "SecurityError";
274 case base::File::FILE_ERROR_NO_SPACE
:
275 return "QuotaExceededError";
276 case base::File::FILE_ERROR_INVALID_URL
:
277 return "EncodingError";
279 return "InvalidModificationError";
283 void GrantAccessForAddedProfileToRunningInstance(Profile
* added_profile
,
284 Profile
* running_profile
) {
285 extensions::ExtensionHost
* const extension_host
=
286 extensions::ProcessManager::Get(running_profile
)
287 ->GetBackgroundHostForExtension(kFileManagerAppId
);
288 if (!extension_host
|| !extension_host
->render_process_host())
291 const int id
= extension_host
->render_process_host()->GetID();
292 file_manager::util::SetupProfileFileAccessPermissions(id
, added_profile
);
295 // Checks if we should send a progress event or not according to the
296 // |last_time| of sending an event. If |always| is true, the function always
297 // returns true. If the function returns true, the function also updates
299 bool ShouldSendProgressEvent(bool always
, base::Time
* last_time
) {
300 const base::Time now
= base::Time::Now();
301 const int64 delta
= (now
- *last_time
).InMilliseconds();
302 // delta < 0 may rarely happen if system clock is synced and rewinded.
303 // To be conservative, we don't skip in that case.
304 if (!always
&& 0 <= delta
&& delta
< kProgressEventFrequencyInMilliseconds
) {
312 // Obtains whether the Files.app should handle the volume or not.
313 bool ShouldShowNotificationForVolume(
315 const DeviceEventRouter
& device_event_router
,
316 const VolumeInfo
& volume_info
) {
317 if (volume_info
.type
!= VOLUME_TYPE_MTP
&&
318 volume_info
.type
!= VOLUME_TYPE_REMOVABLE_DISK_PARTITION
) {
322 if (device_event_router
.is_resuming() || device_event_router
.is_starting_up())
325 // Do not attempt to open File Manager while the login is in progress or
326 // the screen is locked or running in kiosk app mode and make sure the file
327 // manager is opened only for the active user.
328 if (chromeos::LoginDisplayHostImpl::default_host() ||
329 chromeos::ScreenLocker::default_screen_locker() ||
330 chrome::IsRunningInForcedAppMode() ||
331 profile
!= ProfileManager::GetActiveUserProfile()) {
335 // Do not pop-up the File Manager, if the recovery tool is running.
336 if (IsRecoveryToolRunning(profile
))
339 // If the disable-default-apps flag is on, Files.app is not opened
340 // automatically on device mount not to obstruct the manual test.
341 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
342 switches::kDisableDefaultApps
)) {
349 // Sub-part of the event router for handling device events.
350 class DeviceEventRouterImpl
: public DeviceEventRouter
{
352 explicit DeviceEventRouterImpl(Profile
* profile
) : profile_(profile
) {}
354 // DeviceEventRouter overrides.
355 void OnDeviceEvent(file_manager_private::DeviceEventType type
,
356 const std::string
& device_path
) override
{
357 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
359 file_manager_private::DeviceEvent event
;
361 event
.device_path
= device_path
;
363 BroadcastEvent(profile_
,
364 file_manager_private::OnDeviceChanged::kEventName
,
365 file_manager_private::OnDeviceChanged::Create(event
));
368 // DeviceEventRouter overrides.
369 bool IsExternalStorageDisabled() override
{
370 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
371 return profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
);
375 Profile
* const profile_
;
377 DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl
);
382 // Pass dummy value to JobInfo's constructor for make it default constructible.
383 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
384 : job_info(drive::TYPE_DOWNLOAD_FILE
) {
387 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
388 const drive::JobInfo
& info
, const std::string
& status
)
389 : job_info(info
), status(status
) {
392 EventRouter::EventRouter(Profile
* profile
)
393 : pref_change_registrar_(new PrefChangeRegistrar
),
395 device_event_router_(new DeviceEventRouterImpl(profile
)),
396 dispatch_directory_change_event_impl_(
397 base::Bind(&EventRouter::DispatchDirectoryChangeEventImpl
,
398 base::Unretained(this))),
399 weak_factory_(this) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
404 EventRouter::~EventRouter() {
407 void EventRouter::Shutdown() {
408 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
410 DLOG_IF(WARNING
, !file_watchers_
.empty())
411 << "Not all file watchers are "
412 << "removed. This can happen when Files.app is open during shutdown.";
413 STLDeleteValues(&file_watchers_
);
419 pref_change_registrar_
->RemoveAll();
421 if (NetworkHandler::IsInitialized()) {
422 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
426 DriveIntegrationService
* const integration_service
=
427 DriveIntegrationServiceFactory::FindForProfile(profile_
);
428 if (integration_service
) {
429 integration_service
->file_system()->RemoveObserver(this);
430 integration_service
->drive_service()->RemoveObserver(this);
431 integration_service
->job_list()->RemoveObserver(this);
434 VolumeManager
* const volume_manager
= VolumeManager::Get(profile_
);
435 if (volume_manager
) {
436 volume_manager
->RemoveObserver(this);
437 volume_manager
->RemoveObserver(device_event_router_
.get());
440 chromeos::PowerManagerClient
* const power_manager_client
=
441 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
442 power_manager_client
->RemoveObserver(device_event_router_
.get());
447 void EventRouter::ObserveEvents() {
452 if (!chromeos::LoginState::IsInitialized() ||
453 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
457 // Ignore device events for the first few seconds.
458 device_event_router_
->Startup();
460 // VolumeManager's construction triggers DriveIntegrationService's
461 // construction, so it is necessary to call VolumeManager's Get before
462 // accessing DriveIntegrationService.
463 VolumeManager
* const volume_manager
= VolumeManager::Get(profile_
);
464 if (volume_manager
) {
465 volume_manager
->AddObserver(this);
466 volume_manager
->AddObserver(device_event_router_
.get());
469 chromeos::PowerManagerClient
* const power_manager_client
=
470 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
471 power_manager_client
->AddObserver(device_event_router_
.get());
473 DriveIntegrationService
* const integration_service
=
474 DriveIntegrationServiceFactory::FindForProfile(profile_
);
475 if (integration_service
) {
476 integration_service
->drive_service()->AddObserver(this);
477 integration_service
->file_system()->AddObserver(this);
478 integration_service
->job_list()->AddObserver(this);
481 if (NetworkHandler::IsInitialized()) {
482 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
486 pref_change_registrar_
->Init(profile_
->GetPrefs());
487 base::Closure callback
=
488 base::Bind(&EventRouter::OnFileManagerPrefsChanged
,
489 weak_factory_
.GetWeakPtr());
490 pref_change_registrar_
->Add(prefs::kDisableDriveOverCellular
, callback
);
491 pref_change_registrar_
->Add(prefs::kDisableDriveHostedFiles
, callback
);
492 pref_change_registrar_
->Add(prefs::kDisableDrive
, callback
);
493 pref_change_registrar_
->Add(prefs::kUse24HourClock
, callback
);
495 notification_registrar_
.Add(this,
496 chrome::NOTIFICATION_PROFILE_ADDED
,
497 content::NotificationService::AllSources());
500 // File watch setup routines.
501 void EventRouter::AddFileWatch(const base::FilePath
& local_path
,
502 const base::FilePath
& virtual_path
,
503 const std::string
& extension_id
,
504 const BoolCallback
& callback
) {
505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
506 DCHECK(!callback
.is_null());
508 base::FilePath watch_path
= local_path
;
509 bool is_on_drive
= drive::util::IsUnderDriveMountPoint(watch_path
);
510 // Tweak watch path for remote sources - we need to drop leading /special
511 // directory from there in order to be able to pair these events with
512 // their change notifications.
514 watch_path
= drive::util::ExtractDrivePath(watch_path
);
516 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
517 if (iter
== file_watchers_
.end()) {
518 scoped_ptr
<FileWatcher
> watcher(new FileWatcher(virtual_path
));
519 watcher
->AddExtension(extension_id
);
522 // For Drive, file watching is done via OnDirectoryChanged().
523 base::MessageLoopProxy::current()->PostTask(FROM_HERE
,
524 base::Bind(callback
, true));
526 // For local files, start watching using FileWatcher.
527 watcher
->WatchLocalFile(
529 base::Bind(&EventRouter::HandleFileWatchNotification
,
530 weak_factory_
.GetWeakPtr(),
531 static_cast<drive::FileChange
*>(NULL
)),
535 file_watchers_
[watch_path
] = watcher
.release();
537 iter
->second
->AddExtension(extension_id
);
538 base::MessageLoopProxy::current()->PostTask(FROM_HERE
,
539 base::Bind(callback
, true));
543 void EventRouter::RemoveFileWatch(const base::FilePath
& local_path
,
544 const std::string
& extension_id
) {
545 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
547 base::FilePath watch_path
= local_path
;
548 // Tweak watch path for remote sources - we need to drop leading /special
549 // directory from there in order to be able to pair these events with
550 // their change notifications.
551 if (drive::util::IsUnderDriveMountPoint(watch_path
)) {
552 watch_path
= drive::util::ExtractDrivePath(watch_path
);
554 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
555 if (iter
== file_watchers_
.end())
557 // Remove the watcher if |watch_path| is no longer watched by any extensions.
558 iter
->second
->RemoveExtension(extension_id
);
559 if (iter
->second
->GetExtensionIds().empty()) {
561 file_watchers_
.erase(iter
);
565 void EventRouter::OnCopyCompleted(int copy_id
,
566 const GURL
& source_url
,
567 const GURL
& destination_url
,
568 base::File::Error error
) {
569 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
571 file_manager_private::CopyProgressStatus status
;
572 if (error
== base::File::FILE_OK
) {
573 // Send success event.
574 status
.type
= file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS
;
575 status
.source_url
.reset(new std::string(source_url
.spec()));
576 status
.destination_url
.reset(new std::string(destination_url
.spec()));
579 status
.type
= file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR
;
580 status
.error
.reset(new std::string(FileErrorToErrorName(error
)));
585 file_manager_private::OnCopyProgress::kEventName
,
586 file_manager_private::OnCopyProgress::Create(copy_id
, status
));
589 void EventRouter::OnCopyProgress(
591 storage::FileSystemOperation::CopyProgressType type
,
592 const GURL
& source_url
,
593 const GURL
& destination_url
,
595 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
597 file_manager_private::CopyProgressStatus status
;
598 status
.type
= CopyProgressTypeToCopyProgressStatusType(type
);
599 status
.source_url
.reset(new std::string(source_url
.spec()));
600 if (type
== storage::FileSystemOperation::END_COPY_ENTRY
)
601 status
.destination_url
.reset(new std::string(destination_url
.spec()));
602 if (type
== storage::FileSystemOperation::PROGRESS
)
603 status
.size
.reset(new double(size
));
605 // Should not skip events other than TYPE_PROGRESS.
607 status
.type
!= file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS
;
608 if (!ShouldSendProgressEvent(always
, &last_copy_progress_event_
))
613 file_manager_private::OnCopyProgress::kEventName
,
614 file_manager_private::OnCopyProgress::Create(copy_id
, status
));
617 void EventRouter::OnWatcherManagerNotification(
618 const storage::FileSystemURL
& file_system_url
,
619 const std::string
& extension_id
,
620 storage::WatcherManager::ChangeType
/* change_type */) {
621 std::vector
<std::string
> extension_ids
;
622 extension_ids
.push_back(extension_id
);
624 DispatchDirectoryChangeEvent(file_system_url
.virtual_path(), NULL
,
625 false /* error */, extension_ids
);
628 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState
* network
) {
629 if (!profile_
|| !extensions::EventRouter::Get(profile_
)) {
636 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
637 file_manager_private::OnDriveConnectionStatusChanged::Create());
640 void EventRouter::OnFileManagerPrefsChanged() {
641 if (!profile_
|| !extensions::EventRouter::Get(profile_
)) {
648 file_manager_private::OnPreferencesChanged::kEventName
,
649 file_manager_private::OnPreferencesChanged::Create());
652 void EventRouter::OnJobAdded(const drive::JobInfo
& job_info
) {
653 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
654 if (!drive::IsActiveFileTransferJobInfo(job_info
))
656 ScheduleDriveFileTransferEvent(
657 job_info
, kFileTransferStateAdded
, false /* immediate */);
660 void EventRouter::OnJobUpdated(const drive::JobInfo
& job_info
) {
661 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
662 if (!drive::IsActiveFileTransferJobInfo(job_info
))
665 bool is_new_job
= (drive_jobs_
.find(job_info
.job_id
) == drive_jobs_
.end());
667 const std::string status
=
668 is_new_job
? kFileTransferStateStarted
: kFileTransferStateInProgress
;
670 // Replace with the latest job info.
671 drive_jobs_
[job_info
.job_id
] = DriveJobInfoWithStatus(job_info
, status
);
673 ScheduleDriveFileTransferEvent(job_info
, status
, false /* immediate */);
676 void EventRouter::OnJobDone(const drive::JobInfo
& job_info
,
677 drive::FileError error
) {
678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
679 if (!drive::IsActiveFileTransferJobInfo(job_info
))
682 const std::string status
= error
== drive::FILE_ERROR_OK
683 ? kFileTransferStateCompleted
684 : kFileTransferStateFailed
;
686 ScheduleDriveFileTransferEvent(job_info
, status
, true /* immediate */);
688 // Forget about the job.
689 drive_jobs_
.erase(job_info
.job_id
);
692 void EventRouter::ScheduleDriveFileTransferEvent(const drive::JobInfo
& job_info
,
693 const std::string
& status
,
695 const bool no_pending_task
= !drive_job_info_for_scheduled_event_
;
696 // Update the latest event.
697 drive_job_info_for_scheduled_event_
.reset(
698 new DriveJobInfoWithStatus(job_info
, status
));
700 SendDriveFileTransferEvent();
701 } else if (no_pending_task
) {
702 const base::TimeDelta delay
= base::TimeDelta::FromMilliseconds(
703 kFileTransferEventDelayTimeInMilliseconds
);
704 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
706 base::Bind(&EventRouter::SendDriveFileTransferEvent
,
707 weak_factory_
.GetWeakPtr()),
712 void EventRouter::SendDriveFileTransferEvent() {
713 if (!drive_job_info_for_scheduled_event_
)
716 file_manager_private::FileTransferStatus status
;
717 JobInfoToTransferStatus(profile_
,
719 drive_job_info_for_scheduled_event_
->status
,
720 drive_job_info_for_scheduled_event_
->job_info
,
723 drive_job_info_for_scheduled_event_
.reset();
725 BroadcastEvent(profile_
,
726 file_manager_private::OnFileTransfersUpdated::kEventName
,
727 file_manager_private::OnFileTransfersUpdated::Create(status
));
730 void EventRouter::OnDirectoryChanged(const base::FilePath
& drive_path
) {
731 HandleFileWatchNotification(NULL
, drive_path
, false);
734 void EventRouter::OnFileChanged(const drive::FileChange
& changed_files
) {
735 // In this method, we convert changed_files to a map which can be handled by
736 // HandleFileWatchNotification.
739 // /a/b DIRECTORY:DELETE
741 // map[/a] = /a/b DIRECTORY:DELETE
742 // map[/a/b] = /a/b DIRECTORY:DELETE
744 // We used the key of map to match the watched directories of file watchers.
745 typedef std::map
<base::FilePath
, drive::FileChange
> FileChangeMap
;
746 typedef drive::FileChange::ChangeList::List FileChangeList
;
749 const drive::FileChange::Map
& changed_file_map
= changed_files
.map();
750 for (auto const& file_change_key_value
: changed_file_map
) {
751 // Check whether the FileChangeList contains directory deletion.
752 bool contains_directory_deletion
= false;
753 const FileChangeList list
= file_change_key_value
.second
.list();
754 for (drive::FileChange::Change
const& change
: list
) {
755 if (change
.IsDirectory() && change
.IsDelete()) {
756 contains_directory_deletion
= true;
761 const base::FilePath
& path
= file_change_key_value
.first
;
762 map
[path
.DirName()].Update(path
, file_change_key_value
.second
);
764 // For deletion of a directory, onFileChanged gets different changed_files.
765 // We solve the difference here.
767 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
768 // 1. /a/b DELETE:DIRECTORY
769 // 2. /a DELETE:DIRECTORY
771 // /a/b is watched, and /a is deleted from Files.app.
772 // 1. /a DELETE:DIRECTORY
773 if (contains_directory_deletion
) {
774 // Expand the deleted directory path with watched paths.
775 for (WatcherMap::const_iterator file_watchers_it
=
776 file_watchers_
.lower_bound(path
);
777 file_watchers_it
!= file_watchers_
.end(); ++file_watchers_it
) {
778 if (path
== file_watchers_it
->first
||
779 path
.IsParent(file_watchers_it
->first
)) {
780 map
[file_watchers_it
->first
].Update(
781 file_watchers_it
->first
,
782 drive::FileChange::FileType::FILE_TYPE_DIRECTORY
,
783 drive::FileChange::ChangeType::DELETE
);
789 for (auto const& file_change_key_value
: map
) {
790 HandleFileWatchNotification(&(file_change_key_value
.second
),
791 file_change_key_value
.first
, false);
795 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type
,
796 const base::FilePath
& drive_path
) {
797 file_manager_private::DriveSyncErrorEvent event
;
799 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION
:
801 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION
;
803 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE
:
805 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE
;
807 case drive::file_system::DRIVE_SYNC_ERROR_MISC
:
809 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC
;
812 event
.file_url
= util::ConvertDrivePathToFileSystemUrl(
813 profile_
, drive_path
, kFileManagerAppId
).spec();
816 file_manager_private::OnDriveSyncError::kEventName
,
817 file_manager_private::OnDriveSyncError::Create(event
));
820 void EventRouter::OnRefreshTokenInvalid() {
821 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
823 // Raise a DriveConnectionStatusChanged event to notify the status offline.
826 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
827 file_manager_private::OnDriveConnectionStatusChanged::Create());
830 void EventRouter::OnReadyToSendRequests() {
831 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
833 // Raise a DriveConnectionStatusChanged event to notify the status online.
836 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
837 file_manager_private::OnDriveConnectionStatusChanged::Create());
840 void EventRouter::HandleFileWatchNotification(const drive::FileChange
* list
,
841 const base::FilePath
& local_path
,
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
845 WatcherMap::const_iterator iter
= file_watchers_
.find(local_path
);
846 if (iter
== file_watchers_
.end()) {
850 if (list
&& list
->size() > kDirectoryChangeEventMaxDetailInfoSize
) {
851 // Removes the detailed information, if the list size is more than
852 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
853 // and processing it may cause more itme.
854 // This will be invoked full-refresh in Files.app.
858 DispatchDirectoryChangeEvent(iter
->second
->virtual_path(),
861 iter
->second
->GetExtensionIds());
864 void EventRouter::DispatchDirectoryChangeEvent(
865 const base::FilePath
& virtual_path
,
866 const drive::FileChange
* list
,
868 const std::vector
<std::string
>& extension_ids
) {
869 dispatch_directory_change_event_impl_
.Run(virtual_path
, list
, got_error
,
873 void EventRouter::DispatchDirectoryChangeEventImpl(
874 const base::FilePath
& virtual_path
,
875 const drive::FileChange
* list
,
877 const std::vector
<std::string
>& extension_ids
) {
882 linked_ptr
<drive::FileChange
> changes
;
884 changes
.reset(new drive::FileChange(*list
)); // Copy
886 for (size_t i
= 0; i
< extension_ids
.size(); ++i
) {
887 std::string
* extension_id
= new std::string(extension_ids
[i
]);
889 FileDefinition file_definition
;
890 file_definition
.virtual_path
= virtual_path
;
891 // TODO(mtomasz): Add support for watching files in File System Provider
893 file_definition
.is_directory
= true;
895 file_manager::util::ConvertFileDefinitionToEntryDefinition(
900 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition
,
901 weak_factory_
.GetWeakPtr(),
903 base::Owned(extension_id
),
908 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
909 const linked_ptr
<drive::FileChange
> list
,
910 const std::string
* extension_id
,
912 const EntryDefinition
& entry_definition
) {
913 // TODO(mtomasz): Add support for watching files in File System Provider API.
914 if (entry_definition
.error
!= base::File::FILE_OK
||
915 !entry_definition
.is_directory
) {
916 DVLOG(1) << "Unable to dispatch event because resolving the directory "
917 << "entry definition failed.";
921 file_manager_private::FileWatchEvent event
;
922 event
.event_type
= watcher_error
923 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
924 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED
;
926 // Detailed information is available.
928 event
.changed_files
.reset(
929 new std::vector
<linked_ptr
<file_manager_private::FileChange
> >);
931 if (list
->map().empty())
934 for (drive::FileChange::Map::const_iterator it
= list
->map().begin();
935 it
!= list
->map().end();
937 linked_ptr
<file_manager_private::FileChange
> change_list(
938 new file_manager_private::FileChange
);
940 GURL url
= util::ConvertDrivePathToFileSystemUrl(
941 profile_
, it
->first
, *extension_id
);
942 change_list
->url
= url
.spec();
944 for (drive::FileChange::ChangeList::List::const_iterator change
=
945 it
->second
.list().begin();
946 change
!= it
->second
.list().end();
948 change_list
->changes
.push_back(
949 ConvertChangeTypeFromDriveToApi(change
->change()));
952 event
.changed_files
->push_back(change_list
);
956 event
.entry
.additional_properties
.SetString(
957 "fileSystemName", entry_definition
.file_system_name
);
958 event
.entry
.additional_properties
.SetString(
959 "fileSystemRoot", entry_definition
.file_system_root_url
);
960 event
.entry
.additional_properties
.SetString(
961 "fileFullPath", "/" + entry_definition
.full_path
.value());
962 event
.entry
.additional_properties
.SetBoolean("fileIsDirectory",
963 entry_definition
.is_directory
);
965 DispatchEventToExtension(
968 file_manager_private::OnDirectoryChanged::kEventName
,
969 file_manager_private::OnDirectoryChanged::Create(event
));
972 void EventRouter::OnDiskAdded(
973 const DiskMountManager::Disk
& disk
, bool mounting
) {
974 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
978 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk
& disk
) {
979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
983 void EventRouter::OnDeviceAdded(const std::string
& device_path
) {
984 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
988 void EventRouter::OnDeviceRemoved(const std::string
& device_path
) {
989 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
993 void EventRouter::OnVolumeMounted(chromeos::MountError error_code
,
994 const VolumeInfo
& volume_info
) {
995 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
996 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
997 // happen at shutdown. This should be removed after removing Drive mounting
998 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
999 // the only path to come here after Shutdown is called).
1003 DispatchMountCompletedEvent(
1004 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT
,
1009 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code
,
1010 const VolumeInfo
& volume_info
) {
1011 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1012 DispatchMountCompletedEvent(
1013 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT
,
1018 void EventRouter::DispatchMountCompletedEvent(
1019 file_manager_private::MountCompletedEventType event_type
,
1020 chromeos::MountError error
,
1021 const VolumeInfo
& volume_info
) {
1022 // Build an event object.
1023 file_manager_private::MountCompletedEvent event
;
1024 event
.event_type
= event_type
;
1025 event
.status
= MountErrorToMountCompletedStatus(error
);
1026 util::VolumeInfoToVolumeMetadata(
1027 profile_
, volume_info
, &event
.volume_metadata
);
1028 event
.should_notify
= ShouldShowNotificationForVolume(
1029 profile_
, *device_event_router_
, volume_info
);
1030 BroadcastEvent(profile_
,
1031 file_manager_private::OnMountCompleted::kEventName
,
1032 file_manager_private::OnMountCompleted::Create(event
));
1035 void EventRouter::OnFormatStarted(const std::string
& device_path
,
1037 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1041 void EventRouter::OnFormatCompleted(const std::string
& device_path
,
1043 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
1047 void EventRouter::Observe(int type
,
1048 const content::NotificationSource
& source
,
1049 const content::NotificationDetails
& details
) {
1050 if (type
== chrome::NOTIFICATION_PROFILE_ADDED
) {
1051 Profile
* const added_profile
= content::Source
<Profile
>(source
).ptr();
1052 if (!added_profile
->IsOffTheRecord())
1053 GrantAccessForAddedProfileToRunningInstance(added_profile
, profile_
);
1057 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
1058 const DispatchDirectoryChangeEventImplCallback
& callback
) {
1059 dispatch_directory_change_event_impl_
= callback
;
1062 base::WeakPtr
<EventRouter
> EventRouter::GetWeakPtr() {
1063 return weak_factory_
.GetWeakPtr();
1066 } // namespace file_manager