ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / chrome / browser / chromeos / extensions / file_manager / event_router.cc
blob9adea20c941f3d5738801629cbbfde31b69c05f5
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"
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/files/file_util.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/prefs/pref_change_registrar.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h"
14 #include "base/thread_task_runner_handle.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/values.h"
17 #include "chrome/browser/app_mode/app_mode_utils.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
20 #include "chrome/browser/chromeos/drive/file_change.h"
21 #include "chrome/browser/chromeos/drive/file_system_interface.h"
22 #include "chrome/browser/chromeos/drive/file_system_util.h"
23 #include "chrome/browser/chromeos/extensions/file_manager/device_event_router.h"
24 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
25 #include "chrome/browser/chromeos/file_manager/app_id.h"
26 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
27 #include "chrome/browser/chromeos/file_manager/open_util.h"
28 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
29 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
30 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
31 #include "chrome/browser/drive/drive_service_interface.h"
32 #include "chrome/browser/extensions/extension_service.h"
33 #include "chrome/browser/extensions/extension_util.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/profiles/profile_manager.h"
36 #include "chrome/common/chrome_switches.h"
37 #include "chrome/common/pref_names.h"
38 #include "chromeos/dbus/dbus_thread_manager.h"
39 #include "chromeos/login/login_state.h"
40 #include "chromeos/network/network_handler.h"
41 #include "chromeos/network/network_state_handler.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/render_process_host.h"
44 #include "content/public/browser/storage_partition.h"
45 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_host.h"
47 #include "extensions/browser/extension_prefs.h"
48 #include "storage/common/fileapi/file_system_types.h"
49 #include "storage/common/fileapi/file_system_util.h"
51 using chromeos::disks::DiskMountManager;
52 using chromeos::NetworkHandler;
53 using content::BrowserThread;
54 using drive::DriveIntegrationService;
55 using drive::DriveIntegrationServiceFactory;
56 using file_manager::util::EntryDefinition;
57 using file_manager::util::FileDefinition;
59 namespace file_manager_private = extensions::api::file_manager_private;
61 namespace file_manager {
62 namespace {
63 // Constants for the "transferState" field of onFileTransferUpdated event.
64 const char kFileTransferStateAdded[] = "added";
65 const char kFileTransferStateStarted[] = "started";
66 const char kFileTransferStateInProgress[] = "in_progress";
67 const char kFileTransferStateCompleted[] = "completed";
68 const char kFileTransferStateFailed[] = "failed";
70 // Frequency of sending onFileTransferUpdated.
71 const int64 kProgressEventFrequencyInMilliseconds = 1000;
73 // Maximim size of detailed change info on directory change event. If the size
74 // exceeds the maximum size, the detailed info is omitted and the force refresh
75 // is kicked.
76 const size_t kDirectoryChangeEventMaxDetailInfoSize = 1000;
78 // This time(millisecond) is used for confirm following event exists.
79 const int64 kFileTransferEventDelayTimeInMilliseconds = 300;
81 // Utility function to check if |job_info| is a file uploading job.
82 bool IsUploadJob(drive::JobType type) {
83 return (type == drive::TYPE_UPLOAD_NEW_FILE ||
84 type == drive::TYPE_UPLOAD_EXISTING_FILE);
87 size_t CountActiveFileTransferJobInfo(
88 const std::vector<drive::JobInfo>& job_info_list) {
89 size_t num_active_file_transfer_job_info = 0;
90 for (size_t i = 0; i < job_info_list.size(); ++i) {
91 if (IsActiveFileTransferJobInfo(job_info_list[i]))
92 ++num_active_file_transfer_job_info;
94 return num_active_file_transfer_job_info;
97 // Converts the job info to a IDL generated type.
98 void JobInfoToTransferStatus(
99 Profile* profile,
100 const std::string& extension_id,
101 const std::string& job_status,
102 const drive::JobInfo& job_info,
103 file_manager_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_manager_private::ParseTransferState(job_status);
111 status->transfer_type =
112 IsUploadJob(job_info.job_type) ?
113 file_manager_private::TRANSFER_TYPE_UPLOAD :
114 file_manager_private::TRANSFER_TYPE_DOWNLOAD;
115 DriveIntegrationService* const integration_service =
116 DriveIntegrationServiceFactory::FindForProfile(profile);
117 status->num_total_jobs = CountActiveFileTransferJobInfo(
118 integration_service->job_list()->GetJobInfoList());
119 // JavaScript does not have 64-bit integers. Instead we use double, which
120 // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
121 // in C++. Larger values are rounded.
122 status->processed.reset(
123 new double(static_cast<double>(job_info.num_completed_bytes)));
124 status->total.reset(
125 new double(static_cast<double>(job_info.num_total_bytes)));
128 // Checks if the Recovery Tool is running. This is a temporary solution.
129 // TODO(mtomasz): Replace with crbug.com/341902 solution.
130 bool IsRecoveryToolRunning(Profile* profile) {
131 extensions::ExtensionPrefs* extension_prefs =
132 extensions::ExtensionPrefs::Get(profile);
133 if (!extension_prefs)
134 return false;
136 const std::string kRecoveryToolIds[] = {
137 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging
138 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
141 for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
142 const std::string extension_id = kRecoveryToolIds[i];
143 if (extension_prefs->IsExtensionRunning(extension_id))
144 return true;
147 return false;
150 // Sends an event named |event_name| with arguments |event_args| to extensions.
151 void BroadcastEvent(Profile* profile,
152 const std::string& event_name,
153 scoped_ptr<base::ListValue> event_args) {
154 extensions::EventRouter::Get(profile)->BroadcastEvent(
155 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
158 // Sends an event named |event_name| with arguments |event_args| to an extension
159 // of |extention_id|.
160 void DispatchEventToExtension(Profile* profile,
161 const std::string& extension_id,
162 const std::string& event_name,
163 scoped_ptr<base::ListValue> event_args) {
164 extensions::EventRouter::Get(profile)->DispatchEventToExtension(
165 extension_id,
166 make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
169 file_manager_private::MountCompletedStatus
170 MountErrorToMountCompletedStatus(chromeos::MountError error) {
171 switch (error) {
172 case chromeos::MOUNT_ERROR_NONE:
173 return file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS;
174 case chromeos::MOUNT_ERROR_UNKNOWN:
175 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN;
176 case chromeos::MOUNT_ERROR_INTERNAL:
177 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL;
178 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
179 return file_manager_private::
180 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT;
181 case chromeos::MOUNT_ERROR_INVALID_PATH:
182 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH;
183 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
184 return file_manager_private::
185 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED;
186 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
187 return file_manager_private::
188 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED;
189 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
190 return file_manager_private
191 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED;
192 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
193 return file_manager_private
194 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS;
195 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
196 return file_manager_private::
197 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS;
198 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
199 return file_manager_private::
200 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS;
201 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
202 return file_manager_private::
203 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND;
204 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
205 return file_manager_private::
206 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED;
207 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
208 return file_manager_private::
209 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH;
210 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
211 return file_manager_private::
212 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM;
213 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
214 return file_manager_private::
215 MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM;
216 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
217 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE;
218 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
219 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION;
220 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
221 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
223 NOTREACHED();
224 return file_manager_private::MOUNT_COMPLETED_STATUS_NONE;
227 file_manager_private::CopyProgressStatusType
228 CopyProgressTypeToCopyProgressStatusType(
229 storage::FileSystemOperation::CopyProgressType type) {
230 switch (type) {
231 case storage::FileSystemOperation::BEGIN_COPY_ENTRY:
232 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
233 case storage::FileSystemOperation::END_COPY_ENTRY:
234 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
235 case storage::FileSystemOperation::PROGRESS:
236 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
238 NOTREACHED();
239 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_NONE;
242 file_manager_private::ChangeType ConvertChangeTypeFromDriveToApi(
243 drive::FileChange::ChangeType type) {
244 switch (type) {
245 case drive::FileChange::ADD_OR_UPDATE:
246 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE;
247 case drive::FileChange::DELETE:
248 return file_manager_private::CHANGE_TYPE_DELETE;
250 NOTREACHED();
251 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE;
254 std::string FileErrorToErrorName(base::File::Error error_code) {
255 namespace js = extensions::api::file_manager_private;
256 switch (error_code) {
257 case base::File::FILE_ERROR_NOT_FOUND:
258 return "NotFoundError";
259 case base::File::FILE_ERROR_INVALID_OPERATION:
260 case base::File::FILE_ERROR_EXISTS:
261 case base::File::FILE_ERROR_NOT_EMPTY:
262 return "InvalidModificationError";
263 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
264 case base::File::FILE_ERROR_NOT_A_FILE:
265 return "TypeMismatchError";
266 case base::File::FILE_ERROR_ACCESS_DENIED:
267 return "NoModificationAllowedError";
268 case base::File::FILE_ERROR_FAILED:
269 return "InvalidStateError";
270 case base::File::FILE_ERROR_ABORT:
271 return "AbortError";
272 case base::File::FILE_ERROR_SECURITY:
273 return "SecurityError";
274 case base::File::FILE_ERROR_NO_SPACE:
275 return "QuotaExceededError";
276 case base::File::FILE_ERROR_INVALID_URL:
277 return "EncodingError";
278 default:
279 return "InvalidModificationError";
283 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
284 Profile* running_profile) {
285 extensions::ExtensionHost* const extension_host =
286 extensions::ProcessManager::Get(running_profile)
287 ->GetBackgroundHostForExtension(kFileManagerAppId);
288 if (!extension_host || !extension_host->render_process_host())
289 return;
291 const int id = extension_host->render_process_host()->GetID();
292 file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
295 // Checks if we should send a progress event or not according to the
296 // |last_time| of sending an event. If |always| is true, the function always
297 // returns true. If the function returns true, the function also updates
298 // |last_time|.
299 bool ShouldSendProgressEvent(bool always, base::Time* last_time) {
300 const base::Time now = base::Time::Now();
301 const int64 delta = (now - *last_time).InMilliseconds();
302 // delta < 0 may rarely happen if system clock is synced and rewinded.
303 // To be conservative, we don't skip in that case.
304 if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) {
305 return false;
306 } else {
307 *last_time = now;
308 return true;
312 // Obtains whether the Files.app should handle the volume or not.
313 bool ShouldShowNotificationForVolume(
314 Profile* profile,
315 const DeviceEventRouter& device_event_router,
316 const VolumeInfo& volume_info) {
317 if (volume_info.type != VOLUME_TYPE_MTP &&
318 volume_info.type != VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
319 return false;
322 if (device_event_router.is_resuming() || device_event_router.is_starting_up())
323 return false;
325 // Do not attempt to open File Manager while the login is in progress or
326 // the screen is locked or running in kiosk app mode and make sure the file
327 // manager is opened only for the active user.
328 if (chromeos::LoginDisplayHostImpl::default_host() ||
329 chromeos::ScreenLocker::default_screen_locker() ||
330 chrome::IsRunningInForcedAppMode() ||
331 profile != ProfileManager::GetActiveUserProfile()) {
332 return false;
335 // Do not pop-up the File Manager, if the recovery tool is running.
336 if (IsRecoveryToolRunning(profile))
337 return false;
339 // If the disable-default-apps flag is on, Files.app is not opened
340 // automatically on device mount not to obstruct the manual test.
341 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
342 switches::kDisableDefaultApps)) {
343 return false;
346 return true;
349 // Sub-part of the event router for handling device events.
350 class DeviceEventRouterImpl : public DeviceEventRouter {
351 public:
352 explicit DeviceEventRouterImpl(Profile* profile) : profile_(profile) {}
354 // DeviceEventRouter overrides.
355 void OnDeviceEvent(file_manager_private::DeviceEventType type,
356 const std::string& device_path) override {
357 DCHECK_CURRENTLY_ON(BrowserThread::UI);
359 file_manager_private::DeviceEvent event;
360 event.type = type;
361 event.device_path = device_path;
363 BroadcastEvent(profile_,
364 file_manager_private::OnDeviceChanged::kEventName,
365 file_manager_private::OnDeviceChanged::Create(event));
368 // DeviceEventRouter overrides.
369 bool IsExternalStorageDisabled() override {
370 DCHECK_CURRENTLY_ON(BrowserThread::UI);
371 return profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled);
374 private:
375 Profile* const profile_;
377 DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl);
380 } // namespace
382 // Pass dummy value to JobInfo's constructor for make it default constructible.
383 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
384 : job_info(drive::TYPE_DOWNLOAD_FILE) {
387 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
388 const drive::JobInfo& info, const std::string& status)
389 : job_info(info), status(status) {
392 EventRouter::EventRouter(Profile* profile)
393 : pref_change_registrar_(new PrefChangeRegistrar),
394 profile_(profile),
395 device_event_router_(new DeviceEventRouterImpl(profile)),
396 dispatch_directory_change_event_impl_(
397 base::Bind(&EventRouter::DispatchDirectoryChangeEventImpl,
398 base::Unretained(this))),
399 weak_factory_(this) {
400 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
401 ObserveEvents();
404 EventRouter::~EventRouter() {
407 void EventRouter::Shutdown() {
408 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410 DLOG_IF(WARNING, !file_watchers_.empty())
411 << "Not all file watchers are "
412 << "removed. This can happen when Files.app is open during shutdown.";
413 STLDeleteValues(&file_watchers_);
414 if (!profile_) {
415 NOTREACHED();
416 return;
419 pref_change_registrar_->RemoveAll();
421 if (NetworkHandler::IsInitialized()) {
422 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
423 FROM_HERE);
426 DriveIntegrationService* const integration_service =
427 DriveIntegrationServiceFactory::FindForProfile(profile_);
428 if (integration_service) {
429 integration_service->file_system()->RemoveObserver(this);
430 integration_service->drive_service()->RemoveObserver(this);
431 integration_service->job_list()->RemoveObserver(this);
434 VolumeManager* const volume_manager = VolumeManager::Get(profile_);
435 if (volume_manager) {
436 volume_manager->RemoveObserver(this);
437 volume_manager->RemoveObserver(device_event_router_.get());
440 chromeos::PowerManagerClient* const power_manager_client =
441 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
442 power_manager_client->RemoveObserver(device_event_router_.get());
444 profile_ = NULL;
447 void EventRouter::ObserveEvents() {
448 if (!profile_) {
449 NOTREACHED();
450 return;
452 if (!chromeos::LoginState::IsInitialized() ||
453 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
454 return;
457 // Ignore device events for the first few seconds.
458 device_event_router_->Startup();
460 // VolumeManager's construction triggers DriveIntegrationService's
461 // construction, so it is necessary to call VolumeManager's Get before
462 // accessing DriveIntegrationService.
463 VolumeManager* const volume_manager = VolumeManager::Get(profile_);
464 if (volume_manager) {
465 volume_manager->AddObserver(this);
466 volume_manager->AddObserver(device_event_router_.get());
469 chromeos::PowerManagerClient* const power_manager_client =
470 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
471 power_manager_client->AddObserver(device_event_router_.get());
473 DriveIntegrationService* const integration_service =
474 DriveIntegrationServiceFactory::FindForProfile(profile_);
475 if (integration_service) {
476 integration_service->drive_service()->AddObserver(this);
477 integration_service->file_system()->AddObserver(this);
478 integration_service->job_list()->AddObserver(this);
481 if (NetworkHandler::IsInitialized()) {
482 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
483 FROM_HERE);
486 pref_change_registrar_->Init(profile_->GetPrefs());
487 base::Closure callback =
488 base::Bind(&EventRouter::OnFileManagerPrefsChanged,
489 weak_factory_.GetWeakPtr());
490 pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
491 pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
492 pref_change_registrar_->Add(prefs::kDisableDrive, callback);
493 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
495 notification_registrar_.Add(this,
496 chrome::NOTIFICATION_PROFILE_ADDED,
497 content::NotificationService::AllSources());
500 // File watch setup routines.
501 void EventRouter::AddFileWatch(const base::FilePath& local_path,
502 const base::FilePath& virtual_path,
503 const std::string& extension_id,
504 const BoolCallback& callback) {
505 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
506 DCHECK(!callback.is_null());
508 base::FilePath watch_path = local_path;
509 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
510 // Tweak watch path for remote sources - we need to drop leading /special
511 // directory from there in order to be able to pair these events with
512 // their change notifications.
513 if (is_on_drive)
514 watch_path = drive::util::ExtractDrivePath(watch_path);
516 WatcherMap::iterator iter = file_watchers_.find(watch_path);
517 if (iter == file_watchers_.end()) {
518 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
519 watcher->AddExtension(extension_id);
521 if (is_on_drive) {
522 // For Drive, file watching is done via OnDirectoryChanged().
523 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
524 base::Bind(callback, true));
525 } else {
526 // For local files, start watching using FileWatcher.
527 watcher->WatchLocalFile(
528 watch_path,
529 base::Bind(&EventRouter::HandleFileWatchNotification,
530 weak_factory_.GetWeakPtr(),
531 static_cast<drive::FileChange*>(NULL)),
532 callback);
535 file_watchers_[watch_path] = watcher.release();
536 } else {
537 iter->second->AddExtension(extension_id);
538 base::MessageLoopProxy::current()->PostTask(FROM_HERE,
539 base::Bind(callback, true));
543 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
544 const std::string& extension_id) {
545 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
547 base::FilePath watch_path = local_path;
548 // Tweak watch path for remote sources - we need to drop leading /special
549 // directory from there in order to be able to pair these events with
550 // their change notifications.
551 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
552 watch_path = drive::util::ExtractDrivePath(watch_path);
554 WatcherMap::iterator iter = file_watchers_.find(watch_path);
555 if (iter == file_watchers_.end())
556 return;
557 // Remove the watcher if |watch_path| is no longer watched by any extensions.
558 iter->second->RemoveExtension(extension_id);
559 if (iter->second->GetExtensionIds().empty()) {
560 delete iter->second;
561 file_watchers_.erase(iter);
565 void EventRouter::OnCopyCompleted(int copy_id,
566 const GURL& source_url,
567 const GURL& destination_url,
568 base::File::Error error) {
569 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
571 file_manager_private::CopyProgressStatus status;
572 if (error == base::File::FILE_OK) {
573 // Send success event.
574 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
575 status.source_url.reset(new std::string(source_url.spec()));
576 status.destination_url.reset(new std::string(destination_url.spec()));
577 } else {
578 // Send error event.
579 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
580 status.error.reset(new std::string(FileErrorToErrorName(error)));
583 BroadcastEvent(
584 profile_,
585 file_manager_private::OnCopyProgress::kEventName,
586 file_manager_private::OnCopyProgress::Create(copy_id, status));
589 void EventRouter::OnCopyProgress(
590 int copy_id,
591 storage::FileSystemOperation::CopyProgressType type,
592 const GURL& source_url,
593 const GURL& destination_url,
594 int64 size) {
595 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
597 file_manager_private::CopyProgressStatus status;
598 status.type = CopyProgressTypeToCopyProgressStatusType(type);
599 status.source_url.reset(new std::string(source_url.spec()));
600 if (type == storage::FileSystemOperation::END_COPY_ENTRY)
601 status.destination_url.reset(new std::string(destination_url.spec()));
602 if (type == storage::FileSystemOperation::PROGRESS)
603 status.size.reset(new double(size));
605 // Should not skip events other than TYPE_PROGRESS.
606 const bool always =
607 status.type != file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
608 if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
609 return;
611 BroadcastEvent(
612 profile_,
613 file_manager_private::OnCopyProgress::kEventName,
614 file_manager_private::OnCopyProgress::Create(copy_id, status));
617 void EventRouter::OnWatcherManagerNotification(
618 const storage::FileSystemURL& file_system_url,
619 const std::string& extension_id,
620 storage::WatcherManager::ChangeType /* change_type */) {
621 std::vector<std::string> extension_ids;
622 extension_ids.push_back(extension_id);
624 DispatchDirectoryChangeEvent(file_system_url.virtual_path(), NULL,
625 false /* error */, extension_ids);
628 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
629 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
630 NOTREACHED();
631 return;
634 BroadcastEvent(
635 profile_,
636 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
637 file_manager_private::OnDriveConnectionStatusChanged::Create());
640 void EventRouter::OnFileManagerPrefsChanged() {
641 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
642 NOTREACHED();
643 return;
646 BroadcastEvent(
647 profile_,
648 file_manager_private::OnPreferencesChanged::kEventName,
649 file_manager_private::OnPreferencesChanged::Create());
652 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
653 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
654 if (!drive::IsActiveFileTransferJobInfo(job_info))
655 return;
656 ScheduleDriveFileTransferEvent(
657 job_info, kFileTransferStateAdded, false /* immediate */);
660 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
661 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
662 if (!drive::IsActiveFileTransferJobInfo(job_info))
663 return;
665 bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
667 const std::string status =
668 is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress;
670 // Replace with the latest job info.
671 drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(job_info, status);
673 ScheduleDriveFileTransferEvent(job_info, status, false /* immediate */);
676 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
677 drive::FileError error) {
678 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
679 if (!drive::IsActiveFileTransferJobInfo(job_info))
680 return;
682 const std::string status = error == drive::FILE_ERROR_OK
683 ? kFileTransferStateCompleted
684 : kFileTransferStateFailed;
686 ScheduleDriveFileTransferEvent(job_info, status, true /* immediate */);
688 // Forget about the job.
689 drive_jobs_.erase(job_info.job_id);
692 void EventRouter::ScheduleDriveFileTransferEvent(const drive::JobInfo& job_info,
693 const std::string& status,
694 bool immediate) {
695 const bool no_pending_task = !drive_job_info_for_scheduled_event_;
696 // Update the latest event.
697 drive_job_info_for_scheduled_event_.reset(
698 new DriveJobInfoWithStatus(job_info, status));
699 if (immediate) {
700 SendDriveFileTransferEvent();
701 } else if (no_pending_task) {
702 const base::TimeDelta delay = base::TimeDelta::FromMilliseconds(
703 kFileTransferEventDelayTimeInMilliseconds);
704 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
705 FROM_HERE,
706 base::Bind(&EventRouter::SendDriveFileTransferEvent,
707 weak_factory_.GetWeakPtr()),
708 delay);
712 void EventRouter::SendDriveFileTransferEvent() {
713 if (!drive_job_info_for_scheduled_event_)
714 return;
716 file_manager_private::FileTransferStatus status;
717 JobInfoToTransferStatus(profile_,
718 kFileManagerAppId,
719 drive_job_info_for_scheduled_event_->status,
720 drive_job_info_for_scheduled_event_->job_info,
721 &status);
723 drive_job_info_for_scheduled_event_.reset();
725 BroadcastEvent(profile_,
726 file_manager_private::OnFileTransfersUpdated::kEventName,
727 file_manager_private::OnFileTransfersUpdated::Create(status));
730 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
731 HandleFileWatchNotification(NULL, drive_path, false);
734 void EventRouter::OnFileChanged(const drive::FileChange& changed_files) {
735 // In this method, we convert changed_files to a map which can be handled by
736 // HandleFileWatchNotification.
738 // e.g.
739 // /a/b DIRECTORY:DELETE
741 // map[/a] = /a/b DIRECTORY:DELETE
742 // map[/a/b] = /a/b DIRECTORY:DELETE
744 // We used the key of map to match the watched directories of file watchers.
745 typedef std::map<base::FilePath, drive::FileChange> FileChangeMap;
746 typedef drive::FileChange::ChangeList::List FileChangeList;
748 FileChangeMap map;
749 const drive::FileChange::Map& changed_file_map = changed_files.map();
750 for (auto const& file_change_key_value : changed_file_map) {
751 // Check whether the FileChangeList contains directory deletion.
752 bool contains_directory_deletion = false;
753 const FileChangeList list = file_change_key_value.second.list();
754 for (drive::FileChange::Change const& change : list) {
755 if (change.IsDirectory() && change.IsDelete()) {
756 contains_directory_deletion = true;
757 break;
761 const base::FilePath& path = file_change_key_value.first;
762 map[path.DirName()].Update(path, file_change_key_value.second);
764 // For deletion of a directory, onFileChanged gets different changed_files.
765 // We solve the difference here.
767 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
768 // 1. /a/b DELETE:DIRECTORY
769 // 2. /a DELETE:DIRECTORY
771 // /a/b is watched, and /a is deleted from Files.app.
772 // 1. /a DELETE:DIRECTORY
773 if (contains_directory_deletion) {
774 // Expand the deleted directory path with watched paths.
775 for (WatcherMap::const_iterator file_watchers_it =
776 file_watchers_.lower_bound(path);
777 file_watchers_it != file_watchers_.end(); ++file_watchers_it) {
778 if (path == file_watchers_it->first ||
779 path.IsParent(file_watchers_it->first)) {
780 map[file_watchers_it->first].Update(
781 file_watchers_it->first,
782 drive::FileChange::FileType::FILE_TYPE_DIRECTORY,
783 drive::FileChange::ChangeType::DELETE);
789 for (auto const& file_change_key_value : map) {
790 HandleFileWatchNotification(&(file_change_key_value.second),
791 file_change_key_value.first, false);
795 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
796 const base::FilePath& drive_path) {
797 file_manager_private::DriveSyncErrorEvent event;
798 switch (type) {
799 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
800 event.type =
801 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
802 break;
803 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
804 event.type =
805 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
806 break;
807 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
808 event.type =
809 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC;
810 break;
812 event.file_url = util::ConvertDrivePathToFileSystemUrl(
813 profile_, drive_path, kFileManagerAppId).spec();
814 BroadcastEvent(
815 profile_,
816 file_manager_private::OnDriveSyncError::kEventName,
817 file_manager_private::OnDriveSyncError::Create(event));
820 void EventRouter::OnRefreshTokenInvalid() {
821 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
823 // Raise a DriveConnectionStatusChanged event to notify the status offline.
824 BroadcastEvent(
825 profile_,
826 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
827 file_manager_private::OnDriveConnectionStatusChanged::Create());
830 void EventRouter::OnReadyToSendRequests() {
831 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
833 // Raise a DriveConnectionStatusChanged event to notify the status online.
834 BroadcastEvent(
835 profile_,
836 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
837 file_manager_private::OnDriveConnectionStatusChanged::Create());
840 void EventRouter::HandleFileWatchNotification(const drive::FileChange* list,
841 const base::FilePath& local_path,
842 bool got_error) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
845 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
846 if (iter == file_watchers_.end()) {
847 return;
850 if (list && list->size() > kDirectoryChangeEventMaxDetailInfoSize) {
851 // Removes the detailed information, if the list size is more than
852 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
853 // and processing it may cause more itme.
854 // This will be invoked full-refresh in Files.app.
855 list = NULL;
858 DispatchDirectoryChangeEvent(iter->second->virtual_path(),
859 list,
860 got_error,
861 iter->second->GetExtensionIds());
864 void EventRouter::DispatchDirectoryChangeEvent(
865 const base::FilePath& virtual_path,
866 const drive::FileChange* list,
867 bool got_error,
868 const std::vector<std::string>& extension_ids) {
869 dispatch_directory_change_event_impl_.Run(virtual_path, list, got_error,
870 extension_ids);
873 void EventRouter::DispatchDirectoryChangeEventImpl(
874 const base::FilePath& virtual_path,
875 const drive::FileChange* list,
876 bool got_error,
877 const std::vector<std::string>& extension_ids) {
878 if (!profile_) {
879 NOTREACHED();
880 return;
882 linked_ptr<drive::FileChange> changes;
883 if (list)
884 changes.reset(new drive::FileChange(*list)); // Copy
886 for (size_t i = 0; i < extension_ids.size(); ++i) {
887 std::string* extension_id = new std::string(extension_ids[i]);
889 FileDefinition file_definition;
890 file_definition.virtual_path = virtual_path;
891 // TODO(mtomasz): Add support for watching files in File System Provider
892 // API.
893 file_definition.is_directory = true;
895 file_manager::util::ConvertFileDefinitionToEntryDefinition(
896 profile_,
897 *extension_id,
898 file_definition,
899 base::Bind(
900 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
901 weak_factory_.GetWeakPtr(),
902 changes,
903 base::Owned(extension_id),
904 got_error));
908 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
909 const linked_ptr<drive::FileChange> list,
910 const std::string* extension_id,
911 bool watcher_error,
912 const EntryDefinition& entry_definition) {
913 // TODO(mtomasz): Add support for watching files in File System Provider API.
914 if (entry_definition.error != base::File::FILE_OK ||
915 !entry_definition.is_directory) {
916 DVLOG(1) << "Unable to dispatch event because resolving the directory "
917 << "entry definition failed.";
918 return;
921 file_manager_private::FileWatchEvent event;
922 event.event_type = watcher_error
923 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
924 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED;
926 // Detailed information is available.
927 if (list.get()) {
928 event.changed_files.reset(
929 new std::vector<linked_ptr<file_manager_private::FileChange> >);
931 if (list->map().empty())
932 return;
934 for (drive::FileChange::Map::const_iterator it = list->map().begin();
935 it != list->map().end();
936 it++) {
937 linked_ptr<file_manager_private::FileChange> change_list(
938 new file_manager_private::FileChange);
940 GURL url = util::ConvertDrivePathToFileSystemUrl(
941 profile_, it->first, *extension_id);
942 change_list->url = url.spec();
944 for (drive::FileChange::ChangeList::List::const_iterator change =
945 it->second.list().begin();
946 change != it->second.list().end();
947 change++) {
948 change_list->changes.push_back(
949 ConvertChangeTypeFromDriveToApi(change->change()));
952 event.changed_files->push_back(change_list);
956 event.entry.additional_properties.SetString(
957 "fileSystemName", entry_definition.file_system_name);
958 event.entry.additional_properties.SetString(
959 "fileSystemRoot", entry_definition.file_system_root_url);
960 event.entry.additional_properties.SetString(
961 "fileFullPath", "/" + entry_definition.full_path.value());
962 event.entry.additional_properties.SetBoolean("fileIsDirectory",
963 entry_definition.is_directory);
965 DispatchEventToExtension(
966 profile_,
967 *extension_id,
968 file_manager_private::OnDirectoryChanged::kEventName,
969 file_manager_private::OnDirectoryChanged::Create(event));
972 void EventRouter::OnDiskAdded(
973 const DiskMountManager::Disk& disk, bool mounting) {
974 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
975 // Do nothing.
978 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
980 // Do nothing.
983 void EventRouter::OnDeviceAdded(const std::string& device_path) {
984 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
985 // Do nothing.
988 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
989 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
990 // Do nothing.
993 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
994 const VolumeInfo& volume_info) {
995 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
996 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
997 // happen at shutdown. This should be removed after removing Drive mounting
998 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
999 // the only path to come here after Shutdown is called).
1000 if (!profile_)
1001 return;
1003 DispatchMountCompletedEvent(
1004 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
1005 error_code,
1006 volume_info);
1009 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
1010 const VolumeInfo& volume_info) {
1011 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1012 DispatchMountCompletedEvent(
1013 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
1014 error_code,
1015 volume_info);
1018 void EventRouter::DispatchMountCompletedEvent(
1019 file_manager_private::MountCompletedEventType event_type,
1020 chromeos::MountError error,
1021 const VolumeInfo& volume_info) {
1022 // Build an event object.
1023 file_manager_private::MountCompletedEvent event;
1024 event.event_type = event_type;
1025 event.status = MountErrorToMountCompletedStatus(error);
1026 util::VolumeInfoToVolumeMetadata(
1027 profile_, volume_info, &event.volume_metadata);
1028 event.should_notify = ShouldShowNotificationForVolume(
1029 profile_, *device_event_router_, volume_info);
1030 BroadcastEvent(profile_,
1031 file_manager_private::OnMountCompleted::kEventName,
1032 file_manager_private::OnMountCompleted::Create(event));
1035 void EventRouter::OnFormatStarted(const std::string& device_path,
1036 bool success) {
1037 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1038 // Do nothing.
1041 void EventRouter::OnFormatCompleted(const std::string& device_path,
1042 bool success) {
1043 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1044 // Do nothing.
1047 void EventRouter::Observe(int type,
1048 const content::NotificationSource& source,
1049 const content::NotificationDetails& details) {
1050 if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
1051 Profile* const added_profile = content::Source<Profile>(source).ptr();
1052 if (!added_profile->IsOffTheRecord())
1053 GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
1057 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
1058 const DispatchDirectoryChangeEventImplCallback& callback) {
1059 dispatch_directory_change_event_impl_ = callback;
1062 base::WeakPtr<EventRouter> EventRouter::GetWeakPtr() {
1063 return weak_factory_.GetWeakPtr();
1066 } // namespace file_manager