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/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "base/values.h"
16 #include "chrome/browser/app_mode/app_mode_utils.h"
17 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
18 #include "chrome/browser/chromeos/drive/drive_pref_names.h"
19 #include "chrome/browser/chromeos/drive/file_change.h"
20 #include "chrome/browser/chromeos/drive/file_system_interface.h"
21 #include "chrome/browser/chromeos/drive/file_system_util.h"
22 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
23 #include "chrome/browser/chromeos/file_manager/app_id.h"
24 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
25 #include "chrome/browser/chromeos/file_manager/open_util.h"
26 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
27 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
28 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
29 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/extension_util.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/profiles/profile_manager.h"
34 #include "chrome/common/chrome_switches.h"
35 #include "chrome/common/pref_names.h"
36 #include "chromeos/dbus/dbus_thread_manager.h"
37 #include "chromeos/login/login_state.h"
38 #include "chromeos/network/network_handler.h"
39 #include "chromeos/network/network_state_handler.h"
40 #include "components/drive/service/drive_service_interface.h"
41 #include "content/public/browser/browser_thread.h"
42 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/storage_partition.h"
44 #include "extensions/browser/event_router.h"
45 #include "extensions/browser/extension_host.h"
46 #include "extensions/browser/extension_prefs.h"
47 #include "storage/common/fileapi/file_system_types.h"
48 #include "storage/common/fileapi/file_system_util.h"
50 using chromeos::disks::DiskMountManager
;
51 using chromeos::NetworkHandler
;
52 using content::BrowserThread
;
53 using drive::DriveIntegrationService
;
54 using drive::DriveIntegrationServiceFactory
;
55 using file_manager::util::EntryDefinition
;
56 using file_manager::util::FileDefinition
;
58 namespace file_manager_private
= extensions::api::file_manager_private
;
60 namespace file_manager
{
63 // Frequency of sending onFileTransferUpdated.
64 const int64 kProgressEventFrequencyInMilliseconds
= 1000;
66 // Maximim size of detailed change info on directory change event. If the size
67 // exceeds the maximum size, the detailed info is omitted and the force refresh
69 const size_t kDirectoryChangeEventMaxDetailInfoSize
= 1000;
71 // This time(millisecond) is used for confirm following event exists.
72 const int64 kFileTransferEventDelayTimeInMilliseconds
= 300;
74 // Checks if the Recovery Tool is running. This is a temporary solution.
75 // TODO(mtomasz): Replace with crbug.com/341902 solution.
76 bool IsRecoveryToolRunning(Profile
* profile
) {
77 extensions::ExtensionPrefs
* extension_prefs
=
78 extensions::ExtensionPrefs::Get(profile
);
82 const std::string kRecoveryToolIds
[] = {
83 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging
84 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
87 for (size_t i
= 0; i
< arraysize(kRecoveryToolIds
); ++i
) {
88 const std::string extension_id
= kRecoveryToolIds
[i
];
89 if (extension_prefs
->IsExtensionRunning(extension_id
))
96 // Sends an event named |event_name| with arguments |event_args| to extensions.
97 void BroadcastEvent(Profile
* profile
,
98 extensions::events::HistogramValue histogram_value
,
99 const std::string
& event_name
,
100 scoped_ptr
<base::ListValue
> event_args
) {
101 extensions::EventRouter::Get(profile
)->BroadcastEvent(make_scoped_ptr(
102 new extensions::Event(histogram_value
, event_name
, event_args
.Pass())));
105 // Sends an event named |event_name| with arguments |event_args| to an extension
106 // of |extention_id|.
107 void DispatchEventToExtension(
109 const std::string
& extension_id
,
110 extensions::events::HistogramValue histogram_value
,
111 const std::string
& event_name
,
112 scoped_ptr
<base::ListValue
> event_args
) {
113 extensions::EventRouter::Get(profile
)->DispatchEventToExtension(
114 extension_id
, make_scoped_ptr(new extensions::Event(
115 histogram_value
, event_name
, event_args
.Pass())));
118 file_manager_private::MountCompletedStatus
119 MountErrorToMountCompletedStatus(chromeos::MountError error
) {
121 case chromeos::MOUNT_ERROR_NONE
:
122 return file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS
;
123 case chromeos::MOUNT_ERROR_UNKNOWN
:
124 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN
;
125 case chromeos::MOUNT_ERROR_INTERNAL
:
126 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL
;
127 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT
:
128 return file_manager_private::
129 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT
;
130 case chromeos::MOUNT_ERROR_INVALID_PATH
:
131 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH
;
132 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED
:
133 return file_manager_private::
134 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED
;
135 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED
:
136 return file_manager_private::
137 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED
;
138 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED
:
139 return file_manager_private
140 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED
;
141 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS
:
142 return file_manager_private
143 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS
;
144 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS
:
145 return file_manager_private::
146 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS
;
147 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS
:
148 return file_manager_private::
149 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS
;
150 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND
:
151 return file_manager_private::
152 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND
;
153 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED
:
154 return file_manager_private::
155 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED
;
156 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH
:
157 return file_manager_private::
158 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH
;
159 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM
:
160 return file_manager_private::
161 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM
;
162 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM
:
163 return file_manager_private::
164 MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM
;
165 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE
:
166 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE
;
167 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED
:
168 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION
;
169 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED
:
170 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED
;
173 return file_manager_private::MOUNT_COMPLETED_STATUS_NONE
;
176 file_manager_private::CopyProgressStatusType
177 CopyProgressTypeToCopyProgressStatusType(
178 storage::FileSystemOperation::CopyProgressType type
) {
180 case storage::FileSystemOperation::BEGIN_COPY_ENTRY
:
181 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY
;
182 case storage::FileSystemOperation::END_COPY_ENTRY
:
183 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY
;
184 case storage::FileSystemOperation::PROGRESS
:
185 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS
;
186 case storage::FileSystemOperation::ERROR_COPY_ENTRY
:
187 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR
;
190 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_NONE
;
193 file_manager_private::ChangeType
ConvertChangeTypeFromDriveToApi(
194 drive::FileChange::ChangeType type
) {
196 case drive::FileChange::CHANGE_TYPE_ADD_OR_UPDATE
:
197 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE
;
198 case drive::FileChange::CHANGE_TYPE_DELETE
:
199 return file_manager_private::CHANGE_TYPE_DELETE
;
202 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE
;
205 std::string
FileErrorToErrorName(base::File::Error error_code
) {
206 namespace js
= extensions::api::file_manager_private
;
207 switch (error_code
) {
208 case base::File::FILE_ERROR_NOT_FOUND
:
209 return "NotFoundError";
210 case base::File::FILE_ERROR_INVALID_OPERATION
:
211 case base::File::FILE_ERROR_EXISTS
:
212 case base::File::FILE_ERROR_NOT_EMPTY
:
213 return "InvalidModificationError";
214 case base::File::FILE_ERROR_NOT_A_DIRECTORY
:
215 case base::File::FILE_ERROR_NOT_A_FILE
:
216 return "TypeMismatchError";
217 case base::File::FILE_ERROR_ACCESS_DENIED
:
218 return "NoModificationAllowedError";
219 case base::File::FILE_ERROR_FAILED
:
220 return "InvalidStateError";
221 case base::File::FILE_ERROR_ABORT
:
223 case base::File::FILE_ERROR_SECURITY
:
224 return "SecurityError";
225 case base::File::FILE_ERROR_NO_SPACE
:
226 return "QuotaExceededError";
227 case base::File::FILE_ERROR_INVALID_URL
:
228 return "EncodingError";
230 return "InvalidModificationError";
234 // Checks if we should send a progress event or not according to the
235 // |last_time| of sending an event. If |always| is true, the function always
236 // returns true. If the function returns true, the function also updates
238 bool ShouldSendProgressEvent(bool always
, base::Time
* last_time
) {
239 const base::Time now
= base::Time::Now();
240 const int64 delta
= (now
- *last_time
).InMilliseconds();
241 // delta < 0 may rarely happen if system clock is synced and rewinded.
242 // To be conservative, we don't skip in that case.
243 if (!always
&& 0 <= delta
&& delta
< kProgressEventFrequencyInMilliseconds
) {
251 // Obtains whether the Files.app should handle the volume or not.
252 bool ShouldShowNotificationForVolume(
254 const DeviceEventRouter
& device_event_router
,
255 const Volume
& volume
) {
256 if (volume
.type() != VOLUME_TYPE_MTP
&&
257 volume
.type() != VOLUME_TYPE_REMOVABLE_DISK_PARTITION
) {
261 if (device_event_router
.is_resuming() || device_event_router
.is_starting_up())
264 // Do not attempt to open File Manager while the login is in progress or
265 // the screen is locked or running in kiosk app mode and make sure the file
266 // manager is opened only for the active user.
267 if (chromeos::LoginDisplayHostImpl::default_host() ||
268 chromeos::ScreenLocker::default_screen_locker() ||
269 chrome::IsRunningInForcedAppMode() ||
270 profile
!= ProfileManager::GetActiveUserProfile()) {
274 // Do not pop-up the File Manager, if the recovery tool is running.
275 if (IsRecoveryToolRunning(profile
))
278 // If the disable-default-apps flag is on, Files.app is not opened
279 // automatically on device mount not to obstruct the manual test.
280 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
281 switches::kDisableDefaultApps
)) {
288 // Sub-part of the event router for handling device events.
289 class DeviceEventRouterImpl
: public DeviceEventRouter
{
291 explicit DeviceEventRouterImpl(Profile
* profile
) : profile_(profile
) {}
293 // DeviceEventRouter overrides.
294 void OnDeviceEvent(file_manager_private::DeviceEventType type
,
295 const std::string
& device_path
) override
{
296 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
298 file_manager_private::DeviceEvent event
;
300 event
.device_path
= device_path
;
302 BroadcastEvent(profile_
,
303 extensions::events::FILE_MANAGER_PRIVATE_ON_DEVICE_CHANGED
,
304 file_manager_private::OnDeviceChanged::kEventName
,
305 file_manager_private::OnDeviceChanged::Create(event
));
308 // DeviceEventRouter overrides.
309 bool IsExternalStorageDisabled() override
{
310 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
311 return profile_
->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled
);
315 Profile
* const profile_
;
317 DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl
);
320 class JobEventRouterImpl
: public JobEventRouter
{
322 explicit JobEventRouterImpl(Profile
* profile
)
323 : JobEventRouter(base::TimeDelta::FromMilliseconds(
324 kFileTransferEventDelayTimeInMilliseconds
)),
328 GURL
ConvertDrivePathToFileSystemUrl(const base::FilePath
& path
,
329 const std::string
& id
) const override
{
330 return file_manager::util::ConvertDrivePathToFileSystemUrl(profile_
, path
,
333 void BroadcastEvent(extensions::events::HistogramValue histogram_value
,
334 const std::string
& event_name
,
335 scoped_ptr
<base::ListValue
> event_args
) override
{
336 ::file_manager::BroadcastEvent(profile_
, histogram_value
, event_name
,
341 Profile
* const profile_
;
343 DISALLOW_COPY_AND_ASSIGN(JobEventRouterImpl
);
348 EventRouter::EventRouter(Profile
* profile
)
349 : pref_change_registrar_(new PrefChangeRegistrar
),
351 device_event_router_(new DeviceEventRouterImpl(profile
)),
352 job_event_router_(new JobEventRouterImpl(profile
)),
353 dispatch_directory_change_event_impl_(
354 base::Bind(&EventRouter::DispatchDirectoryChangeEventImpl
,
355 base::Unretained(this))),
356 weak_factory_(this) {
357 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
361 EventRouter::~EventRouter() {
364 void EventRouter::Shutdown() {
365 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
367 DLOG_IF(WARNING
, !file_watchers_
.empty())
368 << "Not all file watchers are "
369 << "removed. This can happen when Files.app is open during shutdown.";
370 STLDeleteValues(&file_watchers_
);
376 pref_change_registrar_
->RemoveAll();
378 if (NetworkHandler::IsInitialized()) {
379 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
383 DriveIntegrationService
* const integration_service
=
384 DriveIntegrationServiceFactory::FindForProfile(profile_
);
385 if (integration_service
) {
386 integration_service
->file_system()->RemoveObserver(this);
387 integration_service
->drive_service()->RemoveObserver(this);
388 integration_service
->job_list()->RemoveObserver(job_event_router_
.get());
391 VolumeManager
* const volume_manager
= VolumeManager::Get(profile_
);
392 if (volume_manager
) {
393 volume_manager
->RemoveObserver(this);
394 volume_manager
->RemoveObserver(device_event_router_
.get());
397 chromeos::PowerManagerClient
* const power_manager_client
=
398 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
399 power_manager_client
->RemoveObserver(device_event_router_
.get());
404 void EventRouter::ObserveEvents() {
409 if (!chromeos::LoginState::IsInitialized() ||
410 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
414 // Ignore device events for the first few seconds.
415 device_event_router_
->Startup();
417 // VolumeManager's construction triggers DriveIntegrationService's
418 // construction, so it is necessary to call VolumeManager's Get before
419 // accessing DriveIntegrationService.
420 VolumeManager
* const volume_manager
= VolumeManager::Get(profile_
);
421 if (volume_manager
) {
422 volume_manager
->AddObserver(this);
423 volume_manager
->AddObserver(device_event_router_
.get());
426 chromeos::PowerManagerClient
* const power_manager_client
=
427 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
428 power_manager_client
->AddObserver(device_event_router_
.get());
430 DriveIntegrationService
* const integration_service
=
431 DriveIntegrationServiceFactory::FindForProfile(profile_
);
432 if (integration_service
) {
433 integration_service
->drive_service()->AddObserver(this);
434 integration_service
->file_system()->AddObserver(this);
435 integration_service
->job_list()->AddObserver(job_event_router_
.get());
438 if (NetworkHandler::IsInitialized()) {
439 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
443 pref_change_registrar_
->Init(profile_
->GetPrefs());
444 base::Closure callback
=
445 base::Bind(&EventRouter::OnFileManagerPrefsChanged
,
446 weak_factory_
.GetWeakPtr());
447 pref_change_registrar_
->Add(drive::prefs::kDisableDriveOverCellular
,
449 pref_change_registrar_
->Add(drive::prefs::kDisableDriveHostedFiles
, callback
);
450 pref_change_registrar_
->Add(drive::prefs::kDisableDrive
, callback
);
451 pref_change_registrar_
->Add(prefs::kSearchSuggestEnabled
, callback
);
452 pref_change_registrar_
->Add(prefs::kUse24HourClock
, callback
);
455 // File watch setup routines.
456 void EventRouter::AddFileWatch(const base::FilePath
& local_path
,
457 const base::FilePath
& virtual_path
,
458 const std::string
& extension_id
,
459 const BoolCallback
& callback
) {
460 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
461 DCHECK(!callback
.is_null());
463 base::FilePath watch_path
= local_path
;
464 bool is_on_drive
= drive::util::IsUnderDriveMountPoint(watch_path
);
465 // Tweak watch path for remote sources - we need to drop leading /special
466 // directory from there in order to be able to pair these events with
467 // their change notifications.
469 watch_path
= drive::util::ExtractDrivePath(watch_path
);
471 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
472 if (iter
== file_watchers_
.end()) {
473 scoped_ptr
<FileWatcher
> watcher(new FileWatcher(virtual_path
));
474 watcher
->AddExtension(extension_id
);
477 // For Drive, file watching is done via OnDirectoryChanged().
478 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
479 base::Bind(callback
, true));
481 // For local files, start watching using FileWatcher.
482 watcher
->WatchLocalFile(
484 base::Bind(&EventRouter::HandleFileWatchNotification
,
485 weak_factory_
.GetWeakPtr(),
486 static_cast<drive::FileChange
*>(NULL
)),
490 file_watchers_
[watch_path
] = watcher
.release();
492 iter
->second
->AddExtension(extension_id
);
493 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
494 base::Bind(callback
, true));
498 void EventRouter::RemoveFileWatch(const base::FilePath
& local_path
,
499 const std::string
& extension_id
) {
500 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
502 base::FilePath watch_path
= local_path
;
503 // Tweak watch path for remote sources - we need to drop leading /special
504 // directory from there in order to be able to pair these events with
505 // their change notifications.
506 if (drive::util::IsUnderDriveMountPoint(watch_path
)) {
507 watch_path
= drive::util::ExtractDrivePath(watch_path
);
509 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
510 if (iter
== file_watchers_
.end())
512 // Remove the watcher if |watch_path| is no longer watched by any extensions.
513 iter
->second
->RemoveExtension(extension_id
);
514 if (iter
->second
->GetExtensionIds().empty()) {
516 file_watchers_
.erase(iter
);
520 void EventRouter::OnCopyCompleted(int copy_id
,
521 const GURL
& source_url
,
522 const GURL
& destination_url
,
523 base::File::Error error
) {
524 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
526 file_manager_private::CopyProgressStatus status
;
527 if (error
== base::File::FILE_OK
) {
528 // Send success event.
529 status
.type
= file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS
;
530 status
.source_url
.reset(new std::string(source_url
.spec()));
531 status
.destination_url
.reset(new std::string(destination_url
.spec()));
534 status
.type
= file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR
;
535 status
.error
.reset(new std::string(FileErrorToErrorName(error
)));
538 BroadcastEvent(profile_
,
539 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS
,
540 file_manager_private::OnCopyProgress::kEventName
,
541 file_manager_private::OnCopyProgress::Create(copy_id
, status
));
544 void EventRouter::OnCopyProgress(
546 storage::FileSystemOperation::CopyProgressType type
,
547 const GURL
& source_url
,
548 const GURL
& destination_url
,
550 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
552 file_manager_private::CopyProgressStatus status
;
553 status
.type
= CopyProgressTypeToCopyProgressStatusType(type
);
554 status
.source_url
.reset(new std::string(source_url
.spec()));
555 if (type
== storage::FileSystemOperation::END_COPY_ENTRY
||
556 type
== storage::FileSystemOperation::ERROR_COPY_ENTRY
)
557 status
.destination_url
.reset(new std::string(destination_url
.spec()));
558 if (type
== storage::FileSystemOperation::ERROR_COPY_ENTRY
)
560 new std::string(FileErrorToErrorName(base::File::FILE_ERROR_FAILED
)));
561 if (type
== storage::FileSystemOperation::PROGRESS
)
562 status
.size
.reset(new double(size
));
564 // Discard error progress since current JS code cannot handle this properly.
565 // TODO(yawano): Remove this after JS side is implemented correctly.
566 if (type
== storage::FileSystemOperation::ERROR_COPY_ENTRY
)
569 // Should not skip events other than TYPE_PROGRESS.
571 status
.type
!= file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS
;
572 if (!ShouldSendProgressEvent(always
, &last_copy_progress_event_
))
575 BroadcastEvent(profile_
,
576 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS
,
577 file_manager_private::OnCopyProgress::kEventName
,
578 file_manager_private::OnCopyProgress::Create(copy_id
, status
));
581 void EventRouter::OnWatcherManagerNotification(
582 const storage::FileSystemURL
& file_system_url
,
583 const std::string
& extension_id
,
584 storage::WatcherManager::ChangeType
/* change_type */) {
585 std::vector
<std::string
> extension_ids
;
586 extension_ids
.push_back(extension_id
);
588 DispatchDirectoryChangeEvent(file_system_url
.virtual_path(), NULL
,
589 false /* error */, extension_ids
);
592 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState
* network
) {
593 if (!profile_
|| !extensions::EventRouter::Get(profile_
)) {
599 profile_
, extensions::events::
600 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED
,
601 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
602 file_manager_private::OnDriveConnectionStatusChanged::Create());
605 void EventRouter::OnFileManagerPrefsChanged() {
606 if (!profile_
|| !extensions::EventRouter::Get(profile_
)) {
612 profile_
, extensions::events::FILE_MANAGER_PRIVATE_ON_PREFERENCES_CHANGED
,
613 file_manager_private::OnPreferencesChanged::kEventName
,
614 file_manager_private::OnPreferencesChanged::Create());
617 void EventRouter::OnDirectoryChanged(const base::FilePath
& drive_path
) {
618 HandleFileWatchNotification(NULL
, drive_path
, false);
621 void EventRouter::OnFileChanged(const drive::FileChange
& changed_files
) {
622 // In this method, we convert changed_files to a map which can be handled by
623 // HandleFileWatchNotification.
626 // /a/b DIRECTORY:DELETE
628 // map[/a] = /a/b DIRECTORY:DELETE
629 // map[/a/b] = /a/b DIRECTORY:DELETE
631 // We used the key of map to match the watched directories of file watchers.
632 typedef std::map
<base::FilePath
, drive::FileChange
> FileChangeMap
;
633 typedef drive::FileChange::ChangeList::List FileChangeList
;
636 const drive::FileChange::Map
& changed_file_map
= changed_files
.map();
637 for (auto const& file_change_key_value
: changed_file_map
) {
638 // Check whether the FileChangeList contains directory deletion.
639 bool contains_directory_deletion
= false;
640 const FileChangeList list
= file_change_key_value
.second
.list();
641 for (drive::FileChange::Change
const& change
: list
) {
642 if (change
.IsDirectory() && change
.IsDelete()) {
643 contains_directory_deletion
= true;
648 const base::FilePath
& path
= file_change_key_value
.first
;
649 map
[path
.DirName()].Update(path
, file_change_key_value
.second
);
651 // For deletion of a directory, onFileChanged gets different changed_files.
652 // We solve the difference here.
654 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
655 // 1. /a/b DELETE:DIRECTORY
656 // 2. /a DELETE:DIRECTORY
658 // /a/b is watched, and /a is deleted from Files.app.
659 // 1. /a DELETE:DIRECTORY
660 if (contains_directory_deletion
) {
661 // Expand the deleted directory path with watched paths.
662 for (WatcherMap::const_iterator file_watchers_it
=
663 file_watchers_
.lower_bound(path
);
664 file_watchers_it
!= file_watchers_
.end(); ++file_watchers_it
) {
665 if (path
== file_watchers_it
->first
||
666 path
.IsParent(file_watchers_it
->first
)) {
667 map
[file_watchers_it
->first
].Update(
668 file_watchers_it
->first
,
669 drive::FileChange::FileType::FILE_TYPE_DIRECTORY
,
670 drive::FileChange::ChangeType::CHANGE_TYPE_DELETE
);
676 for (auto const& file_change_key_value
: map
) {
677 HandleFileWatchNotification(&(file_change_key_value
.second
),
678 file_change_key_value
.first
, false);
682 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type
,
683 const base::FilePath
& drive_path
) {
684 file_manager_private::DriveSyncErrorEvent event
;
686 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION
:
688 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION
;
690 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE
:
692 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE
;
694 case drive::file_system::DRIVE_SYNC_ERROR_MISC
:
696 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC
;
699 event
.file_url
= util::ConvertDrivePathToFileSystemUrl(
700 profile_
, drive_path
, kFileManagerAppId
).spec();
701 BroadcastEvent(profile_
,
702 extensions::events::FILE_MANAGER_PRIVATE_ON_DRIVE_SYNC_ERROR
,
703 file_manager_private::OnDriveSyncError::kEventName
,
704 file_manager_private::OnDriveSyncError::Create(event
));
707 void EventRouter::OnRefreshTokenInvalid() {
708 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
710 // Raise a DriveConnectionStatusChanged event to notify the status offline.
712 profile_
, extensions::events::
713 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED
,
714 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
715 file_manager_private::OnDriveConnectionStatusChanged::Create());
718 void EventRouter::OnReadyToSendRequests() {
719 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
721 // Raise a DriveConnectionStatusChanged event to notify the status online.
723 profile_
, extensions::events::
724 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED
,
725 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
726 file_manager_private::OnDriveConnectionStatusChanged::Create());
729 void EventRouter::HandleFileWatchNotification(const drive::FileChange
* list
,
730 const base::FilePath
& local_path
,
732 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
734 WatcherMap::const_iterator iter
= file_watchers_
.find(local_path
);
735 if (iter
== file_watchers_
.end()) {
739 if (list
&& list
->size() > kDirectoryChangeEventMaxDetailInfoSize
) {
740 // Removes the detailed information, if the list size is more than
741 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
742 // and processing it may cause more itme.
743 // This will be invoked full-refresh in Files.app.
747 DispatchDirectoryChangeEvent(iter
->second
->virtual_path(),
750 iter
->second
->GetExtensionIds());
753 void EventRouter::DispatchDirectoryChangeEvent(
754 const base::FilePath
& virtual_path
,
755 const drive::FileChange
* list
,
757 const std::vector
<std::string
>& extension_ids
) {
758 dispatch_directory_change_event_impl_
.Run(virtual_path
, list
, got_error
,
762 void EventRouter::DispatchDirectoryChangeEventImpl(
763 const base::FilePath
& virtual_path
,
764 const drive::FileChange
* list
,
766 const std::vector
<std::string
>& extension_ids
) {
771 linked_ptr
<drive::FileChange
> changes
;
773 changes
.reset(new drive::FileChange(*list
)); // Copy
775 for (size_t i
= 0; i
< extension_ids
.size(); ++i
) {
776 std::string
* extension_id
= new std::string(extension_ids
[i
]);
778 FileDefinition file_definition
;
779 file_definition
.virtual_path
= virtual_path
;
780 // TODO(mtomasz): Add support for watching files in File System Provider
782 file_definition
.is_directory
= true;
784 file_manager::util::ConvertFileDefinitionToEntryDefinition(
789 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition
,
790 weak_factory_
.GetWeakPtr(),
792 base::Owned(extension_id
),
797 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
798 const linked_ptr
<drive::FileChange
> list
,
799 const std::string
* extension_id
,
801 const EntryDefinition
& entry_definition
) {
802 // TODO(mtomasz): Add support for watching files in File System Provider API.
803 if (entry_definition
.error
!= base::File::FILE_OK
||
804 !entry_definition
.is_directory
) {
805 DVLOG(1) << "Unable to dispatch event because resolving the directory "
806 << "entry definition failed.";
810 file_manager_private::FileWatchEvent event
;
811 event
.event_type
= watcher_error
812 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
813 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED
;
815 // Detailed information is available.
817 event
.changed_files
.reset(
818 new std::vector
<linked_ptr
<file_manager_private::FileChange
> >);
820 if (list
->map().empty())
823 for (drive::FileChange::Map::const_iterator it
= list
->map().begin();
824 it
!= list
->map().end();
826 linked_ptr
<file_manager_private::FileChange
> change_list(
827 new file_manager_private::FileChange
);
829 GURL url
= util::ConvertDrivePathToFileSystemUrl(
830 profile_
, it
->first
, *extension_id
);
831 change_list
->url
= url
.spec();
833 for (drive::FileChange::ChangeList::List::const_iterator change
=
834 it
->second
.list().begin();
835 change
!= it
->second
.list().end();
837 change_list
->changes
.push_back(
838 ConvertChangeTypeFromDriveToApi(change
->change()));
841 event
.changed_files
->push_back(change_list
);
845 event
.entry
.additional_properties
.SetString(
846 "fileSystemName", entry_definition
.file_system_name
);
847 event
.entry
.additional_properties
.SetString(
848 "fileSystemRoot", entry_definition
.file_system_root_url
);
849 event
.entry
.additional_properties
.SetString(
850 "fileFullPath", "/" + entry_definition
.full_path
.value());
851 event
.entry
.additional_properties
.SetBoolean("fileIsDirectory",
852 entry_definition
.is_directory
);
854 DispatchEventToExtension(
855 profile_
, *extension_id
,
856 extensions::events::FILE_MANAGER_PRIVATE_ON_DIRECTORY_CHANGED
,
857 file_manager_private::OnDirectoryChanged::kEventName
,
858 file_manager_private::OnDirectoryChanged::Create(event
));
861 void EventRouter::OnDiskAdded(
862 const DiskMountManager::Disk
& disk
, bool mounting
) {
863 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
867 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk
& disk
) {
868 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
872 void EventRouter::OnDeviceAdded(const std::string
& device_path
) {
873 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
877 void EventRouter::OnDeviceRemoved(const std::string
& device_path
) {
878 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
882 void EventRouter::OnVolumeMounted(chromeos::MountError error_code
,
883 const Volume
& volume
) {
884 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
885 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
886 // happen at shutdown. This should be removed after removing Drive mounting
887 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
888 // the only path to come here after Shutdown is called).
892 DispatchMountCompletedEvent(
893 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT
, error_code
,
896 // TODO(mtomasz): Move VolumeManager and part of the event router outside of
897 // file_manager, so there is no dependency between File System API and the
898 // file_manager code.
899 extensions::file_system_api::DispatchVolumeListChangeEvent(profile_
);
902 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code
,
903 const Volume
& volume
) {
904 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
905 DispatchMountCompletedEvent(
906 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT
, error_code
,
910 void EventRouter::DispatchMountCompletedEvent(
911 file_manager_private::MountCompletedEventType event_type
,
912 chromeos::MountError error
,
913 const Volume
& volume
) {
914 // Build an event object.
915 file_manager_private::MountCompletedEvent event
;
916 event
.event_type
= event_type
;
917 event
.status
= MountErrorToMountCompletedStatus(error
);
918 util::VolumeToVolumeMetadata(profile_
, volume
, &event
.volume_metadata
);
919 event
.should_notify
=
920 ShouldShowNotificationForVolume(profile_
, *device_event_router_
, volume
);
921 BroadcastEvent(profile_
,
922 extensions::events::FILE_MANAGER_PRIVATE_ON_MOUNT_COMPLETED
,
923 file_manager_private::OnMountCompleted::kEventName
,
924 file_manager_private::OnMountCompleted::Create(event
));
927 void EventRouter::OnFormatStarted(const std::string
& device_path
,
929 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
933 void EventRouter::OnFormatCompleted(const std::string
& device_path
,
935 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
939 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
940 const DispatchDirectoryChangeEventImplCallback
& callback
) {
941 dispatch_directory_change_event_impl_
= callback
;
944 base::WeakPtr
<EventRouter
> EventRouter::GetWeakPtr() {
945 return weak_factory_
.GetWeakPtr();
948 } // namespace file_manager