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/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/fileapi_util.h"
22 #include "chrome/browser/chromeos/file_manager/open_util.h"
23 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
24 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
25 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
26 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
27 #include "chrome/browser/extensions/extension_service.h"
28 #include "chrome/browser/extensions/extension_util.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/pref_names.h"
33 #include "chromeos/dbus/dbus_thread_manager.h"
34 #include "chromeos/login/login_state.h"
35 #include "chromeos/network/network_handler.h"
36 #include "chromeos/network/network_state_handler.h"
37 #include "components/drive/drive_pref_names.h"
38 #include "components/drive/file_change.h"
39 #include "components/drive/file_system_interface.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
);
454 chromeos::system::TimezoneSettings::GetInstance()->AddObserver(this);
457 // File watch setup routines.
458 void EventRouter::AddFileWatch(const base::FilePath
& local_path
,
459 const base::FilePath
& virtual_path
,
460 const std::string
& extension_id
,
461 const BoolCallback
& callback
) {
462 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
463 DCHECK(!callback
.is_null());
465 base::FilePath watch_path
= local_path
;
466 bool is_on_drive
= drive::util::IsUnderDriveMountPoint(watch_path
);
467 // Tweak watch path for remote sources - we need to drop leading /special
468 // directory from there in order to be able to pair these events with
469 // their change notifications.
471 watch_path
= drive::util::ExtractDrivePath(watch_path
);
473 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
474 if (iter
== file_watchers_
.end()) {
475 scoped_ptr
<FileWatcher
> watcher(new FileWatcher(virtual_path
));
476 watcher
->AddExtension(extension_id
);
479 // For Drive, file watching is done via OnDirectoryChanged().
480 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
481 base::Bind(callback
, true));
483 // For local files, start watching using FileWatcher.
484 watcher
->WatchLocalFile(
486 base::Bind(&EventRouter::HandleFileWatchNotification
,
487 weak_factory_
.GetWeakPtr(),
488 static_cast<drive::FileChange
*>(NULL
)),
492 file_watchers_
[watch_path
] = watcher
.release();
494 iter
->second
->AddExtension(extension_id
);
495 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
496 base::Bind(callback
, true));
500 void EventRouter::RemoveFileWatch(const base::FilePath
& local_path
,
501 const std::string
& extension_id
) {
502 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
504 base::FilePath watch_path
= local_path
;
505 // Tweak watch path for remote sources - we need to drop leading /special
506 // directory from there in order to be able to pair these events with
507 // their change notifications.
508 if (drive::util::IsUnderDriveMountPoint(watch_path
)) {
509 watch_path
= drive::util::ExtractDrivePath(watch_path
);
511 WatcherMap::iterator iter
= file_watchers_
.find(watch_path
);
512 if (iter
== file_watchers_
.end())
514 // Remove the watcher if |watch_path| is no longer watched by any extensions.
515 iter
->second
->RemoveExtension(extension_id
);
516 if (iter
->second
->GetExtensionIds().empty()) {
518 file_watchers_
.erase(iter
);
522 void EventRouter::OnCopyCompleted(int copy_id
,
523 const GURL
& source_url
,
524 const GURL
& destination_url
,
525 base::File::Error error
) {
526 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
528 file_manager_private::CopyProgressStatus status
;
529 if (error
== base::File::FILE_OK
) {
530 // Send success event.
531 status
.type
= file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS
;
532 status
.source_url
.reset(new std::string(source_url
.spec()));
533 status
.destination_url
.reset(new std::string(destination_url
.spec()));
536 status
.type
= file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR
;
537 status
.error
.reset(new std::string(FileErrorToErrorName(error
)));
540 BroadcastEvent(profile_
,
541 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS
,
542 file_manager_private::OnCopyProgress::kEventName
,
543 file_manager_private::OnCopyProgress::Create(copy_id
, status
));
546 void EventRouter::OnCopyProgress(
548 storage::FileSystemOperation::CopyProgressType type
,
549 const GURL
& source_url
,
550 const GURL
& destination_url
,
552 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
554 file_manager_private::CopyProgressStatus status
;
555 status
.type
= CopyProgressTypeToCopyProgressStatusType(type
);
556 status
.source_url
.reset(new std::string(source_url
.spec()));
557 if (type
== storage::FileSystemOperation::END_COPY_ENTRY
||
558 type
== storage::FileSystemOperation::ERROR_COPY_ENTRY
)
559 status
.destination_url
.reset(new std::string(destination_url
.spec()));
560 if (type
== storage::FileSystemOperation::ERROR_COPY_ENTRY
)
562 new std::string(FileErrorToErrorName(base::File::FILE_ERROR_FAILED
)));
563 if (type
== storage::FileSystemOperation::PROGRESS
)
564 status
.size
.reset(new double(size
));
566 // Discard error progress since current JS code cannot handle this properly.
567 // TODO(yawano): Remove this after JS side is implemented correctly.
568 if (type
== storage::FileSystemOperation::ERROR_COPY_ENTRY
)
571 // Should not skip events other than TYPE_PROGRESS.
573 status
.type
!= file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS
;
574 if (!ShouldSendProgressEvent(always
, &last_copy_progress_event_
))
577 BroadcastEvent(profile_
,
578 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS
,
579 file_manager_private::OnCopyProgress::kEventName
,
580 file_manager_private::OnCopyProgress::Create(copy_id
, status
));
583 void EventRouter::OnWatcherManagerNotification(
584 const storage::FileSystemURL
& file_system_url
,
585 const std::string
& extension_id
,
586 storage::WatcherManager::ChangeType
/* change_type */) {
587 std::vector
<std::string
> extension_ids
;
588 extension_ids
.push_back(extension_id
);
590 DispatchDirectoryChangeEvent(file_system_url
.virtual_path(), NULL
,
591 false /* error */, extension_ids
);
594 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState
* network
) {
595 if (!profile_
|| !extensions::EventRouter::Get(profile_
)) {
601 profile_
, extensions::events::
602 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED
,
603 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
604 file_manager_private::OnDriveConnectionStatusChanged::Create());
607 void EventRouter::TimezoneChanged(const icu::TimeZone
& timezone
) {
608 OnFileManagerPrefsChanged();
611 void EventRouter::OnFileManagerPrefsChanged() {
612 if (!profile_
|| !extensions::EventRouter::Get(profile_
)) {
618 profile_
, extensions::events::FILE_MANAGER_PRIVATE_ON_PREFERENCES_CHANGED
,
619 file_manager_private::OnPreferencesChanged::kEventName
,
620 file_manager_private::OnPreferencesChanged::Create());
623 void EventRouter::OnDirectoryChanged(const base::FilePath
& drive_path
) {
624 HandleFileWatchNotification(NULL
, drive_path
, false);
627 void EventRouter::OnFileChanged(const drive::FileChange
& changed_files
) {
628 // In this method, we convert changed_files to a map which can be handled by
629 // HandleFileWatchNotification.
632 // /a/b DIRECTORY:DELETE
634 // map[/a] = /a/b DIRECTORY:DELETE
635 // map[/a/b] = /a/b DIRECTORY:DELETE
637 // We used the key of map to match the watched directories of file watchers.
638 typedef std::map
<base::FilePath
, drive::FileChange
> FileChangeMap
;
639 typedef drive::FileChange::ChangeList::List FileChangeList
;
642 const drive::FileChange::Map
& changed_file_map
= changed_files
.map();
643 for (auto const& file_change_key_value
: changed_file_map
) {
644 // Check whether the FileChangeList contains directory deletion.
645 bool contains_directory_deletion
= false;
646 const FileChangeList list
= file_change_key_value
.second
.list();
647 for (drive::FileChange::Change
const& change
: list
) {
648 if (change
.IsDirectory() && change
.IsDelete()) {
649 contains_directory_deletion
= true;
654 const base::FilePath
& path
= file_change_key_value
.first
;
655 map
[path
.DirName()].Update(path
, file_change_key_value
.second
);
657 // For deletion of a directory, onFileChanged gets different changed_files.
658 // We solve the difference here.
660 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
661 // 1. /a/b DELETE:DIRECTORY
662 // 2. /a DELETE:DIRECTORY
664 // /a/b is watched, and /a is deleted from Files.app.
665 // 1. /a DELETE:DIRECTORY
666 if (contains_directory_deletion
) {
667 // Expand the deleted directory path with watched paths.
668 for (WatcherMap::const_iterator file_watchers_it
=
669 file_watchers_
.lower_bound(path
);
670 file_watchers_it
!= file_watchers_
.end(); ++file_watchers_it
) {
671 if (path
== file_watchers_it
->first
||
672 path
.IsParent(file_watchers_it
->first
)) {
673 map
[file_watchers_it
->first
].Update(
674 file_watchers_it
->first
,
675 drive::FileChange::FileType::FILE_TYPE_DIRECTORY
,
676 drive::FileChange::ChangeType::CHANGE_TYPE_DELETE
);
682 for (auto const& file_change_key_value
: map
) {
683 HandleFileWatchNotification(&(file_change_key_value
.second
),
684 file_change_key_value
.first
, false);
688 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type
,
689 const base::FilePath
& drive_path
) {
690 file_manager_private::DriveSyncErrorEvent event
;
692 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION
:
694 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION
;
696 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE
:
698 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE
;
700 case drive::file_system::DRIVE_SYNC_ERROR_NO_SERVER_SPACE
:
701 event
.type
= file_manager_private::DRIVE_SYNC_ERROR_TYPE_NO_SERVER_SPACE
;
703 case drive::file_system::DRIVE_SYNC_ERROR_MISC
:
705 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC
;
708 event
.file_url
= util::ConvertDrivePathToFileSystemUrl(
709 profile_
, drive_path
, kFileManagerAppId
).spec();
710 BroadcastEvent(profile_
,
711 extensions::events::FILE_MANAGER_PRIVATE_ON_DRIVE_SYNC_ERROR
,
712 file_manager_private::OnDriveSyncError::kEventName
,
713 file_manager_private::OnDriveSyncError::Create(event
));
716 void EventRouter::OnRefreshTokenInvalid() {
717 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
719 // Raise a DriveConnectionStatusChanged event to notify the status offline.
721 profile_
, extensions::events::
722 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED
,
723 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
724 file_manager_private::OnDriveConnectionStatusChanged::Create());
727 void EventRouter::OnReadyToSendRequests() {
728 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
730 // Raise a DriveConnectionStatusChanged event to notify the status online.
732 profile_
, extensions::events::
733 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED
,
734 file_manager_private::OnDriveConnectionStatusChanged::kEventName
,
735 file_manager_private::OnDriveConnectionStatusChanged::Create());
738 void EventRouter::HandleFileWatchNotification(const drive::FileChange
* list
,
739 const base::FilePath
& local_path
,
741 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
743 WatcherMap::const_iterator iter
= file_watchers_
.find(local_path
);
744 if (iter
== file_watchers_
.end()) {
748 if (list
&& list
->size() > kDirectoryChangeEventMaxDetailInfoSize
) {
749 // Removes the detailed information, if the list size is more than
750 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
751 // and processing it may cause more itme.
752 // This will be invoked full-refresh in Files.app.
756 DispatchDirectoryChangeEvent(iter
->second
->virtual_path(),
759 iter
->second
->GetExtensionIds());
762 void EventRouter::DispatchDirectoryChangeEvent(
763 const base::FilePath
& virtual_path
,
764 const drive::FileChange
* list
,
766 const std::vector
<std::string
>& extension_ids
) {
767 dispatch_directory_change_event_impl_
.Run(virtual_path
, list
, got_error
,
771 void EventRouter::DispatchDirectoryChangeEventImpl(
772 const base::FilePath
& virtual_path
,
773 const drive::FileChange
* list
,
775 const std::vector
<std::string
>& extension_ids
) {
780 linked_ptr
<drive::FileChange
> changes
;
782 changes
.reset(new drive::FileChange(*list
)); // Copy
784 for (size_t i
= 0; i
< extension_ids
.size(); ++i
) {
785 std::string
* extension_id
= new std::string(extension_ids
[i
]);
787 FileDefinition file_definition
;
788 file_definition
.virtual_path
= virtual_path
;
789 // TODO(mtomasz): Add support for watching files in File System Provider
791 file_definition
.is_directory
= true;
793 file_manager::util::ConvertFileDefinitionToEntryDefinition(
798 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition
,
799 weak_factory_
.GetWeakPtr(),
801 base::Owned(extension_id
),
806 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
807 const linked_ptr
<drive::FileChange
> list
,
808 const std::string
* extension_id
,
810 const EntryDefinition
& entry_definition
) {
811 // TODO(mtomasz): Add support for watching files in File System Provider API.
812 if (entry_definition
.error
!= base::File::FILE_OK
||
813 !entry_definition
.is_directory
) {
814 DVLOG(1) << "Unable to dispatch event because resolving the directory "
815 << "entry definition failed.";
819 file_manager_private::FileWatchEvent event
;
820 event
.event_type
= watcher_error
821 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
822 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED
;
824 // Detailed information is available.
826 event
.changed_files
.reset(
827 new std::vector
<linked_ptr
<file_manager_private::FileChange
> >);
829 if (list
->map().empty())
832 for (drive::FileChange::Map::const_iterator it
= list
->map().begin();
833 it
!= list
->map().end();
835 linked_ptr
<file_manager_private::FileChange
> change_list(
836 new file_manager_private::FileChange
);
838 GURL url
= util::ConvertDrivePathToFileSystemUrl(
839 profile_
, it
->first
, *extension_id
);
840 change_list
->url
= url
.spec();
842 for (drive::FileChange::ChangeList::List::const_iterator change
=
843 it
->second
.list().begin();
844 change
!= it
->second
.list().end();
846 change_list
->changes
.push_back(
847 ConvertChangeTypeFromDriveToApi(change
->change()));
850 event
.changed_files
->push_back(change_list
);
854 event
.entry
.additional_properties
.SetString(
855 "fileSystemName", entry_definition
.file_system_name
);
856 event
.entry
.additional_properties
.SetString(
857 "fileSystemRoot", entry_definition
.file_system_root_url
);
858 event
.entry
.additional_properties
.SetString(
859 "fileFullPath", "/" + entry_definition
.full_path
.value());
860 event
.entry
.additional_properties
.SetBoolean("fileIsDirectory",
861 entry_definition
.is_directory
);
863 DispatchEventToExtension(
864 profile_
, *extension_id
,
865 extensions::events::FILE_MANAGER_PRIVATE_ON_DIRECTORY_CHANGED
,
866 file_manager_private::OnDirectoryChanged::kEventName
,
867 file_manager_private::OnDirectoryChanged::Create(event
));
870 void EventRouter::OnDiskAdded(
871 const DiskMountManager::Disk
& disk
, bool mounting
) {
872 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
876 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk
& disk
) {
877 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
881 void EventRouter::OnDeviceAdded(const std::string
& device_path
) {
882 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
886 void EventRouter::OnDeviceRemoved(const std::string
& device_path
) {
887 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
891 void EventRouter::OnVolumeMounted(chromeos::MountError error_code
,
892 const Volume
& volume
) {
893 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
894 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
895 // happen at shutdown. This should be removed after removing Drive mounting
896 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
897 // the only path to come here after Shutdown is called).
901 DispatchMountCompletedEvent(
902 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT
, error_code
,
905 // TODO(mtomasz): Move VolumeManager and part of the event router outside of
906 // file_manager, so there is no dependency between File System API and the
907 // file_manager code.
908 extensions::file_system_api::DispatchVolumeListChangeEvent(profile_
);
911 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code
,
912 const Volume
& volume
) {
913 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
914 DispatchMountCompletedEvent(
915 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT
, error_code
,
919 void EventRouter::DispatchMountCompletedEvent(
920 file_manager_private::MountCompletedEventType event_type
,
921 chromeos::MountError error
,
922 const Volume
& volume
) {
923 // Build an event object.
924 file_manager_private::MountCompletedEvent event
;
925 event
.event_type
= event_type
;
926 event
.status
= MountErrorToMountCompletedStatus(error
);
927 util::VolumeToVolumeMetadata(profile_
, volume
, &event
.volume_metadata
);
928 event
.should_notify
=
929 ShouldShowNotificationForVolume(profile_
, *device_event_router_
, volume
);
930 BroadcastEvent(profile_
,
931 extensions::events::FILE_MANAGER_PRIVATE_ON_MOUNT_COMPLETED
,
932 file_manager_private::OnMountCompleted::kEventName
,
933 file_manager_private::OnMountCompleted::Create(event
));
936 void EventRouter::OnFormatStarted(const std::string
& device_path
,
938 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
942 void EventRouter::OnFormatCompleted(const std::string
& device_path
,
944 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
948 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
949 const DispatchDirectoryChangeEventImplCallback
& callback
) {
950 dispatch_directory_change_event_impl_
= callback
;
953 base::WeakPtr
<EventRouter
> EventRouter::GetWeakPtr() {
954 return weak_factory_
.GetWeakPtr();
957 } // namespace file_manager