Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / chromeos / extensions / file_manager / event_router.cc
blob5c625b26507e36101891487c0a4140d5bd34b087
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/drive_pref_names.h"
19 #include "chrome/browser/chromeos/drive/file_change.h"
20 #include "chrome/browser/chromeos/drive/file_system_interface.h"
21 #include "chrome/browser/chromeos/drive/file_system_util.h"
22 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
23 #include "chrome/browser/chromeos/file_manager/app_id.h"
24 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
25 #include "chrome/browser/chromeos/file_manager/open_util.h"
26 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
27 #include "chrome/browser/chromeos/login/lock/screen_locker.h"
28 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
29 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/extensions/extension_util.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/profiles/profile_manager.h"
34 #include "chrome/common/chrome_switches.h"
35 #include "chrome/common/pref_names.h"
36 #include "chromeos/dbus/dbus_thread_manager.h"
37 #include "chromeos/login/login_state.h"
38 #include "chromeos/network/network_handler.h"
39 #include "chromeos/network/network_state_handler.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);
455 // File watch setup routines.
456 void EventRouter::AddFileWatch(const base::FilePath& local_path,
457 const base::FilePath& virtual_path,
458 const std::string& extension_id,
459 const BoolCallback& callback) {
460 DCHECK_CURRENTLY_ON(BrowserThread::UI);
461 DCHECK(!callback.is_null());
463 base::FilePath watch_path = local_path;
464 bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
465 // Tweak watch path for remote sources - we need to drop leading /special
466 // directory from there in order to be able to pair these events with
467 // their change notifications.
468 if (is_on_drive)
469 watch_path = drive::util::ExtractDrivePath(watch_path);
471 WatcherMap::iterator iter = file_watchers_.find(watch_path);
472 if (iter == file_watchers_.end()) {
473 scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
474 watcher->AddExtension(extension_id);
476 if (is_on_drive) {
477 // For Drive, file watching is done via OnDirectoryChanged().
478 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
479 base::Bind(callback, true));
480 } else {
481 // For local files, start watching using FileWatcher.
482 watcher->WatchLocalFile(
483 watch_path,
484 base::Bind(&EventRouter::HandleFileWatchNotification,
485 weak_factory_.GetWeakPtr(),
486 static_cast<drive::FileChange*>(NULL)),
487 callback);
490 file_watchers_[watch_path] = watcher.release();
491 } else {
492 iter->second->AddExtension(extension_id);
493 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
494 base::Bind(callback, true));
498 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
499 const std::string& extension_id) {
500 DCHECK_CURRENTLY_ON(BrowserThread::UI);
502 base::FilePath watch_path = local_path;
503 // Tweak watch path for remote sources - we need to drop leading /special
504 // directory from there in order to be able to pair these events with
505 // their change notifications.
506 if (drive::util::IsUnderDriveMountPoint(watch_path)) {
507 watch_path = drive::util::ExtractDrivePath(watch_path);
509 WatcherMap::iterator iter = file_watchers_.find(watch_path);
510 if (iter == file_watchers_.end())
511 return;
512 // Remove the watcher if |watch_path| is no longer watched by any extensions.
513 iter->second->RemoveExtension(extension_id);
514 if (iter->second->GetExtensionIds().empty()) {
515 delete iter->second;
516 file_watchers_.erase(iter);
520 void EventRouter::OnCopyCompleted(int copy_id,
521 const GURL& source_url,
522 const GURL& destination_url,
523 base::File::Error error) {
524 DCHECK_CURRENTLY_ON(BrowserThread::UI);
526 file_manager_private::CopyProgressStatus status;
527 if (error == base::File::FILE_OK) {
528 // Send success event.
529 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
530 status.source_url.reset(new std::string(source_url.spec()));
531 status.destination_url.reset(new std::string(destination_url.spec()));
532 } else {
533 // Send error event.
534 status.type = file_manager_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
535 status.error.reset(new std::string(FileErrorToErrorName(error)));
538 BroadcastEvent(profile_,
539 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS,
540 file_manager_private::OnCopyProgress::kEventName,
541 file_manager_private::OnCopyProgress::Create(copy_id, status));
544 void EventRouter::OnCopyProgress(
545 int copy_id,
546 storage::FileSystemOperation::CopyProgressType type,
547 const GURL& source_url,
548 const GURL& destination_url,
549 int64 size) {
550 DCHECK_CURRENTLY_ON(BrowserThread::UI);
552 file_manager_private::CopyProgressStatus status;
553 status.type = CopyProgressTypeToCopyProgressStatusType(type);
554 status.source_url.reset(new std::string(source_url.spec()));
555 if (type == storage::FileSystemOperation::END_COPY_ENTRY ||
556 type == storage::FileSystemOperation::ERROR_COPY_ENTRY)
557 status.destination_url.reset(new std::string(destination_url.spec()));
558 if (type == storage::FileSystemOperation::ERROR_COPY_ENTRY)
559 status.error.reset(
560 new std::string(FileErrorToErrorName(base::File::FILE_ERROR_FAILED)));
561 if (type == storage::FileSystemOperation::PROGRESS)
562 status.size.reset(new double(size));
564 // Discard error progress since current JS code cannot handle this properly.
565 // TODO(yawano): Remove this after JS side is implemented correctly.
566 if (type == storage::FileSystemOperation::ERROR_COPY_ENTRY)
567 return;
569 // Should not skip events other than TYPE_PROGRESS.
570 const bool always =
571 status.type != file_manager_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
572 if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
573 return;
575 BroadcastEvent(profile_,
576 extensions::events::FILE_MANAGER_PRIVATE_ON_COPY_PROGRESS,
577 file_manager_private::OnCopyProgress::kEventName,
578 file_manager_private::OnCopyProgress::Create(copy_id, status));
581 void EventRouter::OnWatcherManagerNotification(
582 const storage::FileSystemURL& file_system_url,
583 const std::string& extension_id,
584 storage::WatcherManager::ChangeType /* change_type */) {
585 std::vector<std::string> extension_ids;
586 extension_ids.push_back(extension_id);
588 DispatchDirectoryChangeEvent(file_system_url.virtual_path(), NULL,
589 false /* error */, extension_ids);
592 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
593 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
594 NOTREACHED();
595 return;
598 BroadcastEvent(
599 profile_, extensions::events::
600 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED,
601 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
602 file_manager_private::OnDriveConnectionStatusChanged::Create());
605 void EventRouter::OnFileManagerPrefsChanged() {
606 if (!profile_ || !extensions::EventRouter::Get(profile_)) {
607 NOTREACHED();
608 return;
611 BroadcastEvent(
612 profile_, extensions::events::FILE_MANAGER_PRIVATE_ON_PREFERENCES_CHANGED,
613 file_manager_private::OnPreferencesChanged::kEventName,
614 file_manager_private::OnPreferencesChanged::Create());
617 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
618 HandleFileWatchNotification(NULL, drive_path, false);
621 void EventRouter::OnFileChanged(const drive::FileChange& changed_files) {
622 // In this method, we convert changed_files to a map which can be handled by
623 // HandleFileWatchNotification.
625 // e.g.
626 // /a/b DIRECTORY:DELETE
628 // map[/a] = /a/b DIRECTORY:DELETE
629 // map[/a/b] = /a/b DIRECTORY:DELETE
631 // We used the key of map to match the watched directories of file watchers.
632 typedef std::map<base::FilePath, drive::FileChange> FileChangeMap;
633 typedef drive::FileChange::ChangeList::List FileChangeList;
635 FileChangeMap map;
636 const drive::FileChange::Map& changed_file_map = changed_files.map();
637 for (auto const& file_change_key_value : changed_file_map) {
638 // Check whether the FileChangeList contains directory deletion.
639 bool contains_directory_deletion = false;
640 const FileChangeList list = file_change_key_value.second.list();
641 for (drive::FileChange::Change const& change : list) {
642 if (change.IsDirectory() && change.IsDelete()) {
643 contains_directory_deletion = true;
644 break;
648 const base::FilePath& path = file_change_key_value.first;
649 map[path.DirName()].Update(path, file_change_key_value.second);
651 // For deletion of a directory, onFileChanged gets different changed_files.
652 // We solve the difference here.
654 // /a/b is watched, and /a is deleted from Drive (e.g. from Web).
655 // 1. /a/b DELETE:DIRECTORY
656 // 2. /a DELETE:DIRECTORY
658 // /a/b is watched, and /a is deleted from Files.app.
659 // 1. /a DELETE:DIRECTORY
660 if (contains_directory_deletion) {
661 // Expand the deleted directory path with watched paths.
662 for (WatcherMap::const_iterator file_watchers_it =
663 file_watchers_.lower_bound(path);
664 file_watchers_it != file_watchers_.end(); ++file_watchers_it) {
665 if (path == file_watchers_it->first ||
666 path.IsParent(file_watchers_it->first)) {
667 map[file_watchers_it->first].Update(
668 file_watchers_it->first,
669 drive::FileChange::FileType::FILE_TYPE_DIRECTORY,
670 drive::FileChange::ChangeType::CHANGE_TYPE_DELETE);
676 for (auto const& file_change_key_value : map) {
677 HandleFileWatchNotification(&(file_change_key_value.second),
678 file_change_key_value.first, false);
682 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
683 const base::FilePath& drive_path) {
684 file_manager_private::DriveSyncErrorEvent event;
685 switch (type) {
686 case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
687 event.type =
688 file_manager_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
689 break;
690 case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
691 event.type =
692 file_manager_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
693 break;
694 case drive::file_system::DRIVE_SYNC_ERROR_MISC:
695 event.type =
696 file_manager_private::DRIVE_SYNC_ERROR_TYPE_MISC;
697 break;
699 event.file_url = util::ConvertDrivePathToFileSystemUrl(
700 profile_, drive_path, kFileManagerAppId).spec();
701 BroadcastEvent(profile_,
702 extensions::events::FILE_MANAGER_PRIVATE_ON_DRIVE_SYNC_ERROR,
703 file_manager_private::OnDriveSyncError::kEventName,
704 file_manager_private::OnDriveSyncError::Create(event));
707 void EventRouter::OnRefreshTokenInvalid() {
708 DCHECK_CURRENTLY_ON(BrowserThread::UI);
710 // Raise a DriveConnectionStatusChanged event to notify the status offline.
711 BroadcastEvent(
712 profile_, extensions::events::
713 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED,
714 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
715 file_manager_private::OnDriveConnectionStatusChanged::Create());
718 void EventRouter::OnReadyToSendRequests() {
719 DCHECK_CURRENTLY_ON(BrowserThread::UI);
721 // Raise a DriveConnectionStatusChanged event to notify the status online.
722 BroadcastEvent(
723 profile_, extensions::events::
724 FILE_MANAGER_PRIVATE_ON_DRIVE_CONNECTION_STATUS_CHANGED,
725 file_manager_private::OnDriveConnectionStatusChanged::kEventName,
726 file_manager_private::OnDriveConnectionStatusChanged::Create());
729 void EventRouter::HandleFileWatchNotification(const drive::FileChange* list,
730 const base::FilePath& local_path,
731 bool got_error) {
732 DCHECK_CURRENTLY_ON(BrowserThread::UI);
734 WatcherMap::const_iterator iter = file_watchers_.find(local_path);
735 if (iter == file_watchers_.end()) {
736 return;
739 if (list && list->size() > kDirectoryChangeEventMaxDetailInfoSize) {
740 // Removes the detailed information, if the list size is more than
741 // kDirectoryChangeEventMaxDetailInfoSize, since passing large list
742 // and processing it may cause more itme.
743 // This will be invoked full-refresh in Files.app.
744 list = NULL;
747 DispatchDirectoryChangeEvent(iter->second->virtual_path(),
748 list,
749 got_error,
750 iter->second->GetExtensionIds());
753 void EventRouter::DispatchDirectoryChangeEvent(
754 const base::FilePath& virtual_path,
755 const drive::FileChange* list,
756 bool got_error,
757 const std::vector<std::string>& extension_ids) {
758 dispatch_directory_change_event_impl_.Run(virtual_path, list, got_error,
759 extension_ids);
762 void EventRouter::DispatchDirectoryChangeEventImpl(
763 const base::FilePath& virtual_path,
764 const drive::FileChange* list,
765 bool got_error,
766 const std::vector<std::string>& extension_ids) {
767 if (!profile_) {
768 NOTREACHED();
769 return;
771 linked_ptr<drive::FileChange> changes;
772 if (list)
773 changes.reset(new drive::FileChange(*list)); // Copy
775 for (size_t i = 0; i < extension_ids.size(); ++i) {
776 std::string* extension_id = new std::string(extension_ids[i]);
778 FileDefinition file_definition;
779 file_definition.virtual_path = virtual_path;
780 // TODO(mtomasz): Add support for watching files in File System Provider
781 // API.
782 file_definition.is_directory = true;
784 file_manager::util::ConvertFileDefinitionToEntryDefinition(
785 profile_,
786 *extension_id,
787 file_definition,
788 base::Bind(
789 &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
790 weak_factory_.GetWeakPtr(),
791 changes,
792 base::Owned(extension_id),
793 got_error));
797 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
798 const linked_ptr<drive::FileChange> list,
799 const std::string* extension_id,
800 bool watcher_error,
801 const EntryDefinition& entry_definition) {
802 // TODO(mtomasz): Add support for watching files in File System Provider API.
803 if (entry_definition.error != base::File::FILE_OK ||
804 !entry_definition.is_directory) {
805 DVLOG(1) << "Unable to dispatch event because resolving the directory "
806 << "entry definition failed.";
807 return;
810 file_manager_private::FileWatchEvent event;
811 event.event_type = watcher_error
812 ? file_manager_private::FILE_WATCH_EVENT_TYPE_ERROR
813 : file_manager_private::FILE_WATCH_EVENT_TYPE_CHANGED;
815 // Detailed information is available.
816 if (list.get()) {
817 event.changed_files.reset(
818 new std::vector<linked_ptr<file_manager_private::FileChange> >);
820 if (list->map().empty())
821 return;
823 for (drive::FileChange::Map::const_iterator it = list->map().begin();
824 it != list->map().end();
825 it++) {
826 linked_ptr<file_manager_private::FileChange> change_list(
827 new file_manager_private::FileChange);
829 GURL url = util::ConvertDrivePathToFileSystemUrl(
830 profile_, it->first, *extension_id);
831 change_list->url = url.spec();
833 for (drive::FileChange::ChangeList::List::const_iterator change =
834 it->second.list().begin();
835 change != it->second.list().end();
836 change++) {
837 change_list->changes.push_back(
838 ConvertChangeTypeFromDriveToApi(change->change()));
841 event.changed_files->push_back(change_list);
845 event.entry.additional_properties.SetString(
846 "fileSystemName", entry_definition.file_system_name);
847 event.entry.additional_properties.SetString(
848 "fileSystemRoot", entry_definition.file_system_root_url);
849 event.entry.additional_properties.SetString(
850 "fileFullPath", "/" + entry_definition.full_path.value());
851 event.entry.additional_properties.SetBoolean("fileIsDirectory",
852 entry_definition.is_directory);
854 DispatchEventToExtension(
855 profile_, *extension_id,
856 extensions::events::FILE_MANAGER_PRIVATE_ON_DIRECTORY_CHANGED,
857 file_manager_private::OnDirectoryChanged::kEventName,
858 file_manager_private::OnDirectoryChanged::Create(event));
861 void EventRouter::OnDiskAdded(
862 const DiskMountManager::Disk& disk, bool mounting) {
863 DCHECK_CURRENTLY_ON(BrowserThread::UI);
864 // Do nothing.
867 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
868 DCHECK_CURRENTLY_ON(BrowserThread::UI);
869 // Do nothing.
872 void EventRouter::OnDeviceAdded(const std::string& device_path) {
873 DCHECK_CURRENTLY_ON(BrowserThread::UI);
874 // Do nothing.
877 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
878 DCHECK_CURRENTLY_ON(BrowserThread::UI);
879 // Do nothing.
882 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
883 const Volume& volume) {
884 DCHECK_CURRENTLY_ON(BrowserThread::UI);
885 // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
886 // happen at shutdown. This should be removed after removing Drive mounting
887 // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
888 // the only path to come here after Shutdown is called).
889 if (!profile_)
890 return;
892 DispatchMountCompletedEvent(
893 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT, error_code,
894 volume);
896 // TODO(mtomasz): Move VolumeManager and part of the event router outside of
897 // file_manager, so there is no dependency between File System API and the
898 // file_manager code.
899 extensions::file_system_api::DispatchVolumeListChangeEvent(profile_);
902 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
903 const Volume& volume) {
904 DCHECK_CURRENTLY_ON(BrowserThread::UI);
905 DispatchMountCompletedEvent(
906 file_manager_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT, error_code,
907 volume);
910 void EventRouter::DispatchMountCompletedEvent(
911 file_manager_private::MountCompletedEventType event_type,
912 chromeos::MountError error,
913 const Volume& volume) {
914 // Build an event object.
915 file_manager_private::MountCompletedEvent event;
916 event.event_type = event_type;
917 event.status = MountErrorToMountCompletedStatus(error);
918 util::VolumeToVolumeMetadata(profile_, volume, &event.volume_metadata);
919 event.should_notify =
920 ShouldShowNotificationForVolume(profile_, *device_event_router_, volume);
921 BroadcastEvent(profile_,
922 extensions::events::FILE_MANAGER_PRIVATE_ON_MOUNT_COMPLETED,
923 file_manager_private::OnMountCompleted::kEventName,
924 file_manager_private::OnMountCompleted::Create(event));
927 void EventRouter::OnFormatStarted(const std::string& device_path,
928 bool success) {
929 DCHECK_CURRENTLY_ON(BrowserThread::UI);
930 // Do nothing.
933 void EventRouter::OnFormatCompleted(const std::string& device_path,
934 bool success) {
935 DCHECK_CURRENTLY_ON(BrowserThread::UI);
936 // Do nothing.
939 void EventRouter::SetDispatchDirectoryChangeEventImplForTesting(
940 const DispatchDirectoryChangeEventImplCallback& callback) {
941 dispatch_directory_change_event_impl_ = callback;
944 base::WeakPtr<EventRouter> EventRouter::GetWeakPtr() {
945 return weak_factory_.GetWeakPtr();
948 } // namespace file_manager