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/files/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/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/chrome_notification_types.h"
18 #include "chrome/browser/chromeos/drive/drive_integration_service.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/device_event_router.h"
23 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
24 #include "chrome/browser/chromeos/file_manager/app_id.h"
25 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
26 #include "chrome/browser/chromeos/file_manager/open_util.h"
27 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
28 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
29 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
30 #include "chrome/browser/drive/drive_service_interface.h"
31 #include "chrome/browser/extensions/extension_service.h"
32 #include "chrome/browser/extensions/extension_util.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/profiles/profile_manager.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/pref_names.h"
37 #include "chromeos/dbus/dbus_thread_manager.h"
38 #include "chromeos/login/login_state.h"
39 #include "chromeos/network/network_handler.h"
40 #include "chromeos/network/network_state_handler.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 {
62 // Constants for the "transferState" field of onFileTransferUpdated event.
63 const char kFileTransferStateAdded[] = "added";
64 const char kFileTransferStateStarted[] = "started";
65 const char kFileTransferStateInProgress[] = "in_progress";
66 const char kFileTransferStateCompleted[] = "completed";
67 const char kFileTransferStateFailed[] = "failed";
69 // Frequency of sending onFileTransferUpdated.
70 const int64 kProgressEventFrequencyInMilliseconds = 1000;
72 // Maximim size of detailed change info on directory change event. If the size
73 // exceeds the maximum size, the detailed info is omitted and the force refresh
75 const size_t kDirectoryChangeEventMaxDetailInfoSize = 1000;
77 // This time(millisecond) is used for confirm following event exists.
78 const int64 kFileTransferEventDelayTimeInMilliseconds = 300;
80 // Utility function to check if |job_info| is a file uploading job.
81 bool IsUploadJob(drive::JobType type) {
82 return (type == drive::TYPE_UPLOAD_NEW_FILE ||
83 type == drive::TYPE_UPLOAD_EXISTING_FILE);
86 size_t CountActiveFileTransferJobInfo(
87 const std::vector<drive::JobInfo>& job_info_list) {
88 size_t num_active_file_transfer_job_info = 0;
89 for (size_t i = 0; i < job_info_list.size(); ++i) {
90 if (IsActiveFileTransferJobInfo(job_info_list[i]))
91 ++num_active_file_transfer_job_info;
93 return num_active_file_transfer_job_info;
96 // Converts the job info to a IDL generated type.
97 void JobInfoToTransferStatus(
99 const std::string& extension_id,
100 const std::string& job_status,
101 const drive::JobInfo& job_info,
102 file_manager_private::FileTransferStatus* status) {
103 DCHECK(IsActiveFileTransferJobInfo(job_info));
105 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
106 GURL url = util::ConvertDrivePathToFileSystemUrl(
107 profile, job_info.file_path, extension_id);
108 status->file_url = url.spec();
109 status->transfer_state = file_manager_private::ParseTransferState(job_status);
110 status->transfer_type =
111 IsUploadJob(job_info.job_type) ?
112 file_manager_private::TRANSFER_TYPE_UPLOAD :
113 file_manager_private::TRANSFER_TYPE_DOWNLOAD;
114 DriveIntegrationService* const integration_service =
115 DriveIntegrationServiceFactory::FindForProfile(profile);
116 status->num_total_jobs = CountActiveFileTransferJobInfo(
117 integration_service->job_list()->GetJobInfoList());
118 // JavaScript does not have 64-bit integers. Instead we use double, which
119 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
120 // in C++. Larger values are rounded.
121 status->processed.reset(
122 new double(static_cast<double>(job_info.num_completed_bytes)));
124 new double(static_cast<double>(job_info.num_total_bytes)));
127 // Checks if the Recovery Tool is running. This is a temporary solution.
128 // TODO(mtomasz): Replace with crbug.com/341902 solution.
129 bool IsRecoveryToolRunning(Profile* profile) {
130 extensions::ExtensionPrefs* extension_prefs =
131 extensions::ExtensionPrefs::Get(profile);
132 if (!extension_prefs)
135 const std::string kRecoveryToolIds[] = {
136 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging
137 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
140 for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
141 const std::string extension_id = kRecoveryToolIds[i];
142 if (extension_prefs->IsExtensionRunning(extension_id))
149 // Sends an event named |event_name| with arguments |event_args| to extensions.
150 void BroadcastEvent(Profile* profile,
151 const std::string& event_name,
152 scoped_ptr<base::ListValue> event_args) {
153 extensions::EventRouter::Get(profile)->BroadcastEvent(
154 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
157 file_manager_private::MountCompletedStatus
158 MountErrorToMountCompletedStatus(chromeos::MountError error) {
160 case chromeos::MOUNT_ERROR_NONE:
161 return file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS;
162 case chromeos::MOUNT_ERROR_UNKNOWN:
163 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN;
164 case chromeos::MOUNT_ERROR_INTERNAL:
165 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL;
166 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
167 return file_manager_private::
168 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT;
169 case chromeos::MOUNT_ERROR_INVALID_PATH:
170 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH;
171 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
172 return file_manager_private::
173 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED;
174 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
175 return file_manager_private::
176 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED;
177 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
178 return file_manager_private
179 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED;
180 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
181 return file_manager_private
182 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS;
183 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
184 return file_manager_private::
185 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS;
186 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
187 return file_manager_private::
188 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS;
189 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
190 return file_manager_private::
191 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND;
192 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
193 return file_manager_private::
194 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED;
195 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
196 return file_manager_private::
197 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH;
198 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
199 return file_manager_private::
200 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM;
201 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
202 return file_manager_private::
203 MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM;
204 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
205 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE;
206 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
207 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION;
208 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
209 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
212 return file_manager_private::MOUNT_COMPLETED_STATUS_NONE;
215 file_manager_private::CopyProgressStatusType
216 CopyProgressTypeToCopyProgressStatusType(
217 storage::FileSystemOperation::CopyProgressType type) {
219 case storage::FileSystemOperation::BEGIN_COPY_ENTRY:
220 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
221 case storage::FileSystemOperation::END_COPY_ENTRY:
222 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
223 case storage::FileSystemOperation::PROGRESS:
224 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
227 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_NONE;
230 file_manager_private::ChangeType ConvertChangeTypeFromDriveToApi(
231 drive::FileChange::ChangeType type) {
233 case drive::FileChange::ADD_OR_UPDATE:
234 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE;
235 case drive::FileChange::DELETE:
236 return file_manager_private::CHANGE_TYPE_DELETE;
239 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE;
242 std::string FileErrorToErrorName(base::File::Error error_code) {
243 namespace js = extensions::api::file_manager_private;
244 switch (error_code) {
245 case base::File::FILE_ERROR_NOT_FOUND:
246 return "NotFoundError";
247 case base::File::FILE_ERROR_INVALID_OPERATION:
248 case base::File::FILE_ERROR_EXISTS:
249 case base::File::FILE_ERROR_NOT_EMPTY:
250 return "InvalidModificationError";
251 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
252 case base::File::FILE_ERROR_NOT_A_FILE:
253 return "TypeMismatchError";
254 case base::File::FILE_ERROR_ACCESS_DENIED:
255 return "NoModificationAllowedError";
256 case base::File::FILE_ERROR_FAILED:
257 return "InvalidStateError";
258 case base::File::FILE_ERROR_ABORT:
260 case base::File::FILE_ERROR_SECURITY:
261 return "SecurityError";
262 case base::File::FILE_ERROR_NO_SPACE:
263 return "QuotaExceededError";
264 case base::File::FILE_ERROR_INVALID_URL:
265 return "EncodingError";
267 return "InvalidModificationError";
271 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
272 Profile* running_profile) {
273 extensions::ExtensionHost* const extension_host =
274 extensions::ProcessManager::Get(running_profile)
275 ->GetBackgroundHostForExtension(kFileManagerAppId);
276 if (!extension_host || !extension_host->render_process_host())
279 const int id = extension_host->render_process_host()->GetID();
280 file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
283 // Checks if we should send a progress event or not according to the
284 // |last_time| of sending an event. If |always| is true, the function always
285 // returns true. If the function returns true, the function also updates
287 bool ShouldSendProgressEvent(bool always, base::Time* last_time) {
288 const base::Time now = base::Time::Now();
289 const int64 delta = (now - *last_time).InMilliseconds();
290 // delta < 0 may rarely happen if system clock is synced and rewinded.
291 // To be conservative, we don't skip in that case.
292 if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) {
300 // Obtains whether the Files.app should handle the volume or not.
301 bool ShouldShowNotificationForVolume(
303 const DeviceEventRouter& device_event_router,
304 const VolumeInfo& volume_info) {
305 if (volume_info.type != VOLUME_TYPE_MTP &&
306 volume_info.type != VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
310 if (device_event_router.is_resuming() || device_event_router.is_starting_up())
313 // Do not attempt to open File Manager while the login is in progress or
314 // the screen is locked or running in kiosk app mode and make sure the file
315 // manager is opened only for the active user.
316 if (chromeos::LoginDisplayHostImpl::default_host() ||
317 chromeos::ScreenLocker::default_screen_locker() ||
318 chrome::IsRunningInForcedAppMode() ||
319 profile != ProfileManager::GetActiveUserProfile()) {
323 // Do not pop-up the File Manager, if the recovery tool is running.
324 if (IsRecoveryToolRunning(profile))
327 // If the disable-default-apps flag is on, Files.app is not opened
328 // automatically on device mount not to obstruct the manual test.
329 if (CommandLine::ForCurrentProcess()->HasSwitch(
330 switches::kDisableDefaultApps)) {
337 // Sub-part of the event router for handling device events.
338 class DeviceEventRouterImpl : public DeviceEventRouter {
340 explicit DeviceEventRouterImpl(Profile* profile) : profile_(profile) {}
342 // DeviceEventRouter overrides.
343 virtual void OnDeviceEvent(file_manager_private::DeviceEventType type,
344 const std::string& device_path) override {
345 DCHECK_CURRENTLY_ON(BrowserThread::UI);
347 file_manager_private::DeviceEvent event;
349 event.device_path = device_path;
351 BroadcastEvent(profile_,
352 file_manager_private::OnDeviceChanged::kEventName,
353 file_manager_private::OnDeviceChanged::Create(event));
356 // DeviceEventRouter overrides.
357 virtual bool IsExternalStorageDisabled() override {
358 DCHECK_CURRENTLY_ON(BrowserThread::UI);
359 return profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled);
363 Profile* const profile_;
365 DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl);
370 // Pass dummy value to JobInfo's constructor for make it default constructible.
371 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
372 : job_info(drive::TYPE_DOWNLOAD_FILE) {
375 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
376 const drive::JobInfo& info, const std::string& status)
377 : job_info(info), status(status) {
380 EventRouter::EventRouter(Profile* profile)
381 : pref_change_registrar_(new PrefChangeRegistrar),
383 device_event_router_(new DeviceEventRouterImpl(profile)),
384 dispatch_directory_change_event_impl_(
385 base::Bind(&EventRouter::DispatchDirectoryChangeEventImpl,
386 base::Unretained(this))),
387 weak_factory_(this) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
392 EventRouter::~EventRouter() {
395 void EventRouter::Shutdown() {
396 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
398 DLOG_IF(WARNING, !file_watchers_.empty())
399 << "Not all file watchers are "
400 << "removed. This can happen when Files.app is open during shutdown.";
401 STLDeleteValues(&file_watchers_);
407 pref_change_registrar_->RemoveAll();
409 if (NetworkHandler::IsInitialized()) {
410 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
414 DriveIntegrationService* const integration_service =
415 DriveIntegrationServiceFactory::FindForProfile(profile_);
416 if (integration_service) {
417 integration_service->file_system()->RemoveObserver(this);
418 integration_service->drive_service()->RemoveObserver(this);
419 integration_service->job_list()->RemoveObserver(this);
422 VolumeManager* const volume_manager = VolumeManager::Get(profile_);
423 if (volume_manager) {
424 volume_manager->RemoveObserver(this);
425 volume_manager->RemoveObserver(device_event_router_.get());
428 chromeos::PowerManagerClient* const power_manager_client =
429 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
430 power_manager_client->RemoveObserver(device_event_router_.get());
435 void EventRouter::ObserveEvents() {
440 if (!chromeos::LoginState::IsInitialized() ||
441 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
445 // Ignore device events for the first few seconds.
446 device_event_router_->Startup();
448 // VolumeManager's construction triggers DriveIntegrationService's
449 // construction, so it is necessary to call VolumeManager's Get before
450 // accessing DriveIntegrationService.
451 VolumeManager* const volume_manager = VolumeManager::Get(profile_);
452 if (volume_manager) {
453 volume_manager->AddObserver(this);
454 volume_manager->AddObserver(device_event_router_.get());
457 chromeos::PowerManagerClient* const power_manager_client =
458 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
459 power_manager_client->AddObserver(device_event_router_.get());
461 DriveIntegrationService* const integration_service =
462 DriveIntegrationServiceFactory::FindForProfile(profile_);
463 if (integration_service) {
464 integration_service->drive_service()->AddObserver(this);
465 integration_service->file_system()->AddObserver(this);
466 integration_service->job_list()->AddObserver(this);
469 if (NetworkHandler::IsInitialized()) {
470 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
474 pref_change_registrar_->Init(profile_->GetPrefs());
475 base::Closure callback =
476 base::Bind(&EventRouter::OnFileManagerPrefsChanged,
477 weak_factory_.GetWeakPtr());
478 pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
479 pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
480 pref_change_registrar_->Add(prefs::kDisableDrive, callback);
481 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
483 notification_registrar_.Add(this,
484 chrome::NOTIFICATION_PROFILE_ADDED,
485 content::NotificationService::AllSources());
488 // File watch setup routines.
489 void EventRouter::AddFileWatch(const base::FilePath& local_path,
490 const base::FilePath& virtual_path,
491 const std::string& extension_id,
492 const BoolCallback& callback) {
493 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
494 DCHECK(!callback.is_null());
496 base::FilePath watch_path = local_path;
497 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
498 // Tweak watch path for remote sources - we need to drop leading /special
499 // directory from there in order to be able to pair these events with
500 // their change notifications.
502 watch_path = drive::util::ExtractDrivePath(watch_path);
504 WatcherMap::iterator iter = file_watchers_.find(watch_path);
505 if (iter == file_watchers_.end()) {
506 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
507 watcher->AddExtension(extension_id);
510 // For Drive, file watching is done via OnDirectoryChanged().
511 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
512 base::Bind(callback, true));
514 // For local files, start watching using FileWatcher.
515 watcher->WatchLocalFile(
517 base::Bind(&EventRouter::HandleFileWatchNotification,
518 weak_factory_.GetWeakPtr(),
519 static_cast<drive::FileChange*>(NULL)),
523 file_watchers_[watch_path] = watcher.release();
525 iter->second->AddExtension(extension_id);
526 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
527 base::Bind(callback, true));
531 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
532 const std::string& extension_id) {
533 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
535 base::FilePath watch_path = local_path;
536 // Tweak watch path for remote sources - we need to drop leading /special
537 // directory from there in order to be able to pair these events with
538 // their change notifications.
539 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
540 watch_path = drive::util::ExtractDrivePath(watch_path);
542 WatcherMap::iterator iter = file_watchers_.find(watch_path);
543 if (iter == file_watchers_.end())
545 // Remove the watcher if |watch_path| is no longer watched by any extensions.
546 iter->second->RemoveExtension(extension_id);
547 if (iter->second->GetExtensionIds().empty()) {
549 file_watchers_.erase(iter);
553 void EventRouter::OnCopyCompleted(int copy_id,
554 const GURL& source_url,
555 const GURL& destination_url,
556 base::File::Error error) {
557 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
559 file_manager_private::CopyProgressStatus status;
560 if (error == base::File::FILE_OK) {
561 // Send success event.
562 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
563 status.source_url.reset(new std::string(source_url.spec()));
564 status.destination_url.reset(new std::string(destination_url.spec()));
567 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
568 status.error.reset(new std::string(FileErrorToErrorName(error)));
573 file_manager_private::OnCopyProgress::kEventName,
574 file_manager_private::OnCopyProgress::Create(copy_id, status));
577 void EventRouter::OnCopyProgress(
579 storage::FileSystemOperation::CopyProgressType type,
580 const GURL& source_url,
581 const GURL& destination_url,
583 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
585 file_manager_private::CopyProgressStatus status;
586 status.type = CopyProgressTypeToCopyProgressStatusType(type);
587 status.source_url.reset(new std::string(source_url.spec()));
588 if (type == storage::FileSystemOperation::END_COPY_ENTRY)
589 status.destination_url.reset(new std::string(destination_url.spec()));
590 if (type == storage::FileSystemOperation::PROGRESS)
591 status.size.reset(new double(size));
593 // Should not skip events other than TYPE_PROGRESS.
595 status.type != file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
596 if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
601 file_manager_private::OnCopyProgress::kEventName,
602 file_manager_private::OnCopyProgress::Create(copy_id, status));
605 void EventRouter::OnWatcherManagerNotification(
606 const storage::FileSystemURL& file_system_url,
607 const std::string& extension_id,
608 storage::WatcherManager::ChangeType /* change_type */) {
609 std::vector<std::string> extension_ids;
610 extension_ids.push_back(extension_id);
612 DispatchDirectoryChangeEvent(file_system_url.virtual_path(), NULL,
613 false /* error */, extension_ids);
616 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
617 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
624 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
625 file_manager_private::OnDriveConnectionStatusChanged::Create());
628 void EventRouter::OnFileManagerPrefsChanged() {
629 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
636 file_manager_private::OnPreferencesChanged::kEventName,
637 file_manager_private::OnPreferencesChanged::Create());
640 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
641 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
642 if (!drive::IsActiveFileTransferJobInfo(job_info))
644 ScheduleDriveFileTransferEvent(
645 job_info, kFileTransferStateAdded, false /* immediate */);
648 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
650 if (!drive::IsActiveFileTransferJobInfo(job_info))
653 bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
655 const std::string status =
656 is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress;
658 // Replace with the latest job info.
659 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(job_info, status);
661 ScheduleDriveFileTransferEvent(job_info, status, false /* immediate */);
664 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
665 drive::FileError error) {
666 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
667 if (!drive::IsActiveFileTransferJobInfo(job_info))
670 const std::string status = error == drive::FILE_ERROR_OK
671 ? kFileTransferStateCompleted
672 : kFileTransferStateFailed;
674 ScheduleDriveFileTransferEvent(job_info, status, true /* immediate */);
676 // Forget about the job.
677 drive_jobs_.erase(job_info.job_id);
680 void EventRouter::ScheduleDriveFileTransferEvent(const drive::JobInfo& job_info,
681 const std::string& status,
683 const bool no_pending_task = !drive_job_info_for_scheduled_event_;
684 // Update the latest event.
685 drive_job_info_for_scheduled_event_.reset(
686 new DriveJobInfoWithStatus(job_info, status));
688 SendDriveFileTransferEvent();
689 } else if (no_pending_task) {
690 const base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
691 kFileTransferEventDelayTimeInMilliseconds);
692 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
694 base::Bind(&EventRouter::SendDriveFileTransferEvent,
695 weak_factory_.GetWeakPtr()),
700 void EventRouter::SendDriveFileTransferEvent() {
701 if (!drive_job_info_for_scheduled_event_)
704 file_manager_private::FileTransferStatus status;
705 JobInfoToTransferStatus(profile_,
707 drive_job_info_for_scheduled_event_->status,
708 drive_job_info_for_scheduled_event_->job_info,
711 drive_job_info_for_scheduled_event_.reset();
713 BroadcastEvent(profile_,
714 file_manager_private::OnFileTransfersUpdated::kEventName,
715 file_manager_private::OnFileTransfersUpdated::Create(status));
718 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
719 HandleFileWatchNotification(NULL, drive_path, false);
722 void EventRouter::OnFileChanged(const drive::FileChange& changed_files) {
723 // In this method, we convert changed_files to a map which can be handled by
724 // HandleFileWatchNotification.
727 // /a/b DIRECTORY:DELETE
729 // map[/a] = /a/b DIRECTORY:DELETE
730 // map[/a/b] = /a/b DIRECTORY:DELETE
732 // We used the key of map to match the watched directories of file watchers.
733 typedef std::map<base::FilePath, drive::FileChange> FileChangeMap;
734 typedef drive::FileChange::ChangeList::List FileChangeList;
737 const drive::FileChange::Map& changed_file_map = changed_files.map();
738 for (auto const& file_change_key_value : changed_file_map) {
739 // Check whether the FileChangeList contains directory deletion.
740 bool contains_directory_deletion = false;
741 const FileChangeList list = file_change_key_value.second.list();
742 for (drive::FileChange::Change const& change : list) {
743 if (change.IsDirectory() && change.IsDelete()) {
744 contains_directory_deletion = true;
749 const base::FilePath& path = file_change_key_value.first;
750 map[path.DirName()].Update(path, file_change_key_value.second);
752 // For deletion of a directory, onFileChanged gets different changed_files.
753 // We solve the difference here.
755 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
756 // 1. /a/b DELETE:DIRECTORY
757 // 2. /a DELETE:DIRECTORY
759 // /a/b is watched, and /a is deleted from Files.app.
760 // 1. /a DELETE:DIRECTORY
761 if (contains_directory_deletion) {
762 // Expand the deleted directory path with watched paths.
763 for (WatcherMap::const_iterator file_watchers_it =
764 file_watchers_.lower_bound(path);
765 file_watchers_it != file_watchers_.end(); ++file_watchers_it) {
766 if (path == file_watchers_it->first ||
767 path.IsParent(file_watchers_it->first)) {
768 map[file_watchers_it->first].Update(
769 file_watchers_it->first,
770 drive::FileChange::FileType::FILE_TYPE_DIRECTORY,
771 drive::FileChange::ChangeType::DELETE);
777 for (auto const& file_change_key_value : map) {
778 HandleFileWatchNotification(&(file_change_key_value.second),
779 file_change_key_value.first, false);
783 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
784 const base::FilePath& drive_path) {
785 file_manager_private::DriveSyncErrorEvent event;
787 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
789 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
791 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
793 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
795 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
797 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC;
800 event.file_url = util::ConvertDrivePathToFileSystemUrl(
801 profile_, drive_path, kFileManagerAppId).spec();
804 file_manager_private::OnDriveSyncError::kEventName,
805 file_manager_private::OnDriveSyncError::Create(event));
808 void EventRouter::OnRefreshTokenInvalid() {
809 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
811 // Raise a DriveConnectionStatusChanged event to notify the status offline.
814 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
815 file_manager_private::OnDriveConnectionStatusChanged::Create());
818 void EventRouter::HandleFileWatchNotification(const drive::FileChange* list,
819 const base::FilePath& local_path,
821 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
823 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
824 if (iter == file_watchers_.end()) {
828 if (list && list->size() > kDirectoryChangeEventMaxDetailInfoSize) {
829 // Removes the detailed information, if the list size is more than
830 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
831 // and processing it may cause more itme.
832 // This will be invoked full-refresh in Files.app.
836 DispatchDirectoryChangeEvent(iter->second->virtual_path(),
839 iter->second->GetExtensionIds());
842 void EventRouter::DispatchDirectoryChangeEvent(
843 const base::FilePath& virtual_path,
844 const drive::FileChange* list,
846 const std::vector<std::string>& extension_ids) {
847 dispatch_directory_change_event_impl_.Run(virtual_path, list, got_error,
851 void EventRouter::DispatchDirectoryChangeEventImpl(
852 const base::FilePath& virtual_path,
853 const drive::FileChange* list,
855 const std::vector<std::string>& extension_ids) {
860 linked_ptr<drive::FileChange> changes;
862 changes.reset(new drive::FileChange(*list)); // Copy
864 for (size_t i = 0; i < extension_ids.size(); ++i) {
865 std::string* extension_id = new std::string(extension_ids[i]);
867 FileDefinition file_definition;
868 file_definition.virtual_path = virtual_path;
869 // TODO(mtomasz): Add support for watching files in File System Provider
871 file_definition.is_directory = true;
873 file_manager::util::ConvertFileDefinitionToEntryDefinition(
878 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
879 weak_factory_.GetWeakPtr(),
881 base::Owned(extension_id),
886 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
887 const linked_ptr<drive::FileChange> list,
888 const std::string* extension_id,
890 const EntryDefinition& entry_definition) {
891 typedef std::map<base::FilePath, drive::FileChange::ChangeList> ChangeListMap;
893 // TODO(mtomasz): Add support for watching files in File System Provider API.
894 if (entry_definition.error != base::File::FILE_OK ||
895 !entry_definition.is_directory) {
896 DVLOG(1) << "Unable to dispatch event because resolving the directory "
897 << "entry definition failed.";
901 file_manager_private::FileWatchEvent event;
902 event.event_type = watcher_error
903 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
904 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED;
906 // Detailed information is available.
908 event.changed_files.reset(
909 new std::vector<linked_ptr<file_manager_private::FileChange> >);
911 if (list->map().empty())
914 for (drive::FileChange::Map::const_iterator it = list->map().begin();
915 it != list->map().end();
917 linked_ptr<file_manager_private::FileChange> change_list(
918 new file_manager_private::FileChange);
920 GURL url = util::ConvertDrivePathToFileSystemUrl(
921 profile_, it->first, *extension_id);
922 change_list->url = url.spec();
924 for (drive::FileChange::ChangeList::List::const_iterator change =
925 it->second.list().begin();
926 change != it->second.list().end();
928 change_list->changes.push_back(
929 ConvertChangeTypeFromDriveToApi(change->change()));
932 event.changed_files->push_back(change_list);
936 event.entry.additional_properties.SetString(
937 "fileSystemName", entry_definition.file_system_name);
938 event.entry.additional_properties.SetString(
939 "fileSystemRoot", entry_definition.file_system_root_url);
940 event.entry.additional_properties.SetString(
941 "fileFullPath", "/" + entry_definition.full_path.value());
942 event.entry.additional_properties.SetBoolean("fileIsDirectory",
943 entry_definition.is_directory);
945 BroadcastEvent(profile_,
946 file_manager_private::OnDirectoryChanged::kEventName,
947 file_manager_private::OnDirectoryChanged::Create(event));
950 void EventRouter::OnDiskAdded(
951 const DiskMountManager::Disk& disk, bool mounting) {
952 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
956 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
957 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
961 void EventRouter::OnDeviceAdded(const std::string& device_path) {
962 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
966 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
967 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
971 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
972 const VolumeInfo& volume_info) {
973 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
974 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
975 // happen at shutdown. This should be removed after removing Drive mounting
976 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
977 // the only path to come here after Shutdown is called).
981 DispatchMountCompletedEvent(
982 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
987 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
988 const VolumeInfo& volume_info) {
989 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
990 DispatchMountCompletedEvent(
991 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
996 void EventRouter::DispatchMountCompletedEvent(
997 file_manager_private::MountCompletedEventType event_type,
998 chromeos::MountError error,
999 const VolumeInfo& volume_info) {
1000 // Build an event object.
1001 file_manager_private::MountCompletedEvent event;
1002 event.event_type = event_type;
1003 event.status = MountErrorToMountCompletedStatus(error);
1004 util::VolumeInfoToVolumeMetadata(
1005 profile_, volume_info, &event.volume_metadata);
1006 event.should_notify = ShouldShowNotificationForVolume(
1007 profile_, *device_event_router_, volume_info);
1008 BroadcastEvent(profile_,
1009 file_manager_private::OnMountCompleted::kEventName,
1010 file_manager_private::OnMountCompleted::Create(event));
1013 void EventRouter::OnFormatStarted(const std::string& device_path,
1015 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1019 void EventRouter::OnFormatCompleted(const std::string& device_path,
1021 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1025 void EventRouter::Observe(int type,
1026 const content::NotificationSource& source,
1027 const content::NotificationDetails& details) {
1028 if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
1029 Profile* const added_profile = content::Source<Profile>(source).ptr();
1030 if (!added_profile->IsOffTheRecord())
1031 GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
1035 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
1036 const DispatchDirectoryChangeEventImplCallback& callback) {
1037 dispatch_directory_change_event_impl_ = callback;
1040 base::WeakPtr<EventRouter> EventRouter::GetWeakPtr() {
1041 return weak_factory_.GetWeakPtr();
1044 } // namespace file_manager