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/event_names.h"
29 #include "chrome/browser/extensions/extension_host.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/profiles/profile_manager.h"
33 #include "chrome/common/pref_names.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 "content/public/browser/browser_thread.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "extensions/browser/event_router.h"
41 #include "extensions/browser/extension_prefs.h"
42 #include "extensions/browser/extension_system.h"
43 #include "webkit/common/fileapi/file_system_types.h"
44 #include "webkit/common/fileapi/file_system_util.h"
46 using chromeos::disks::DiskMountManager;
47 using chromeos::NetworkHandler;
48 using content::BrowserThread;
49 using drive::DriveIntegrationService;
50 using drive::DriveIntegrationServiceFactory;
52 namespace file_browser_private = extensions::api::file_browser_private;
54 namespace file_manager {
57 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
58 const base::Closure& success_callback,
59 const base::Closure& failure_callback) {
60 DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
62 if (base::DirectoryExists(directory_path))
63 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
65 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
68 void DirectoryExistsOnUIThread(const base::FilePath& directory_path,
69 const base::Closure& success_callback,
70 const base::Closure& failure_callback) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73 content::BrowserThread::PostBlockingPoolTask(
75 base::Bind(&DirectoryExistsOnBlockingPool,
81 // Constants for the "transferState" field of onFileTransferUpdated event.
82 const char kFileTransferStateStarted[] = "started";
83 const char kFileTransferStateInProgress[] = "in_progress";
84 const char kFileTransferStateCompleted[] = "completed";
85 const char kFileTransferStateFailed[] = "failed";
87 // Frequency of sending onFileTransferUpdated.
88 const int64 kFileTransferEventFrequencyInMilliseconds = 1000;
90 // Utility function to check if |job_info| is a file uploading job.
91 bool IsUploadJob(drive::JobType type) {
92 return (type == drive::TYPE_UPLOAD_NEW_FILE ||
93 type == drive::TYPE_UPLOAD_EXISTING_FILE);
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_browser_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_browser_private::ParseTransferState(job_status);
110 status->transfer_type =
111 IsUploadJob(job_info.job_type) ?
112 file_browser_private::TRANSFER_TYPE_UPLOAD :
113 file_browser_private::TRANSFER_TYPE_DOWNLOAD;
114 // JavaScript does not have 64-bit integers. Instead we use double, which
115 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
116 // in C++. Larger values are rounded.
117 status->processed.reset(
118 new double(static_cast<double>(job_info.num_completed_bytes)));
120 new double(static_cast<double>(job_info.num_total_bytes)));
123 // Checks for availability of the Google+ Photos app.
124 // TODO(mtomasz): Replace with crbug.com/341902 solution.
125 bool IsGooglePhotosInstalled(Profile *profile) {
126 ExtensionService* service =
127 extensions::ExtensionSystem::Get(profile)->extension_service();
131 // Google+ Photos uses several ids for different channels. Therefore, all of
132 // them should be checked.
133 const std::string kGooglePlusPhotosIds[] = {
134 "ebpbnabdhheoknfklmpddcdijjkmklkp", // G+ Photos staging
135 "efjnaogkjbogokcnohkmnjdojkikgobo", // G+ Photos prod
136 "ejegoaikibpmikoejfephaneibodccma" // G+ Photos dev
139 for (size_t i = 0; i < arraysize(kGooglePlusPhotosIds); ++i) {
140 if (service->GetExtensionById(kGooglePlusPhotosIds[i],
141 false /* include_disable */) != NULL)
148 // Checks if the Recovery Tool is running. This is a temporary solution.
149 // TODO(mtomasz): Replace with crbug.com/341902 solution.
150 bool IsRecoveryToolRunning(Profile* profile) {
151 extensions::ExtensionPrefs* extension_prefs =
152 extensions::ExtensionPrefs::Get(profile);
153 if (!extension_prefs)
156 const std::string kRecoveryToolIds[] = {
157 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovert tool staging
158 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
161 for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
162 const std::string extension_id = kRecoveryToolIds[i];
163 if (extension_prefs->IsExtensionRunning(extension_id))
170 // Sends an event named |event_name| with arguments |event_args| to extensions.
171 void BroadcastEvent(Profile* profile,
172 const std::string& event_name,
173 scoped_ptr<base::ListValue> event_args) {
174 extensions::ExtensionSystem::Get(profile)->event_router()->
175 BroadcastEvent(make_scoped_ptr(
176 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;
250 if (!volume_info.mount_path.empty() &&
251 event.volume_metadata.mount_path.empty()) {
253 file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
258 file_browser_private::OnMountCompleted::kEventName,
259 file_browser_private::OnMountCompleted::Create(event));
262 file_browser_private::CopyProgressStatusType
263 CopyProgressTypeToCopyProgressStatusType(
264 fileapi::FileSystemOperation::CopyProgressType type) {
266 case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY:
267 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
268 case fileapi::FileSystemOperation::END_COPY_ENTRY:
269 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
270 case fileapi::FileSystemOperation::PROGRESS:
271 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
274 return file_browser_private::COPY_PROGRESS_STATUS_TYPE_NONE;
277 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
278 Profile* running_profile) {
279 extensions::ProcessManager* const process_manager =
280 extensions::ExtensionSystem::Get(running_profile)->process_manager();
281 if (!process_manager)
284 extensions::ExtensionHost* const extension_host =
285 process_manager->GetBackgroundHostForExtension(kFileManagerAppId);
286 if (!extension_host || !extension_host->render_process_host())
289 const int id = extension_host->render_process_host()->GetID();
290 file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
295 // Pass dummy value to JobInfo's constructor for make it default constructible.
296 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
297 : job_info(drive::TYPE_DOWNLOAD_FILE) {
300 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
301 const drive::JobInfo& info, const std::string& status)
302 : job_info(info), status(status) {
305 EventRouter::EventRouter(Profile* profile)
306 : pref_change_registrar_(new PrefChangeRegistrar),
308 multi_user_window_manager_observer_registered_(false),
309 weak_factory_(this) {
310 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313 EventRouter::~EventRouter() {
316 void EventRouter::Shutdown() {
317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
319 DLOG_IF(WARNING, !file_watchers_.empty())
320 << "Not all file watchers are "
321 << "removed. This can happen when Files.app is open during shutdown.";
322 STLDeleteValues(&file_watchers_);
328 pref_change_registrar_->RemoveAll();
330 if (NetworkHandler::IsInitialized()) {
331 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
335 DriveIntegrationService* integration_service =
336 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
338 if (integration_service) {
339 integration_service->file_system()->RemoveObserver(this);
340 integration_service->drive_service()->RemoveObserver(this);
341 integration_service->job_list()->RemoveObserver(this);
344 VolumeManager* volume_manager = VolumeManager::Get(profile_);
346 volume_manager->RemoveObserver(this);
348 chrome::MultiUserWindowManager* const multi_user_window_manager =
349 chrome::MultiUserWindowManager::GetInstance();
350 if (multi_user_window_manager &&
351 multi_user_window_manager_observer_registered_) {
352 multi_user_window_manager_observer_registered_ = false;
353 multi_user_window_manager->RemoveObserver(this);
359 void EventRouter::ObserveEvents() {
364 if (!chromeos::LoginState::IsInitialized() ||
365 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
369 // VolumeManager's construction triggers DriveIntegrationService's
370 // construction, so it is necessary to call VolumeManager's Get before
371 // accessing DriveIntegrationService.
372 VolumeManager* volume_manager = VolumeManager::Get(profile_);
374 volume_manager->AddObserver(this);
376 DriveIntegrationService* integration_service =
377 DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
379 if (integration_service) {
380 integration_service->drive_service()->AddObserver(this);
381 integration_service->file_system()->AddObserver(this);
382 integration_service->job_list()->AddObserver(this);
385 if (NetworkHandler::IsInitialized()) {
386 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
390 pref_change_registrar_->Init(profile_->GetPrefs());
391 base::Closure callback =
392 base::Bind(&EventRouter::OnFileManagerPrefsChanged,
393 weak_factory_.GetWeakPtr());
394 pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
395 pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
396 pref_change_registrar_->Add(prefs::kDisableDrive, callback);
397 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
399 notification_registrar_.Add(this,
400 chrome::NOTIFICATION_PROFILE_ADDED,
401 content::NotificationService::AllSources());
404 // File watch setup routines.
405 void EventRouter::AddFileWatch(const base::FilePath& local_path,
406 const base::FilePath& virtual_path,
407 const std::string& extension_id,
408 const BoolCallback& callback) {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410 DCHECK(!callback.is_null());
412 base::FilePath watch_path = local_path;
413 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
414 // Tweak watch path for remote sources - we need to drop leading /special
415 // directory from there in order to be able to pair these events with
416 // their change notifications.
418 watch_path = drive::util::ExtractDrivePath(watch_path);
420 WatcherMap::iterator iter = file_watchers_.find(watch_path);
421 if (iter == file_watchers_.end()) {
422 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
423 watcher->AddExtension(extension_id);
426 // For Drive, file watching is done via OnDirectoryChanged().
427 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
428 base::Bind(callback, true));
430 // For local files, start watching using FileWatcher.
431 watcher->WatchLocalFile(
433 base::Bind(&EventRouter::HandleFileWatchNotification,
434 weak_factory_.GetWeakPtr()),
438 file_watchers_[watch_path] = watcher.release();
440 iter->second->AddExtension(extension_id);
441 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
442 base::Bind(callback, true));
446 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
447 const std::string& extension_id) {
448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 base::FilePath watch_path = local_path;
451 // Tweak watch path for remote sources - we need to drop leading /special
452 // directory from there in order to be able to pair these events with
453 // their change notifications.
454 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
455 watch_path = drive::util::ExtractDrivePath(watch_path);
457 WatcherMap::iterator iter = file_watchers_.find(watch_path);
458 if (iter == file_watchers_.end())
460 // Remove the watcher if |watch_path| is no longer watched by any extensions.
461 iter->second->RemoveExtension(extension_id);
462 if (iter->second->GetExtensionIds().empty()) {
464 file_watchers_.erase(iter);
468 void EventRouter::OnCopyCompleted(int copy_id,
469 const GURL& source_url,
470 const GURL& destination_url,
471 base::File::Error error) {
472 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
474 file_browser_private::CopyProgressStatus status;
475 if (error == base::File::FILE_OK) {
476 // Send success event.
477 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
478 status.source_url.reset(new std::string(source_url.spec()));
479 status.destination_url.reset(new std::string(destination_url.spec()));
482 status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
484 new int(fileapi::FileErrorToWebFileError(error)));
489 file_browser_private::OnCopyProgress::kEventName,
490 file_browser_private::OnCopyProgress::Create(copy_id, status));
493 void EventRouter::OnCopyProgress(
495 fileapi::FileSystemOperation::CopyProgressType type,
496 const GURL& source_url,
497 const GURL& destination_url,
499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
501 file_browser_private::CopyProgressStatus status;
502 status.type = CopyProgressTypeToCopyProgressStatusType(type);
503 status.source_url.reset(new std::string(source_url.spec()));
504 if (type == fileapi::FileSystemOperation::END_COPY_ENTRY)
505 status.destination_url.reset(new std::string(destination_url.spec()));
506 if (type == fileapi::FileSystemOperation::PROGRESS)
507 status.size.reset(new double(size));
511 file_browser_private::OnCopyProgress::kEventName,
512 file_browser_private::OnCopyProgress::Create(copy_id, status));
515 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
517 !extensions::ExtensionSystem::Get(profile_)->event_router()) {
524 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
525 file_browser_private::OnDriveConnectionStatusChanged::Create());
528 void EventRouter::OnFileManagerPrefsChanged() {
530 !extensions::ExtensionSystem::Get(profile_)->event_router()) {
537 file_browser_private::OnPreferencesChanged::kEventName,
538 file_browser_private::OnPreferencesChanged::Create());
541 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543 OnJobUpdated(job_info);
546 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
547 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
548 if (!drive::IsActiveFileTransferJobInfo(job_info))
551 bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
553 // Replace with the latest job info.
554 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
556 is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress);
558 // Fire event if needed.
559 bool always = is_new_job;
560 SendDriveFileTransferEvent(always);
563 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
564 drive::FileError error) {
565 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566 if (!drive::IsActiveFileTransferJobInfo(job_info))
569 // Replace with the latest job info.
570 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
572 error == drive::FILE_ERROR_OK ? kFileTransferStateCompleted
573 : kFileTransferStateFailed);
575 // Fire event if needed.
577 SendDriveFileTransferEvent(always);
579 // Forget about the job.
580 drive_jobs_.erase(job_info.job_id);
583 void EventRouter::SendDriveFileTransferEvent(bool always) {
584 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
586 const base::Time now = base::Time::Now();
588 // When |always| flag is not set, we don't send the event until certain
589 // amount of time passes after the previous one. This is to avoid
590 // flooding the IPC between extensions by many onFileTransferUpdated events.
592 const int64 delta = (now - last_file_transfer_event_).InMilliseconds();
593 // delta < 0 may rarely happen if system clock is synced and rewinded.
594 // To be conservative, we don't skip in that case.
595 if (0 <= delta && delta < kFileTransferEventFrequencyInMilliseconds)
599 // Convert the current |drive_jobs_| to IDL type.
600 std::vector<linked_ptr<file_browser_private::FileTransferStatus> >
602 for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
603 iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
604 linked_ptr<file_browser_private::FileTransferStatus> status(
605 new file_browser_private::FileTransferStatus());
606 JobInfoToTransferStatus(profile_,
609 iter->second.job_info,
611 status_list.push_back(status);
615 file_browser_private::OnFileTransfersUpdated::kEventName,
616 file_browser_private::OnFileTransfersUpdated::Create(status_list));
617 last_file_transfer_event_ = now;
620 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
621 HandleFileWatchNotification(drive_path, false);
624 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
625 const base::FilePath& drive_path) {
626 file_browser_private::DriveSyncErrorEvent event;
628 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
630 file_browser_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
632 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
634 file_browser_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
636 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
638 file_browser_private::DRIVE_SYNC_ERROR_TYPE_MISC;
641 event.file_url = util::ConvertDrivePathToFileSystemUrl(
642 profile_, drive_path, kFileManagerAppId).spec();
645 file_browser_private::OnDriveSyncError::kEventName,
646 file_browser_private::OnDriveSyncError::Create(event));
649 void EventRouter::OnRefreshTokenInvalid() {
650 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
652 // Raise a DriveConnectionStatusChanged event to notify the status offline.
655 file_browser_private::OnDriveConnectionStatusChanged::kEventName,
656 file_browser_private::OnDriveConnectionStatusChanged::Create());
659 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path,
661 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
663 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
664 if (iter == file_watchers_.end()) {
667 DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
668 iter->second->GetExtensionIds());
671 void EventRouter::DispatchDirectoryChangeEvent(
672 const base::FilePath& virtual_path,
674 const std::vector<std::string>& extension_ids) {
680 for (size_t i = 0; i < extension_ids.size(); ++i) {
681 const std::string& extension_id = extension_ids[i];
682 const GURL target_origin_url(
683 extensions::Extension::GetBaseURLFromExtensionId(extension_id));
684 // This will be replaced with a real Entry in custom bindings.
685 const fileapi::FileSystemInfo info =
686 fileapi::GetFileSystemInfoForChromeOS(target_origin_url.GetOrigin());
688 file_browser_private::FileWatchEvent event;
689 event.event_type = got_error ?
690 file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR :
691 file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
692 event.entry.additional_properties.SetString("fileSystemName", info.name);
693 event.entry.additional_properties.SetString("fileSystemRoot",
694 info.root_url.spec());
695 event.entry.additional_properties.SetString("fileFullPath",
696 "/" + virtual_path.value());
697 event.entry.additional_properties.SetBoolean("fileIsDirectory", true);
701 file_browser_private::OnDirectoryChanged::kEventName,
702 file_browser_private::OnDirectoryChanged::Create(event));
706 void EventRouter::ShowRemovableDeviceInFileManager(
707 const base::FilePath& mount_path) {
708 // Do not attempt to open File Manager while the login is in progress or
709 // the screen is locked or running in kiosk app mode and make sure the file
710 // manager is opened only for the active user.
711 if (chromeos::LoginDisplayHostImpl::default_host() ||
712 chromeos::ScreenLocker::default_screen_locker() ||
713 chrome::IsRunningInForcedAppMode() ||
714 profile_ != ProfileManager::GetActiveUserProfile())
717 // Do not pop-up the File Manager, if the recovery tool is running.
718 if (IsRecoveryToolRunning(profile_))
721 // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
722 // cameras should have pictures located in the DCIM root directory.
723 const base::FilePath dcim_path = mount_path.Append(
724 FILE_PATH_LITERAL("DCIM"));
726 // If there is a DCIM folder and Google+ Photos is installed, then do not
728 DirectoryExistsOnUIThread(
730 IsGooglePhotosInstalled(profile_)
731 ? base::Bind(&base::DoNothing)
732 : base::Bind(&util::OpenRemovableDrive, profile_, mount_path),
733 base::Bind(&util::OpenRemovableDrive, profile_, mount_path));
736 void EventRouter::DispatchDeviceEvent(
737 file_browser_private::DeviceEventType type,
738 const std::string& device_path) {
739 file_browser_private::DeviceEvent event;
741 event.device_path = device_path;
742 BroadcastEvent(profile_,
743 file_browser_private::OnDeviceChanged::kEventName,
744 file_browser_private::OnDeviceChanged::Create(event));
747 void EventRouter::OnDiskAdded(
748 const DiskMountManager::Disk& disk, bool mounting) {
749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
752 // If the disk is not being mounted, we don't want the Scanning
753 // notification to persist.
755 file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
756 disk.system_path_prefix());
760 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
761 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
765 void EventRouter::OnDeviceAdded(const std::string& device_path) {
766 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
768 // If the policy is set instead of showing the new device notification,
769 // we show a notification that the operation is not permitted.
770 if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
772 file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
778 file_browser_private::DEVICE_EVENT_TYPE_ADDED,
782 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
783 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
786 file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
790 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
791 const VolumeInfo& volume_info,
792 bool is_remounting) {
793 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
794 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
795 // happen at shutdown. This should be removed after removing Drive mounting
796 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
797 // the only path to come here after Shutdown is called).
801 BroadcastMountCompletedEvent(
803 file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
808 if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
810 // If a new device was mounted, a new File manager window may need to be
812 if (error_code == chromeos::MOUNT_ERROR_NONE)
813 ShowRemovableDeviceInFileManager(volume_info.mount_path);
817 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
818 const VolumeInfo& volume_info) {
819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
820 BroadcastMountCompletedEvent(
822 file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
828 void EventRouter::OnFormatStarted(const std::string& device_path,
830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
833 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
836 DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
841 void EventRouter::OnFormatCompleted(const std::string& device_path,
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
844 DispatchDeviceEvent(success ?
845 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
846 file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
850 void EventRouter::Observe(int type,
851 const content::NotificationSource& source,
852 const content::NotificationDetails& details) {
853 if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
854 Profile* const added_profile = content::Source<Profile>(source).ptr();
855 if (!added_profile->IsOffTheRecord())
856 GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
858 BroadcastEvent(profile_,
859 file_browser_private::OnProfileAdded::kEventName,
860 file_browser_private::OnProfileAdded::Create());
864 void EventRouter::RegisterMultiUserWindowManagerObserver() {
865 if (multi_user_window_manager_observer_registered_)
867 chrome::MultiUserWindowManager* const multi_user_window_manager =
868 chrome::MultiUserWindowManager::GetInstance();
869 if (multi_user_window_manager) {
870 multi_user_window_manager->AddObserver(this);
871 multi_user_window_manager_observer_registered_ = true;
875 void EventRouter::OnOwnerEntryChanged(aura::Window* window) {
876 BroadcastEvent(profile_,
877 file_browser_private::OnDesktopChanged::kEventName,
878 file_browser_private::OnDesktopChanged::Create());
881 } // namespace file_manager