Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / chromeos / extensions / file_manager / event_router.cc
blobb4041533c7c27ec50f206886cb382b356c01db86
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/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/thread_task_runner_handle.h"
14 #include "base/threading/sequenced_worker_pool.h"
15 #include "base/values.h"
16 #include "chrome/browser/app_mode/app_mode_utils.h"
17 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
18 #include "chrome/browser/chromeos/drive/file_system_util.h"
19 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
20 #include "chrome/browser/chromeos/file_manager/app_id.h"
21 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
22 #include "chrome/browser/chromeos/file_manager/open_util.h"
23 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
24 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
25 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
26 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
27 #include "chrome/browser/extensions/extension_service.h"
28 #include "chrome/browser/extensions/extension_util.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/pref_names.h"
33 #include "chromeos/dbus/dbus_thread_manager.h"
34 #include "chromeos/login/login_state.h"
35 #include "chromeos/network/network_handler.h"
36 #include "chromeos/network/network_state_handler.h"
37 #include "components/drive/drive_pref_names.h"
38 #include "components/drive/file_change.h"
39 #include "components/drive/file_system_interface.h"
40 #include "components/drive/service/drive_service_interface.h"
41 #include "content/public/browser/browser_thread.h"
42 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/storage_partition.h"
44 #include "extensions/browser/event_router.h"
45 #include "extensions/browser/extension_host.h"
46 #include "extensions/browser/extension_prefs.h"
47 #include "storage/common/fileapi/file_system_types.h"
48 #include "storage/common/fileapi/file_system_util.h"
50 using chromeos::disks::DiskMountManager;
51 using chromeos::NetworkHandler;
52 using content::BrowserThread;
53 using drive::DriveIntegrationService;
54 using drive::DriveIntegrationServiceFactory;
55 using file_manager::util::EntryDefinition;
56 using file_manager::util::FileDefinition;
58 namespace file_manager_private = extensions::api::file_manager_private;
60 namespace file_manager {
61 namespace {
63 // Frequency of sending onFileTransferUpdated.
64 const int64 kProgressEventFrequencyInMilliseconds = 1000;
66 // Maximim size of detailed change info on directory change event. If the size
67 // exceeds the maximum size, the detailed info is omitted and the force refresh
68 // is kicked.
69 const size_t kDirectoryChangeEventMaxDetailInfoSize = 1000;
71 // This time(millisecond) is used for confirm following event exists.
72 const int64 kFileTransferEventDelayTimeInMilliseconds = 300;
74 // Checks if the Recovery Tool is running. This is a temporary solution.
75 // TODO(mtomasz): Replace with crbug.com/341902 solution.
76 bool IsRecoveryToolRunning(Profile* profile) {
77 extensions::ExtensionPrefs* extension_prefs =
78 extensions::ExtensionPrefs::Get(profile);
79 if (!extension_prefs)
80 return false;
82 const std::string kRecoveryToolIds[] = {
83 "kkebgepbbgbcmghedmmdfcbdcodlkngh", // Recovery tool staging
84 "jndclpdbaamdhonoechobihbbiimdgai" // Recovery tool prod
87 for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
88 const std::string extension_id = kRecoveryToolIds[i];
89 if (extension_prefs->IsExtensionRunning(extension_id))
90 return true;
93 return false;
96 // Sends an event named |event_name| with arguments |event_args| to extensions.
97 void BroadcastEvent(Profile* profile,
98 extensions::events::HistogramValue histogram_value,
99 const std::string& event_name,
100 scoped_ptr<base::ListValue> event_args) {
101 extensions::EventRouter::Get(profile)->BroadcastEvent(make_scoped_ptr(
102 new extensions::Event(histogram_value, event_name, event_args.Pass())));
105 // Sends an event named |event_name| with arguments |event_args| to an extension
106 // of |extention_id|.
107 void DispatchEventToExtension(
108 Profile* profile,
109 const std::string& extension_id,
110 extensions::events::HistogramValue histogram_value,
111 const std::string& event_name,
112 scoped_ptr<base::ListValue> event_args) {
113 extensions::EventRouter::Get(profile)->DispatchEventToExtension(
114 extension_id, make_scoped_ptr(new extensions::Event(
115 histogram_value, event_name, event_args.Pass())));
118 file_manager_private::MountCompletedStatus
119 MountErrorToMountCompletedStatus(chromeos::MountError error) {
120 switch (error) {
121 case chromeos::MOUNT_ERROR_NONE:
122 return file_manager_private::MOUNT_COMPLETED_STATUS_SUCCESS;
123 case chromeos::MOUNT_ERROR_UNKNOWN:
124 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN;
125 case chromeos::MOUNT_ERROR_INTERNAL:
126 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL;
127 case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
128 return file_manager_private::
129 MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT;
130 case chromeos::MOUNT_ERROR_INVALID_PATH:
131 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH;
132 case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
133 return file_manager_private::
134 MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED;
135 case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
136 return file_manager_private::
137 MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED;
138 case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
139 return file_manager_private
140 ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED;
141 case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
142 return file_manager_private
143 ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS;
144 case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
145 return file_manager_private::
146 MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS;
147 case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
148 return file_manager_private::
149 MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS;
150 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
151 return file_manager_private::
152 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND;
153 case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
154 return file_manager_private::
155 MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED;
156 case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
157 return file_manager_private::
158 MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH;
159 case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
160 return file_manager_private::
161 MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM;
162 case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
163 return file_manager_private::
164 MOUNT_COMPLETED_STATUS_ERROR_UNSUPPORTED_FILESYSTEM;
165 case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
166 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE;
167 case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
168 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION;
169 case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
170 return file_manager_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
172 NOTREACHED();
173 return file_manager_private::MOUNT_COMPLETED_STATUS_NONE;
176 file_manager_private::CopyProgressStatusType
177 CopyProgressTypeToCopyProgressStatusType(
178 storage::FileSystemOperation::CopyProgressType type) {
179 switch (type) {
180 case storage::FileSystemOperation::BEGIN_COPY_ENTRY:
181 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
182 case storage::FileSystemOperation::END_COPY_ENTRY:
183 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
184 case storage::FileSystemOperation::PROGRESS:
185 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
186 case storage::FileSystemOperation::ERROR_COPY_ENTRY:
187 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
189 NOTREACHED();
190 return file_manager_private::COPY_PROGRESS_STATUS_TYPE_NONE;
193 file_manager_private::ChangeType ConvertChangeTypeFromDriveToApi(
194 drive::FileChange::ChangeType type) {
195 switch (type) {
196 case drive::FileChange::CHANGE_TYPE_ADD_OR_UPDATE:
197 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE;
198 case drive::FileChange::CHANGE_TYPE_DELETE:
199 return file_manager_private::CHANGE_TYPE_DELETE;
201 NOTREACHED();
202 return file_manager_private::CHANGE_TYPE_ADD_OR_UPDATE;
205 std::string FileErrorToErrorName(base::File::Error error_code) {
206 namespace js = extensions::api::file_manager_private;
207 switch (error_code) {
208 case base::File::FILE_ERROR_NOT_FOUND:
209 return "NotFoundError";
210 case base::File::FILE_ERROR_INVALID_OPERATION:
211 case base::File::FILE_ERROR_EXISTS:
212 case base::File::FILE_ERROR_NOT_EMPTY:
213 return "InvalidModificationError";
214 case base::File::FILE_ERROR_NOT_A_DIRECTORY:
215 case base::File::FILE_ERROR_NOT_A_FILE:
216 return "TypeMismatchError";
217 case base::File::FILE_ERROR_ACCESS_DENIED:
218 return "NoModificationAllowedError";
219 case base::File::FILE_ERROR_FAILED:
220 return "InvalidStateError";
221 case base::File::FILE_ERROR_ABORT:
222 return "AbortError";
223 case base::File::FILE_ERROR_SECURITY:
224 return "SecurityError";
225 case base::File::FILE_ERROR_NO_SPACE:
226 return "QuotaExceededError";
227 case base::File::FILE_ERROR_INVALID_URL:
228 return "EncodingError";
229 default:
230 return "InvalidModificationError";
234 // Checks if we should send a progress event or not according to the
235 // |last_time| of sending an event. If |always| is true, the function always
236 // returns true. If the function returns true, the function also updates
237 // |last_time|.
238 bool ShouldSendProgressEvent(bool always, base::Time* last_time) {
239 const base::Time now = base::Time::Now();
240 const int64 delta = (now - *last_time).InMilliseconds();
241 // delta < 0 may rarely happen if system clock is synced and rewinded.
242 // To be conservative, we don't skip in that case.
243 if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) {
244 return false;
245 } else {
246 *last_time = now;
247 return true;
251 // Obtains whether the Files.app should handle the volume or not.
252 bool ShouldShowNotificationForVolume(
253 Profile* profile,
254 const DeviceEventRouter& device_event_router,
255 const Volume& volume) {
256 if (volume.type() != VOLUME_TYPE_MTP &&
257 volume.type() != VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
258 return false;
261 if (device_event_router.is_resuming() || device_event_router.is_starting_up())
262 return false;
264 // Do not attempt to open File Manager while the login is in progress or
265 // the screen is locked or running in kiosk app mode and make sure the file
266 // manager is opened only for the active user.
267 if (chromeos::LoginDisplayHostImpl::default_host() ||
268 chromeos::ScreenLocker::default_screen_locker() ||
269 chrome::IsRunningInForcedAppMode() ||
270 profile != ProfileManager::GetActiveUserProfile()) {
271 return false;
274 // Do not pop-up the File Manager, if the recovery tool is running.
275 if (IsRecoveryToolRunning(profile))
276 return false;
278 // If the disable-default-apps flag is on, Files.app is not opened
279 // automatically on device mount not to obstruct the manual test.
280 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
281 switches::kDisableDefaultApps)) {
282 return false;
285 return true;
288 // Sub-part of the event router for handling device events.
289 class DeviceEventRouterImpl : public DeviceEventRouter {
290 public:
291 explicit DeviceEventRouterImpl(Profile* profile) : profile_(profile) {}
293 // DeviceEventRouter overrides.
294 void OnDeviceEvent(file_manager_private::DeviceEventType type,
295 const std::string& device_path) override {
296 DCHECK_CURRENTLY_ON(BrowserThread::UI);
298 file_manager_private::DeviceEvent event;
299 event.type = type;
300 event.device_path = device_path;
302 BroadcastEvent(profile_,
303 extensions::events::FILE_MANAGER_PRIVATE_ON_DEVICE_CHANGED,
304 file_manager_private::OnDeviceChanged::kEventName,
305 file_manager_private::OnDeviceChanged::Create(event));
308 // DeviceEventRouter overrides.
309 bool IsExternalStorageDisabled() override {
310 DCHECK_CURRENTLY_ON(BrowserThread::UI);
311 return profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled);
314 private:
315 Profile* const profile_;
317 DISALLOW_COPY_AND_ASSIGN(DeviceEventRouterImpl);
320 class JobEventRouterImpl : public JobEventRouter {
321 public:
322 explicit JobEventRouterImpl(Profile* profile)
323 : JobEventRouter(base::TimeDelta::FromMilliseconds(
324 kFileTransferEventDelayTimeInMilliseconds)),
325 profile_(profile) {}
327 protected:
328 GURL ConvertDrivePathToFileSystemUrl(const base::FilePath& path,
329 const std::string& id) const override {
330 return file_manager::util::ConvertDrivePathToFileSystemUrl(profile_, path,
331 id);
333 void BroadcastEvent(extensions::events::HistogramValue histogram_value,
334 const std::string& event_name,
335 scoped_ptr<base::ListValue> event_args) override {
336 ::file_manager::BroadcastEvent(profile_, histogram_value, event_name,
337 event_args.Pass());
340 private:
341 Profile* const profile_;
343 DISALLOW_COPY_AND_ASSIGN(JobEventRouterImpl);
346 } // namespace
348 EventRouter::EventRouter(Profile* profile)
349 : pref_change_registrar_(new PrefChangeRegistrar),
350 profile_(profile),
351 device_event_router_(new DeviceEventRouterImpl(profile)),
352 job_event_router_(new JobEventRouterImpl(profile)),
353 dispatch_directory_change_event_impl_(
354 base::Bind(&EventRouter::DispatchDirectoryChangeEventImpl,
355 base::Unretained(this))),
356 weak_factory_(this) {
357 DCHECK_CURRENTLY_ON(BrowserThread::UI);
358 ObserveEvents();
361 EventRouter::~EventRouter() {
364 void EventRouter::Shutdown() {
365 DCHECK_CURRENTLY_ON(BrowserThread::UI);
367 DLOG_IF(WARNING, !file_watchers_.empty())
368 << "Not all file watchers are "
369 << "removed. This can happen when Files.app is open during shutdown.";
370 STLDeleteValues(&file_watchers_);
371 if (!profile_) {
372 NOTREACHED();
373 return;
376 pref_change_registrar_->RemoveAll();
378 if (NetworkHandler::IsInitialized()) {
379 NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
380 FROM_HERE);
383 DriveIntegrationService* const integration_service =
384 DriveIntegrationServiceFactory::FindForProfile(profile_);
385 if (integration_service) {
386 integration_service->file_system()->RemoveObserver(this);
387 integration_service->drive_service()->RemoveObserver(this);
388 integration_service->job_list()->RemoveObserver(job_event_router_.get());
391 VolumeManager* const volume_manager = VolumeManager::Get(profile_);
392 if (volume_manager) {
393 volume_manager->RemoveObserver(this);
394 volume_manager->RemoveObserver(device_event_router_.get());
397 chromeos::PowerManagerClient* const power_manager_client =
398 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
399 power_manager_client->RemoveObserver(device_event_router_.get());
401 profile_ = NULL;
404 void EventRouter::ObserveEvents() {
405 if (!profile_) {
406 NOTREACHED();
407 return;
409 if (!chromeos::LoginState::IsInitialized() ||
410 !chromeos::LoginState::Get()->IsUserLoggedIn()) {
411 return;
414 // Ignore device events for the first few seconds.
415 device_event_router_->Startup();
417 // VolumeManager's construction triggers DriveIntegrationService's
418 // construction, so it is necessary to call VolumeManager's Get before
419 // accessing DriveIntegrationService.
420 VolumeManager* const volume_manager = VolumeManager::Get(profile_);
421 if (volume_manager) {
422 volume_manager->AddObserver(this);
423 volume_manager->AddObserver(device_event_router_.get());
426 chromeos::PowerManagerClient* const power_manager_client =
427 chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
428 power_manager_client->AddObserver(device_event_router_.get());
430 DriveIntegrationService* const integration_service =
431 DriveIntegrationServiceFactory::FindForProfile(profile_);
432 if (integration_service) {
433 integration_service->drive_service()->AddObserver(this);
434 integration_service->file_system()->AddObserver(this);
435 integration_service->job_list()->AddObserver(job_event_router_.get());
438 if (NetworkHandler::IsInitialized()) {
439 NetworkHandler::Get()->network_state_handler()->AddObserver(this,
440 FROM_HERE);
443 pref_change_registrar_->Init(profile_->GetPrefs());
444 base::Closure callback =
445 base::Bind(&EventRouter::OnFileManagerPrefsChanged,
446 weak_factory_.GetWeakPtr());
447 pref_change_registrar_->Add(drive::prefs::kDisableDriveOverCellular,
448 callback);
449 pref_change_registrar_->Add(drive::prefs::kDisableDriveHostedFiles, callback);
450 pref_change_registrar_->Add(drive::prefs::kDisableDrive, callback);
451 pref_change_registrar_->Add(prefs::kSearchSuggestEnabled, callback);
452 pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
454 chromeos::system::TimezoneSettings::GetInstance()->AddObserver(this);
457 // File watch setup routines.
458 void EventRouter::AddFileWatch(const base::FilePath& local_path,
459 const base::FilePath& virtual_path,
460 const std::string& extension_id,
461 const BoolCallback& callback) {
462 DCHECK_CURRENTLY_ON(BrowserThread::UI);
463 DCHECK(!callback.is_null());
465 base::FilePath watch_path = local_path;
466 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
467 // Tweak watch path for remote sources - we need to drop leading /special
468 // directory from there in order to be able to pair these events with
469 // their change notifications.
470 if (is_on_drive)
471 watch_path = drive::util::ExtractDrivePath(watch_path);
473 WatcherMap::iterator iter = file_watchers_.find(watch_path);
474 if (iter == file_watchers_.end()) {
475 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
476 watcher->AddExtension(extension_id);
478 if (is_on_drive) {
479 // For Drive, file watching is done via OnDirectoryChanged().
480 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
481 base::Bind(callback, true));
482 } else {
483 // For local files, start watching using FileWatcher.
484 watcher->WatchLocalFile(
485 watch_path,
486 base::Bind(&EventRouter::HandleFileWatchNotification,
487 weak_factory_.GetWeakPtr(),
488 static_cast<drive::FileChange*>(NULL)),
489 callback);
492 file_watchers_[watch_path] = watcher.release();
493 } else {
494 iter->second->AddExtension(extension_id);
495 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
496 base::Bind(callback, true));
500 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
501 const std::string& extension_id) {
502 DCHECK_CURRENTLY_ON(BrowserThread::UI);
504 base::FilePath watch_path = local_path;
505 // Tweak watch path for remote sources - we need to drop leading /special
506 // directory from there in order to be able to pair these events with
507 // their change notifications.
508 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
509 watch_path = drive::util::ExtractDrivePath(watch_path);
511 WatcherMap::iterator iter = file_watchers_.find(watch_path);
512 if (iter == file_watchers_.end())
513 return;
514 // Remove the watcher if |watch_path| is no longer watched by any extensions.
515 iter->second->RemoveExtension(extension_id);
516 if (iter->second->GetExtensionIds().empty()) {
517 delete iter->second;
518 file_watchers_.erase(iter);
522 void EventRouter::OnCopyCompleted(int copy_id,
523 const GURL& source_url,
524 const GURL& destination_url,
525 base::File::Error error) {
526 DCHECK_CURRENTLY_ON(BrowserThread::UI);
528 file_manager_private::CopyProgressStatus status;
529 if (error == base::File::FILE_OK) {
530 // Send success event.
531 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
532 status.source_url.reset(new std::string(source_url.spec()));
533 status.destination_url.reset(new std::string(destination_url.spec()));
534 } else {
535 // Send error event.
536 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
537 status.error.reset(new std::string(FileErrorToErrorName(error)));
540 BroadcastEvent(profile_,
541 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS,
542 file_manager_private::OnCopyProgress::kEventName,
543 file_manager_private::OnCopyProgress::Create(copy_id, status));
546 void EventRouter::OnCopyProgress(
547 int copy_id,
548 storage::FileSystemOperation::CopyProgressType type,
549 const GURL& source_url,
550 const GURL& destination_url,
551 int64 size) {
552 DCHECK_CURRENTLY_ON(BrowserThread::UI);
554 file_manager_private::CopyProgressStatus status;
555 status.type = CopyProgressTypeToCopyProgressStatusType(type);
556 status.source_url.reset(new std::string(source_url.spec()));
557 if (type == storage::FileSystemOperation::END_COPY_ENTRY ||
558 type == storage::FileSystemOperation::ERROR_COPY_ENTRY)
559 status.destination_url.reset(new std::string(destination_url.spec()));
560 if (type == storage::FileSystemOperation::ERROR_COPY_ENTRY)
561 status.error.reset(
562 new std::string(FileErrorToErrorName(base::File::FILE_ERROR_FAILED)));
563 if (type == storage::FileSystemOperation::PROGRESS)
564 status.size.reset(new double(size));
566 // Discard error progress since current JS code cannot handle this properly.
567 // TODO(yawano): Remove this after JS side is implemented correctly.
568 if (type == storage::FileSystemOperation::ERROR_COPY_ENTRY)
569 return;
571 // Should not skip events other than TYPE_PROGRESS.
572 const bool always =
573 status.type != file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
574 if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
575 return;
577 BroadcastEvent(profile_,
578 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS,
579 file_manager_private::OnCopyProgress::kEventName,
580 file_manager_private::OnCopyProgress::Create(copy_id, status));
583 void EventRouter::OnWatcherManagerNotification(
584 const storage::FileSystemURL& file_system_url,
585 const std::string& extension_id,
586 storage::WatcherManager::ChangeType /* change_type */) {
587 std::vector<std::string> extension_ids;
588 extension_ids.push_back(extension_id);
590 DispatchDirectoryChangeEvent(file_system_url.virtual_path(), NULL,
591 false /* error */, extension_ids);
594 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
595 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
596 NOTREACHED();
597 return;
600 BroadcastEvent(
601 profile_, extensions::events::
602 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED,
603 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
604 file_manager_private::OnDriveConnectionStatusChanged::Create());
607 void EventRouter::TimezoneChanged(const icu::TimeZone& timezone) {
608 OnFileManagerPrefsChanged();
611 void EventRouter::OnFileManagerPrefsChanged() {
612 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
613 NOTREACHED();
614 return;
617 BroadcastEvent(
618 profile_, extensions::events::FILE_MANAGER_PRIVATE_ON_PREFERENCES_CHANGED,
619 file_manager_private::OnPreferencesChanged::kEventName,
620 file_manager_private::OnPreferencesChanged::Create());
623 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
624 HandleFileWatchNotification(NULL, drive_path, false);
627 void EventRouter::OnFileChanged(const drive::FileChange& changed_files) {
628 // In this method, we convert changed_files to a map which can be handled by
629 // HandleFileWatchNotification.
631 // e.g.
632 // /a/b DIRECTORY:DELETE
634 // map[/a] = /a/b DIRECTORY:DELETE
635 // map[/a/b] = /a/b DIRECTORY:DELETE
637 // We used the key of map to match the watched directories of file watchers.
638 typedef std::map<base::FilePath, drive::FileChange> FileChangeMap;
639 typedef drive::FileChange::ChangeList::List FileChangeList;
641 FileChangeMap map;
642 const drive::FileChange::Map& changed_file_map = changed_files.map();
643 for (auto const& file_change_key_value : changed_file_map) {
644 // Check whether the FileChangeList contains directory deletion.
645 bool contains_directory_deletion = false;
646 const FileChangeList list = file_change_key_value.second.list();
647 for (drive::FileChange::Change const& change : list) {
648 if (change.IsDirectory() && change.IsDelete()) {
649 contains_directory_deletion = true;
650 break;
654 const base::FilePath& path = file_change_key_value.first;
655 map[path.DirName()].Update(path, file_change_key_value.second);
657 // For deletion of a directory, onFileChanged gets different changed_files.
658 // We solve the difference here.
660 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
661 // 1. /a/b DELETE:DIRECTORY
662 // 2. /a DELETE:DIRECTORY
664 // /a/b is watched, and /a is deleted from Files.app.
665 // 1. /a DELETE:DIRECTORY
666 if (contains_directory_deletion) {
667 // Expand the deleted directory path with watched paths.
668 for (WatcherMap::const_iterator file_watchers_it =
669 file_watchers_.lower_bound(path);
670 file_watchers_it != file_watchers_.end(); ++file_watchers_it) {
671 if (path == file_watchers_it->first ||
672 path.IsParent(file_watchers_it->first)) {
673 map[file_watchers_it->first].Update(
674 file_watchers_it->first,
675 drive::FileChange::FileType::FILE_TYPE_DIRECTORY,
676 drive::FileChange::ChangeType::CHANGE_TYPE_DELETE);
682 for (auto const& file_change_key_value : map) {
683 HandleFileWatchNotification(&(file_change_key_value.second),
684 file_change_key_value.first, false);
688 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
689 const base::FilePath& drive_path) {
690 file_manager_private::DriveSyncErrorEvent event;
691 switch (type) {
692 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
693 event.type =
694 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
695 break;
696 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
697 event.type =
698 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
699 break;
700 case drive::file_system::DRIVE_SYNC_ERROR_NO_SERVER_SPACE:
701 event.type = file_manager_private::DRIVE_SYNC_ERROR_TYPE_NO_SERVER_SPACE;
702 break;
703 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
704 event.type =
705 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC;
706 break;
708 event.file_url = util::ConvertDrivePathToFileSystemUrl(
709 profile_, drive_path, kFileManagerAppId).spec();
710 BroadcastEvent(profile_,
711 extensions::events::FILE_MANAGER_PRIVATE_ON_DRIVE_SYNC_ERROR,
712 file_manager_private::OnDriveSyncError::kEventName,
713 file_manager_private::OnDriveSyncError::Create(event));
716 void EventRouter::OnRefreshTokenInvalid() {
717 DCHECK_CURRENTLY_ON(BrowserThread::UI);
719 // Raise a DriveConnectionStatusChanged event to notify the status offline.
720 BroadcastEvent(
721 profile_, extensions::events::
722 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED,
723 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
724 file_manager_private::OnDriveConnectionStatusChanged::Create());
727 void EventRouter::OnReadyToSendRequests() {
728 DCHECK_CURRENTLY_ON(BrowserThread::UI);
730 // Raise a DriveConnectionStatusChanged event to notify the status online.
731 BroadcastEvent(
732 profile_, extensions::events::
733 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED,
734 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
735 file_manager_private::OnDriveConnectionStatusChanged::Create());
738 void EventRouter::HandleFileWatchNotification(const drive::FileChange* list,
739 const base::FilePath& local_path,
740 bool got_error) {
741 DCHECK_CURRENTLY_ON(BrowserThread::UI);
743 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
744 if (iter == file_watchers_.end()) {
745 return;
748 if (list && list->size() > kDirectoryChangeEventMaxDetailInfoSize) {
749 // Removes the detailed information, if the list size is more than
750 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
751 // and processing it may cause more itme.
752 // This will be invoked full-refresh in Files.app.
753 list = NULL;
756 DispatchDirectoryChangeEvent(iter->second->virtual_path(),
757 list,
758 got_error,
759 iter->second->GetExtensionIds());
762 void EventRouter::DispatchDirectoryChangeEvent(
763 const base::FilePath& virtual_path,
764 const drive::FileChange* list,
765 bool got_error,
766 const std::vector<std::string>& extension_ids) {
767 dispatch_directory_change_event_impl_.Run(virtual_path, list, got_error,
768 extension_ids);
771 void EventRouter::DispatchDirectoryChangeEventImpl(
772 const base::FilePath& virtual_path,
773 const drive::FileChange* list,
774 bool got_error,
775 const std::vector<std::string>& extension_ids) {
776 if (!profile_) {
777 NOTREACHED();
778 return;
780 linked_ptr<drive::FileChange> changes;
781 if (list)
782 changes.reset(new drive::FileChange(*list)); // Copy
784 for (size_t i = 0; i < extension_ids.size(); ++i) {
785 std::string* extension_id = new std::string(extension_ids[i]);
787 FileDefinition file_definition;
788 file_definition.virtual_path = virtual_path;
789 // TODO(mtomasz): Add support for watching files in File System Provider
790 // API.
791 file_definition.is_directory = true;
793 file_manager::util::ConvertFileDefinitionToEntryDefinition(
794 profile_,
795 *extension_id,
796 file_definition,
797 base::Bind(
798 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
799 weak_factory_.GetWeakPtr(),
800 changes,
801 base::Owned(extension_id),
802 got_error));
806 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
807 const linked_ptr<drive::FileChange> list,
808 const std::string* extension_id,
809 bool watcher_error,
810 const EntryDefinition& entry_definition) {
811 // TODO(mtomasz): Add support for watching files in File System Provider API.
812 if (entry_definition.error != base::File::FILE_OK ||
813 !entry_definition.is_directory) {
814 DVLOG(1) << "Unable to dispatch event because resolving the directory "
815 << "entry definition failed.";
816 return;
819 file_manager_private::FileWatchEvent event;
820 event.event_type = watcher_error
821 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
822 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED;
824 // Detailed information is available.
825 if (list.get()) {
826 event.changed_files.reset(
827 new std::vector<linked_ptr<file_manager_private::FileChange> >);
829 if (list->map().empty())
830 return;
832 for (drive::FileChange::Map::const_iterator it = list->map().begin();
833 it != list->map().end();
834 it++) {
835 linked_ptr<file_manager_private::FileChange> change_list(
836 new file_manager_private::FileChange);
838 GURL url = util::ConvertDrivePathToFileSystemUrl(
839 profile_, it->first, *extension_id);
840 change_list->url = url.spec();
842 for (drive::FileChange::ChangeList::List::const_iterator change =
843 it->second.list().begin();
844 change != it->second.list().end();
845 change++) {
846 change_list->changes.push_back(
847 ConvertChangeTypeFromDriveToApi(change->change()));
850 event.changed_files->push_back(change_list);
854 event.entry.additional_properties.SetString(
855 "fileSystemName", entry_definition.file_system_name);
856 event.entry.additional_properties.SetString(
857 "fileSystemRoot", entry_definition.file_system_root_url);
858 event.entry.additional_properties.SetString(
859 "fileFullPath", "/" + entry_definition.full_path.value());
860 event.entry.additional_properties.SetBoolean("fileIsDirectory",
861 entry_definition.is_directory);
863 DispatchEventToExtension(
864 profile_, *extension_id,
865 extensions::events::FILE_MANAGER_PRIVATE_ON_DIRECTORY_CHANGED,
866 file_manager_private::OnDirectoryChanged::kEventName,
867 file_manager_private::OnDirectoryChanged::Create(event));
870 void EventRouter::OnDiskAdded(
871 const DiskMountManager::Disk& disk, bool mounting) {
872 DCHECK_CURRENTLY_ON(BrowserThread::UI);
873 // Do nothing.
876 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
877 DCHECK_CURRENTLY_ON(BrowserThread::UI);
878 // Do nothing.
881 void EventRouter::OnDeviceAdded(const std::string& device_path) {
882 DCHECK_CURRENTLY_ON(BrowserThread::UI);
883 // Do nothing.
886 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
887 DCHECK_CURRENTLY_ON(BrowserThread::UI);
888 // Do nothing.
891 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
892 const Volume& volume) {
893 DCHECK_CURRENTLY_ON(BrowserThread::UI);
894 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
895 // happen at shutdown. This should be removed after removing Drive mounting
896 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
897 // the only path to come here after Shutdown is called).
898 if (!profile_)
899 return;
901 DispatchMountCompletedEvent(
902 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT, error_code,
903 volume);
905 // TODO(mtomasz): Move VolumeManager and part of the event router outside of
906 // file_manager, so there is no dependency between File System API and the
907 // file_manager code.
908 extensions::file_system_api::DispatchVolumeListChangeEvent(profile_);
911 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
912 const Volume& volume) {
913 DCHECK_CURRENTLY_ON(BrowserThread::UI);
914 DispatchMountCompletedEvent(
915 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT, error_code,
916 volume);
919 void EventRouter::DispatchMountCompletedEvent(
920 file_manager_private::MountCompletedEventType event_type,
921 chromeos::MountError error,
922 const Volume& volume) {
923 // Build an event object.
924 file_manager_private::MountCompletedEvent event;
925 event.event_type = event_type;
926 event.status = MountErrorToMountCompletedStatus(error);
927 util::VolumeToVolumeMetadata(profile_, volume, &event.volume_metadata);
928 event.should_notify =
929 ShouldShowNotificationForVolume(profile_, *device_event_router_, volume);
930 BroadcastEvent(profile_,
931 extensions::events::FILE_MANAGER_PRIVATE_ON_MOUNT_COMPLETED,
932 file_manager_private::OnMountCompleted::kEventName,
933 file_manager_private::OnMountCompleted::Create(event));
936 void EventRouter::OnFormatStarted(const std::string& device_path,
937 bool success) {
938 DCHECK_CURRENTLY_ON(BrowserThread::UI);
939 // Do nothing.
942 void EventRouter::OnFormatCompleted(const std::string& device_path,
943 bool success) {
944 DCHECK_CURRENTLY_ON(BrowserThread::UI);
945 // Do nothing.
948 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
949 const DispatchDirectoryChangeEventImplCallback& callback) {
950 dispatch_directory_change_event_impl_ = callback;
953 base::WeakPtr<EventRouter> EventRouter::GetWeakPtr() {
954 return weak_factory_.GetWeakPtr();
957 } // namespace file_manager