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