1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
8 #include "base/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "base/values.h"
15 #include "chrome/browser/app_mode/app_mode_utils.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
18 #include "chrome/browser/chromeos/drive/file_system_interface.h"
19 #include "chrome/browser/chromeos/drive/file_system_util.h"
20 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
21 #include "chrome/browser/chromeos/file_manager/app_id.h"
22 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
23 #include "chrome/browser/chromeos/file_manager/open_util.h"
24 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
25 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
26 #include "chrome/browser/chromeos/login/screen_locker.h"
27 #include "chrome/browser/drive/drive_service_interface.h"
28 #include "chrome/browser/extensions/extension_service.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/common/pref_names.h"
32 #include "chromeos/login/login_state.h"
33 #include "chromeos/network/network_handler.h"
34 #include "chromeos/network/network_state_handler.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/notification_source.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "extensions/browser/event_router.h"
39 #include "extensions/browser/extension_host.h"
40 #include "extensions/browser/extension_prefs.h"
41 #include "extensions/browser/extension_system.h"
42 #include "webkit/common/fileapi/file_system_types.h"
43 #include "webkit/common/fileapi/file_system_util.h"
45 using chromeos::disks::DiskMountManager;
46 using chromeos::NetworkHandler;
47 using content::BrowserThread;
48 using drive::DriveIntegrationService;
49 using drive::DriveIntegrationServiceFactory;
50 using file_manager::util::EntryDefinition;
51 using file_manager::util::FileDefinition;
53 namespace file_browser_private = extensions::api::file_browser_private;
55 namespace file_manager {
58 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
59 const base::Closure& success_callback,
60 const base::Closure& failure_callback) {
61 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
63 if (base::DirectoryExists(directory_path))
64 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
66 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
69 void DirectoryExistsOnUIThread(const base::FilePath& directory_path,
70 const base::Closure& success_callback,
71 const base::Closure& failure_callback) {
72 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
74 content::BrowserThread::PostBlockingPoolTask(
76 base::Bind(&DirectoryExistsOnBlockingPool,
82 // Constants for the "transferState" field of onFileTransferUpdated event.
83 const char kFileTransferStateStarted[] = "started";
84 const char kFileTransferStateInProgress[] = "in_progress";
85 const char kFileTransferStateCompleted[] = "completed";
86 const char kFileTransferStateFailed[] = "failed";
88 // Frequency of sending onFileTransferUpdated.
89 const int64 kProgressEventFrequencyInMilliseconds = 1000;
91 // Utility function to check if |job_info| is a file uploading job.
92 bool IsUploadJob(drive::JobType type) {
93 return (type == drive::TYPE_UPLOAD_NEW_FILE ||
94 type == drive::TYPE_UPLOAD_EXISTING_FILE);
97 // Converts the job info to a IDL generated type.
98 void JobInfoToTransferStatus(
100 const std::string& extension_id,
101 const std::string& job_status,
102 const drive::JobInfo& job_info,
103 file_browser_private::FileTransferStatus* status) {
104 DCHECK(IsActiveFileTransferJobInfo(job_info));
106 scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
107 GURL url = util::ConvertDrivePathToFileSystemUrl(
108 profile, job_info.file_path, extension_id);
109 status->file_url = url.spec();
110 status->transfer_state = file_browser_private::ParseTransferState(job_status);
111 status->transfer_type =
112 IsUploadJob(job_info.job_type) ?
113 file_browser_private::TRANSFER_TYPE_UPLOAD :
114 file_browser_private::TRANSFER_TYPE_DOWNLOAD;
115 // JavaScript does not have 64-bit integers. Instead we use double, which
116 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
117 // in C++. Larger values are rounded.
118 status->processed.reset(
119 new double(static_cast<double>(job_info.num_completed_bytes)));
121 new double(static_cast<double>(job_info.num_total_bytes)));
124 // Checks for availability of the Google+ Photos app.
125 // TODO(mtomasz): Replace with crbug.com/341902 solution.
126 bool IsGooglePhotosInstalled(Profile *profile) {
127 ExtensionService* service =
128 extensions::ExtensionSystem::Get(profile)->extension_service();
132 // Google+ Photos uses several ids for different channels. Therefore, all of
133 // them should be checked.
134 const std::string kGooglePlusPhotosIds[] = {
135 "ebpbnabdhheoknfklmpddcdijjkmklkp", // G+ Photos staging
136 "efjnaogkjbogokcnohkmnjdojkikgobo", // G+ Photos prod
137 "ejegoaikibpmikoejfephaneibodccma" // G+ Photos dev
140 for (size_t i = 0; i < arraysize(kGooglePlusPhotosIds); ++i) {
141 if (service->GetExtensionById(kGooglePlusPhotosIds[i],
142 false /* include_disable */) != NULL)
149 // Checks if the Recovery Tool is running. This is a temporary solution.
150 // TODO(mtomasz): Replace with crbug.com/341902 solution.
151 bool IsRecoveryToolRunning(Profile* profile) {
152 extensions::ExtensionPrefs* extension_prefs =
153 extensions::ExtensionPrefs::Get(profile);
154 if (!extension_prefs)
157 const std::string kRecoveryToolIds[] = {
158 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovert tool staging
159 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
162 for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
163 const std::string extension_id = kRecoveryToolIds[i];
164 if (extension_prefs->IsExtensionRunning(extension_id))
171 // Sends an event named |event_name| with arguments |event_args| to extensions.
172 void BroadcastEvent(Profile* profile,
173 const std::string& event_name,
174 scoped_ptr<base::ListValue> event_args) {
175 extensions::EventRouter::Get(profile)->BroadcastEvent(
176 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
179 file_browser_private::MountCompletedStatus
180 MountErrorToMountCompletedStatus(chromeos::MountError error) {
182 case chromeos::MOUNT_ERROR_NONE:
183 return file_browser_private::MOUNT_COMPLETED_STATUS_SUCCESS;
184 case chromeos::MOUNT_ERROR_UNKNOWN:
185 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN;
186 case chromeos::MOUNT_ERROR_INTERNAL:
187 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL;
188 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
189 return file_browser_private::
190 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT;
191 case chromeos::MOUNT_ERROR_INVALID_PATH:
192 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH;
193 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
194 return file_browser_private::
195 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED;
196 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
197 return file_browser_private::
198 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED;
199 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
200 return file_browser_private
201 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED;
202 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
203 return file_browser_private
204 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS;
205 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
206 return file_browser_private::
207 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS;
208 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
209 return file_browser_private::
210 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS;
211 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
212 return file_browser_private::
213 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND;
214 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
215 return file_browser_private::
216 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED;
217 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
218 return file_browser_private::
219 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH;
220 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
221 return file_browser_private::
222 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM;
223 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
224 return file_browser_private::
225 MOUNT_COMPLETED_STATUS_ERROR_UNSUPORTED_FILESYSTEM;
226 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
227 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE;
228 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
229 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION;
230 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
231 return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
234 return file_browser_private::MOUNT_COMPLETED_STATUS_NONE;
237 void BroadcastMountCompletedEvent(
239 file_browser_private::MountCompletedEventType event_type,
240 chromeos::MountError error,
241 const VolumeInfo& volume_info,
242 bool is_remounting) {
243 file_browser_private::MountCompletedEvent event;
244 event.event_type = event_type;
245 event.status = MountErrorToMountCompletedStatus(error);
246 util::VolumeInfoToVolumeMetadata(
247 profile, volume_info, &event.volume_metadata);
248 event.is_remounting = is_remounting;
252 file_browser_private::OnMountCompleted::kEventName,
253 file_browser_private::OnMountCompleted::Create(event));
256 file_browser_private::CopyProgressStatusType
257 CopyProgressTypeToCopyProgressStatusType(
258 fileapi::FileSystemOperation::CopyProgressType type) {
260 case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY:
261 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
262 case fileapi::FileSystemOperation::END_COPY_ENTRY:
263 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
264 case fileapi::FileSystemOperation::PROGRESS:
265 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
268 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_NONE;
271 std::string FileErrorToErrorName(base::File::Error error_code) {
272 namespace js = extensions::api::file_browser_private;
273 switch (error_code) {
274 case base::File::FILE_ERROR_NOT_FOUND:
275 return "NotFoundError";
276 case base::File::FILE_ERROR_INVALID_OPERATION:
277 case base::File::FILE_ERROR_EXISTS:
278 case base::File::FILE_ERROR_NOT_EMPTY:
279 return "InvalidModificationError";
280 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
281 case base::File::FILE_ERROR_NOT_A_FILE:
282 return "TypeMismatchError";
283 case base::File::FILE_ERROR_ACCESS_DENIED:
284 return "NoModificationAllowedError";
285 case base::File::FILE_ERROR_FAILED:
286 return "InvalidStateError";
287 case base::File::FILE_ERROR_ABORT:
289 case base::File::FILE_ERROR_SECURITY:
290 return "SecurityError";
291 case base::File::FILE_ERROR_NO_SPACE:
292 return "QuotaExceededError";
293 case base::File::FILE_ERROR_INVALID_URL:
294 return "EncodingError";
296 return "InvalidModificationError";
300 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
301 Profile* running_profile) {
302 extensions::ProcessManager* const process_manager =
303 extensions::ExtensionSystem::Get(running_profile)->process_manager();
304 if (!process_manager)
307 extensions::ExtensionHost* const extension_host =
308 process_manager->GetBackgroundHostForExtension(kFileManagerAppId);
309 if (!extension_host || !extension_host->render_process_host())
312 const int id = extension_host->render_process_host()->GetID();
313 file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
316 // Checks if we should send a progress event or not according to the
317 // |last_time| of sending an event. If |always| is true, the function always
318 // returns true. If the function returns true, the function also updates
320 bool ShouldSendProgressEvent(bool always, base::Time* last_time) {
321 const base::Time now = base::Time::Now();
322 const int64 delta = (now - *last_time).InMilliseconds();
323 // delta < 0 may rarely happen if system clock is synced and rewinded.
324 // To be conservative, we don't skip in that case.
325 if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) {
335 // Pass dummy value to JobInfo's constructor for make it default constructible.
336 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
337 : job_info(drive::TYPE_DOWNLOAD_FILE) {
340 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
341 const drive::JobInfo& info, const std::string& status)
342 : job_info(info), status(status) {
345 EventRouter::EventRouter(Profile* profile)
346 : pref_change_registrar_(new PrefChangeRegistrar),
348 multi_user_window_manager_observer_registered_(false),
349 weak_factory_(this) {
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353 EventRouter::~EventRouter() {
356 void EventRouter::Shutdown() {
357 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
359 DLOG_IF(WARNING, !file_watchers_.empty())
360 << "Not all file watchers are "
361 << "removed. This can happen when Files.app is open during shutdown.";
362 STLDeleteValues(&file_watchers_);
368 pref_change_registrar_->RemoveAll();
370 if (NetworkHandler::IsInitialized()) {
371 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
375 DriveIntegrationService* integration_service =
376 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
378 if (integration_service) {
379 integration_service->file_system()->RemoveObserver(this);
380 integration_service->drive_service()->RemoveObserver(this);
381 integration_service->job_list()->RemoveObserver(this);
384 VolumeManager* volume_manager = VolumeManager::Get(profile_);
386 volume_manager->RemoveObserver(this);
388 chrome::MultiUserWindowManager* const multi_user_window_manager =
389 chrome::MultiUserWindowManager::GetInstance();
390 if (multi_user_window_manager &&
391 multi_user_window_manager_observer_registered_) {
392 multi_user_window_manager_observer_registered_ = false;
393 multi_user_window_manager->RemoveObserver(this);
399 void EventRouter::ObserveEvents() {
404 if (!chromeos::LoginState::IsInitialized() ||
405 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
409 // VolumeManager's construction triggers DriveIntegrationService's
410 // construction, so it is necessary to call VolumeManager's Get before
411 // accessing DriveIntegrationService.
412 VolumeManager* volume_manager = VolumeManager::Get(profile_);
414 volume_manager->AddObserver(this);
416 DriveIntegrationService* integration_service =
417 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
419 if (integration_service) {
420 integration_service->drive_service()->AddObserver(this);
421 integration_service->file_system()->AddObserver(this);
422 integration_service->job_list()->AddObserver(this);
425 if (NetworkHandler::IsInitialized()) {
426 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
430 pref_change_registrar_->Init(profile_->GetPrefs());
431 base::Closure callback =
432 base::Bind(&EventRouter::OnFileManagerPrefsChanged,
433 weak_factory_.GetWeakPtr());
434 pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
435 pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
436 pref_change_registrar_->Add(prefs::kDisableDrive, callback);
437 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
439 notification_registrar_.Add(this,
440 chrome::NOTIFICATION_PROFILE_ADDED,
441 content::NotificationService::AllSources());
444 // File watch setup routines.
445 void EventRouter::AddFileWatch(const base::FilePath& local_path,
446 const base::FilePath& virtual_path,
447 const std::string& extension_id,
448 const BoolCallback& callback) {
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 DCHECK(!callback.is_null());
452 base::FilePath watch_path = local_path;
453 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
454 // Tweak watch path for remote sources - we need to drop leading /special
455 // directory from there in order to be able to pair these events with
456 // their change notifications.
458 watch_path = drive::util::ExtractDrivePath(watch_path);
460 WatcherMap::iterator iter = file_watchers_.find(watch_path);
461 if (iter == file_watchers_.end()) {
462 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
463 watcher->AddExtension(extension_id);
466 // For Drive, file watching is done via OnDirectoryChanged().
467 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
468 base::Bind(callback, true));
470 // For local files, start watching using FileWatcher.
471 watcher->WatchLocalFile(
473 base::Bind(&EventRouter::HandleFileWatchNotification,
474 weak_factory_.GetWeakPtr()),
478 file_watchers_[watch_path] = watcher.release();
480 iter->second->AddExtension(extension_id);
481 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
482 base::Bind(callback, true));
486 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
487 const std::string& extension_id) {
488 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
490 base::FilePath watch_path = local_path;
491 // Tweak watch path for remote sources - we need to drop leading /special
492 // directory from there in order to be able to pair these events with
493 // their change notifications.
494 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
495 watch_path = drive::util::ExtractDrivePath(watch_path);
497 WatcherMap::iterator iter = file_watchers_.find(watch_path);
498 if (iter == file_watchers_.end())
500 // Remove the watcher if |watch_path| is no longer watched by any extensions.
501 iter->second->RemoveExtension(extension_id);
502 if (iter->second->GetExtensionIds().empty()) {
504 file_watchers_.erase(iter);
508 void EventRouter::OnCopyCompleted(int copy_id,
509 const GURL& source_url,
510 const GURL& destination_url,
511 base::File::Error error) {
512 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
514 file_browser_private::CopyProgressStatus status;
515 if (error == base::File::FILE_OK) {
516 // Send success event.
517 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
518 status.source_url.reset(new std::string(source_url.spec()));
519 status.destination_url.reset(new std::string(destination_url.spec()));
522 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
523 status.error.reset(new std::string(FileErrorToErrorName(error)));
528 file_browser_private::OnCopyProgress::kEventName,
529 file_browser_private::OnCopyProgress::Create(copy_id, status));
532 void EventRouter::OnCopyProgress(
534 fileapi::FileSystemOperation::CopyProgressType type,
535 const GURL& source_url,
536 const GURL& destination_url,
538 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
540 file_browser_private::CopyProgressStatus status;
541 status.type = CopyProgressTypeToCopyProgressStatusType(type);
542 status.source_url.reset(new std::string(source_url.spec()));
543 if (type == fileapi::FileSystemOperation::END_COPY_ENTRY)
544 status.destination_url.reset(new std::string(destination_url.spec()));
545 if (type == fileapi::FileSystemOperation::PROGRESS)
546 status.size.reset(new double(size));
548 // Should not skip events other than TYPE_PROGRESS.
550 status.type != file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
551 if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
556 file_browser_private::OnCopyProgress::kEventName,
557 file_browser_private::OnCopyProgress::Create(copy_id, status));
560 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
561 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
568 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
569 file_browser_private::OnDriveConnectionStatusChanged::Create());
572 void EventRouter::OnFileManagerPrefsChanged() {
573 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
580 file_browser_private::OnPreferencesChanged::kEventName,
581 file_browser_private::OnPreferencesChanged::Create());
584 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
585 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
586 OnJobUpdated(job_info);
589 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
590 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
591 if (!drive::IsActiveFileTransferJobInfo(job_info))
594 bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
596 // Replace with the latest job info.
597 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
599 is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress);
601 // Fire event if needed.
602 bool always = is_new_job;
603 SendDriveFileTransferEvent(always);
606 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
607 drive::FileError error) {
608 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
609 if (!drive::IsActiveFileTransferJobInfo(job_info))
612 // Replace with the latest job info.
613 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
615 error == drive::FILE_ERROR_OK ? kFileTransferStateCompleted
616 : kFileTransferStateFailed);
618 // Fire event if needed.
620 SendDriveFileTransferEvent(always);
622 // Forget about the job.
623 drive_jobs_.erase(job_info.job_id);
626 void EventRouter::SendDriveFileTransferEvent(bool always) {
627 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
629 // When |always| flag is not set, we don't send the event until certain
630 // amount of time passes after the previous one. This is to avoid
631 // flooding the IPC between extensions by many onFileTransferUpdated events.
632 if (!ShouldSendProgressEvent(always, &last_file_transfer_event_))
635 // Convert the current |drive_jobs_| to IDL type.
636 std::vector<linked_ptr<file_browser_private::FileTransferStatus> >
638 for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
639 iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
640 linked_ptr<file_browser_private::FileTransferStatus> status(
641 new file_browser_private::FileTransferStatus());
642 JobInfoToTransferStatus(profile_,
645 iter->second.job_info,
647 status_list.push_back(status);
651 file_browser_private::OnFileTransfersUpdated::kEventName,
652 file_browser_private::OnFileTransfersUpdated::Create(status_list));
655 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
656 HandleFileWatchNotification(drive_path, false);
659 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
660 const base::FilePath& drive_path) {
661 file_browser_private::DriveSyncErrorEvent event;
663 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
665 file_browser_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
667 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
669 file_browser_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
671 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
673 file_browser_private::DRIVE_SYNC_ERROR_TYPE_MISC;
676 event.file_url = util::ConvertDrivePathToFileSystemUrl(
677 profile_, drive_path, kFileManagerAppId).spec();
680 file_browser_private::OnDriveSyncError::kEventName,
681 file_browser_private::OnDriveSyncError::Create(event));
684 void EventRouter::OnRefreshTokenInvalid() {
685 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
687 // Raise a DriveConnectionStatusChanged event to notify the status offline.
690 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
691 file_browser_private::OnDriveConnectionStatusChanged::Create());
694 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path,
696 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
698 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
699 if (iter == file_watchers_.end()) {
702 DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
703 iter->second->GetExtensionIds());
706 void EventRouter::DispatchDirectoryChangeEvent(
707 const base::FilePath& virtual_path,
709 const std::vector<std::string>& extension_ids) {
715 for (size_t i = 0; i < extension_ids.size(); ++i) {
716 const std::string& extension_id = extension_ids[i];
718 FileDefinition file_definition;
719 file_definition.virtual_path = virtual_path;
720 file_definition.is_directory = true;
722 file_manager::util::ConvertFileDefinitionToEntryDefinition(
727 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
728 weak_factory_.GetWeakPtr(),
733 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
735 const EntryDefinition& entry_definition) {
736 if (entry_definition.error != base::File::FILE_OK) {
737 DVLOG(1) << "Unable to dispatch event because resolving the entry "
738 << "definition failed.";
742 file_browser_private::FileWatchEvent event;
743 event.event_type = watcher_error
744 ? file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR
745 : file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
747 event.entry.additional_properties.SetString(
748 "fileSystemName", entry_definition.file_system_name);
749 event.entry.additional_properties.SetString(
750 "fileSystemRoot", entry_definition.file_system_root_url);
751 event.entry.additional_properties.SetString(
752 "fileFullPath", "/" + entry_definition.full_path.value());
753 event.entry.additional_properties.SetBoolean("fileIsDirectory",
754 entry_definition.is_directory);
756 BroadcastEvent(profile_,
757 file_browser_private::OnDirectoryChanged::kEventName,
758 file_browser_private::OnDirectoryChanged::Create(event));
761 void EventRouter::ShowRemovableDeviceInFileManager(
763 const base::FilePath& mount_path) {
764 // Do not attempt to open File Manager while the login is in progress or
765 // the screen is locked or running in kiosk app mode and make sure the file
766 // manager is opened only for the active user.
767 if (chromeos::LoginDisplayHostImpl::default_host() ||
768 chromeos::ScreenLocker::default_screen_locker() ||
769 chrome::IsRunningInForcedAppMode() ||
770 profile_ != ProfileManager::GetActiveUserProfile())
773 // Do not pop-up the File Manager, if the recovery tool is running.
774 if (IsRecoveryToolRunning(profile_))
777 // Do not pop-up the File Manager, if Google+ Photos may be launched.
778 if (IsGooglePhotosInstalled(profile_)) {
779 // MTP device is handled by Photos app.
780 if (type == VOLUME_TYPE_MTP)
782 // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
783 // cameras should have pictures located in the DCIM root directory.
784 // Such removable disks are handled by Photos app.
785 if (type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
786 DirectoryExistsOnUIThread(
787 mount_path.AppendASCII("DCIM"),
788 base::Bind(&base::DoNothing),
789 base::Bind(&util::OpenRemovableDrive, profile_, mount_path));
794 util::OpenRemovableDrive(profile_, mount_path);
797 void EventRouter::DispatchDeviceEvent(
798 file_browser_private::DeviceEventType type,
799 const std::string& device_path) {
800 file_browser_private::DeviceEvent event;
802 event.device_path = device_path;
803 BroadcastEvent(profile_,
804 file_browser_private::OnDeviceChanged::kEventName,
805 file_browser_private::OnDeviceChanged::Create(event));
808 void EventRouter::OnDiskAdded(
809 const DiskMountManager::Disk& disk, bool mounting) {
810 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
813 // If the disk is not being mounted, we don't want the Scanning
814 // notification to persist.
816 file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
817 disk.system_path_prefix());
821 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
822 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
826 void EventRouter::OnDeviceAdded(const std::string& device_path) {
827 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
829 // If the policy is set instead of showing the new device notification,
830 // we show a notification that the operation is not permitted.
831 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
833 file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
839 file_browser_private::DEVICE_EVENT_TYPE_ADDED,
843 void EventRouter::OnDeviceRemoved(const std::string& device_path,
844 bool hard_unplugged) {
845 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
848 file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
851 if (hard_unplugged) {
852 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_HARD_UNPLUGGED,
857 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
858 const VolumeInfo& volume_info,
859 bool is_remounting) {
860 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
861 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
862 // happen at shutdown. This should be removed after removing Drive mounting
863 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
864 // the only path to come here after Shutdown is called).
868 BroadcastMountCompletedEvent(
870 file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
875 if ((volume_info.type == VOLUME_TYPE_MTP ||
876 volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) &&
878 // If a new device was mounted, a new File manager window may need to be
880 if (error_code == chromeos::MOUNT_ERROR_NONE)
881 ShowRemovableDeviceInFileManager(volume_info.type,
882 volume_info.mount_path);
886 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
887 const VolumeInfo& volume_info) {
888 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
889 BroadcastMountCompletedEvent(
891 file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
897 void EventRouter::OnFormatStarted(const std::string& device_path,
899 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
902 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
905 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
910 void EventRouter::OnFormatCompleted(const std::string& device_path,
912 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
913 DispatchDeviceEvent(success ?
914 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
915 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
919 void EventRouter::Observe(int type,
920 const content::NotificationSource& source,
921 const content::NotificationDetails& details) {
922 if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
923 Profile* const added_profile = content::Source<Profile>(source).ptr();
924 if (!added_profile->IsOffTheRecord())
925 GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
927 BroadcastEvent(profile_,
928 file_browser_private::OnProfileAdded::kEventName,
929 file_browser_private::OnProfileAdded::Create());
933 void EventRouter::RegisterMultiUserWindowManagerObserver() {
934 if (multi_user_window_manager_observer_registered_)
936 chrome::MultiUserWindowManager* const multi_user_window_manager =
937 chrome::MultiUserWindowManager::GetInstance();
938 if (multi_user_window_manager) {
939 multi_user_window_manager->AddObserver(this);
940 multi_user_window_manager_observer_registered_ = true;
944 void EventRouter::OnOwnerEntryChanged(aura::Window* window) {
945 BroadcastEvent(profile_,
946 file_browser_private::OnDesktopChanged::kEventName,
947 file_browser_private::OnDesktopChanged::Create());
950 } // namespace file_manager