Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / extensions / file_manager / event_router.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
6
7 #include "base/bind.h"
8 #include "base/file_util.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_change_registrar.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/stl_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "base/values.h"
15 #include "chrome/browser/app_mode/app_mode_utils.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/chromeos/drive/drive_integration_service.h"
18 #include "chrome/browser/chromeos/drive/file_system_interface.h"
19 #include "chrome/browser/chromeos/drive/file_system_util.h"
20 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
21 #include "chrome/browser/chromeos/file_manager/app_id.h"
22 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
23 #include "chrome/browser/chromeos/file_manager/open_util.h"
24 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
25 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
26 #include "chrome/browser/chromeos/login/screen_locker.h"
27 #include "chrome/browser/drive/drive_service_interface.h"
28 #include "chrome/browser/extensions/event_names.h"
29 #include "chrome/browser/extensions/extension_host.h"
30 #include "chrome/browser/extensions/extension_service.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/browser/profiles/profile_manager.h"
33 #include "chrome/common/pref_names.h"
34 #include "chromeos/login/login_state.h"
35 #include "chromeos/network/network_handler.h"
36 #include "chromeos/network/network_state_handler.h"
37 #include "content/public/browser/browser_thread.h"
38 #include "content/public/browser/notification_source.h"
39 #include "content/public/browser/render_process_host.h"
40 #include "extensions/browser/event_router.h"
41 #include "extensions/browser/extension_prefs.h"
42 #include "extensions/browser/extension_system.h"
43 #include "webkit/common/fileapi/file_system_types.h"
44 #include "webkit/common/fileapi/file_system_util.h"
45
46 using chromeos::disks::DiskMountManager;
47 using chromeos::NetworkHandler;
48 using content::BrowserThread;
49 using drive::DriveIntegrationService;
50 using drive::DriveIntegrationServiceFactory;
51
52 namespace file_browser_private = extensions::api::file_browser_private;
53
54 namespace file_manager {
55 namespace {
56
57 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
58                                    const base::Closure& success_callback,
59                                    const base::Closure& failure_callback) {
60   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
61
62   if (base::DirectoryExists(directory_path))
63     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
64   else
65     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
66 };
67
68 void DirectoryExistsOnUIThread(const base::FilePath& directory_path,
69                                const base::Closure& success_callback,
70                                const base::Closure& failure_callback) {
71   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
72
73   content::BrowserThread::PostBlockingPoolTask(
74       FROM_HERE,
75       base::Bind(&DirectoryExistsOnBlockingPool,
76                  directory_path,
77                  success_callback,
78                  failure_callback));
79 };
80
81 // Constants for the "transferState" field of onFileTransferUpdated event.
82 const char kFileTransferStateStarted[] = "started";
83 const char kFileTransferStateInProgress[] = "in_progress";
84 const char kFileTransferStateCompleted[] = "completed";
85 const char kFileTransferStateFailed[] = "failed";
86
87 // Frequency of sending onFileTransferUpdated.
88 const int64 kFileTransferEventFrequencyInMilliseconds = 1000;
89
90 // Utility function to check if |job_info| is a file uploading job.
91 bool IsUploadJob(drive::JobType type) {
92   return (type == drive::TYPE_UPLOAD_NEW_FILE ||
93           type == drive::TYPE_UPLOAD_EXISTING_FILE);
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_browser_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_browser_private::ParseTransferState(job_status);
110   status->transfer_type =
111       IsUploadJob(job_info.job_type) ?
112       file_browser_private::TRANSFER_TYPE_UPLOAD :
113       file_browser_private::TRANSFER_TYPE_DOWNLOAD;
114   // JavaScript does not have 64-bit integers. Instead we use double, which
115   // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
116   // in C++. Larger values are rounded.
117   status->processed.reset(
118       new double(static_cast<double>(job_info.num_completed_bytes)));
119   status->total.reset(
120       new double(static_cast<double>(job_info.num_total_bytes)));
121 }
122
123 // Checks for availability of the Google+ Photos app.
124 // TODO(mtomasz): Replace with crbug.com/341902 solution.
125 bool IsGooglePhotosInstalled(Profile *profile) {
126   ExtensionService* service =
127       extensions::ExtensionSystem::Get(profile)->extension_service();
128   if (!service)
129     return false;
130
131   // Google+ Photos uses several ids for different channels. Therefore, all of
132   // them should be checked.
133   const std::string kGooglePlusPhotosIds[] = {
134       "ebpbnabdhheoknfklmpddcdijjkmklkp",  // G+ Photos staging
135       "efjnaogkjbogokcnohkmnjdojkikgobo",  // G+ Photos prod
136       "ejegoaikibpmikoejfephaneibodccma"   // G+ Photos dev
137   };
138
139   for (size_t i = 0; i < arraysize(kGooglePlusPhotosIds); ++i) {
140     if (service->GetExtensionById(kGooglePlusPhotosIds[i],
141                                   false /* include_disable */) != NULL)
142       return true;
143   }
144
145   return false;
146 }
147
148 // Checks if the Recovery Tool is running. This is a temporary solution.
149 // TODO(mtomasz): Replace with crbug.com/341902 solution.
150 bool IsRecoveryToolRunning(Profile* profile) {
151   extensions::ExtensionPrefs* extension_prefs =
152       extensions::ExtensionPrefs::Get(profile);
153   if (!extension_prefs)
154     return false;
155
156   const std::string kRecoveryToolIds[] = {
157       "kkebgepbbgbcmghedmmdfcbdcodlkngh",  // Recovert tool staging
158       "jndclpdbaamdhonoechobihbbiimdgai"   // Recovery tool prod
159   };
160
161   for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
162     const std::string extension_id = kRecoveryToolIds[i];
163     if (extension_prefs->IsExtensionRunning(extension_id))
164       return true;
165   }
166
167   return false;
168 }
169
170 // Sends an event named |event_name| with arguments |event_args| to extensions.
171 void BroadcastEvent(Profile* profile,
172                     const std::string& event_name,
173                     scoped_ptr<base::ListValue> event_args) {
174   extensions::ExtensionSystem::Get(profile)->event_router()->
175       BroadcastEvent(make_scoped_ptr(
176           new extensions::Event(event_name, event_args.Pass())));
177 }
178
179 file_browser_private::MountCompletedStatus
180 MountErrorToMountCompletedStatus(chromeos::MountError error) {
181   switch (error) {
182     case chromeos::MOUNT_ERROR_NONE:
183       return file_browser_private::MOUNT_COMPLETED_STATUS_SUCCESS;
184     case chromeos::MOUNT_ERROR_UNKNOWN:
185       return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN;
186     case chromeos::MOUNT_ERROR_INTERNAL:
187       return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INTERNAL;
188     case chromeos::MOUNT_ERROR_INVALID_ARGUMENT:
189       return file_browser_private::
190           MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARGUMENT;
191     case chromeos::MOUNT_ERROR_INVALID_PATH:
192       return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_PATH;
193     case chromeos::MOUNT_ERROR_PATH_ALREADY_MOUNTED:
194       return file_browser_private::
195           MOUNT_COMPLETED_STATUS_ERROR_PATH_ALREADY_MOUNTED;
196     case chromeos::MOUNT_ERROR_PATH_NOT_MOUNTED:
197       return file_browser_private::
198           MOUNT_COMPLETED_STATUS_ERROR_PATH_NOT_MOUNTED;
199     case chromeos::MOUNT_ERROR_DIRECTORY_CREATION_FAILED:
200       return file_browser_private
201           ::MOUNT_COMPLETED_STATUS_ERROR_DIRECTORY_CREATION_FAILED;
202     case chromeos::MOUNT_ERROR_INVALID_MOUNT_OPTIONS:
203       return file_browser_private
204           ::MOUNT_COMPLETED_STATUS_ERROR_INVALID_MOUNT_OPTIONS;
205     case chromeos::MOUNT_ERROR_INVALID_UNMOUNT_OPTIONS:
206       return file_browser_private::
207           MOUNT_COMPLETED_STATUS_ERROR_INVALID_UNMOUNT_OPTIONS;
208     case chromeos::MOUNT_ERROR_INSUFFICIENT_PERMISSIONS:
209       return file_browser_private::
210           MOUNT_COMPLETED_STATUS_ERROR_INSUFFICIENT_PERMISSIONS;
211     case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_NOT_FOUND:
212       return file_browser_private::
213           MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_NOT_FOUND;
214     case chromeos::MOUNT_ERROR_MOUNT_PROGRAM_FAILED:
215       return file_browser_private::
216           MOUNT_COMPLETED_STATUS_ERROR_MOUNT_PROGRAM_FAILED;
217     case chromeos::MOUNT_ERROR_INVALID_DEVICE_PATH:
218       return file_browser_private::
219           MOUNT_COMPLETED_STATUS_ERROR_INVALID_DEVICE_PATH;
220     case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
221       return file_browser_private::
222           MOUNT_COMPLETED_STATUS_ERROR_UNKNOWN_FILESYSTEM;
223     case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
224       return file_browser_private::
225           MOUNT_COMPLETED_STATUS_ERROR_UNSUPORTED_FILESYSTEM;
226     case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
227       return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_INVALID_ARCHIVE;
228     case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
229       return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_AUTHENTICATION;
230     case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
231       return file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
232   }
233   NOTREACHED();
234   return file_browser_private::MOUNT_COMPLETED_STATUS_NONE;
235 }
236
237 void BroadcastMountCompletedEvent(
238     Profile* profile,
239     file_browser_private::MountCompletedEventType event_type,
240     chromeos::MountError error,
241     const VolumeInfo& volume_info,
242     bool is_remounting) {
243   file_browser_private::MountCompletedEvent event;
244   event.event_type = event_type;
245   event.status = MountErrorToMountCompletedStatus(error);
246   util::VolumeInfoToVolumeMetadata(
247       profile, volume_info, &event.volume_metadata);
248   event.is_remounting = is_remounting;
249
250   if (!volume_info.mount_path.empty() &&
251       event.volume_metadata.mount_path.empty()) {
252     event.status =
253         file_browser_private::MOUNT_COMPLETED_STATUS_ERROR_PATH_UNMOUNTED;
254   }
255
256   BroadcastEvent(
257       profile,
258       file_browser_private::OnMountCompleted::kEventName,
259       file_browser_private::OnMountCompleted::Create(event));
260 }
261
262 file_browser_private::CopyProgressStatusType
263 CopyProgressTypeToCopyProgressStatusType(
264     fileapi::FileSystemOperation::CopyProgressType type) {
265   switch (type) {
266     case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY:
267       return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
268     case fileapi::FileSystemOperation::END_COPY_ENTRY:
269       return file_browser_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
270     case fileapi::FileSystemOperation::PROGRESS:
271       return file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
272   }
273   NOTREACHED();
274   return file_browser_private::COPY_PROGRESS_STATUS_TYPE_NONE;
275 }
276
277 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
278                                                  Profile* running_profile) {
279   extensions::ProcessManager* const process_manager =
280       extensions::ExtensionSystem::Get(running_profile)->process_manager();
281   if (!process_manager)
282     return;
283
284   extensions::ExtensionHost* const extension_host =
285       process_manager->GetBackgroundHostForExtension(kFileManagerAppId);
286   if (!extension_host || !extension_host->render_process_host())
287     return;
288
289   const int id = extension_host->render_process_host()->GetID();
290   file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
291 }
292
293 }  // namespace
294
295 // Pass dummy value to JobInfo's constructor for make it default constructible.
296 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
297     : job_info(drive::TYPE_DOWNLOAD_FILE) {
298 }
299
300 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
301     const drive::JobInfo& info, const std::string& status)
302     : job_info(info), status(status) {
303 }
304
305 EventRouter::EventRouter(Profile* profile)
306     : pref_change_registrar_(new PrefChangeRegistrar),
307       profile_(profile),
308       multi_user_window_manager_observer_registered_(false),
309       weak_factory_(this) {
310   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
311 }
312
313 EventRouter::~EventRouter() {
314 }
315
316 void EventRouter::Shutdown() {
317   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318
319   DLOG_IF(WARNING, !file_watchers_.empty())
320       << "Not all file watchers are "
321       << "removed. This can happen when Files.app is open during shutdown.";
322   STLDeleteValues(&file_watchers_);
323   if (!profile_) {
324     NOTREACHED();
325     return;
326   }
327
328   pref_change_registrar_->RemoveAll();
329
330   if (NetworkHandler::IsInitialized()) {
331     NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
332                                                                    FROM_HERE);
333   }
334
335   DriveIntegrationService* integration_service =
336       DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
337           profile_);
338   if (integration_service) {
339     integration_service->file_system()->RemoveObserver(this);
340     integration_service->drive_service()->RemoveObserver(this);
341     integration_service->job_list()->RemoveObserver(this);
342   }
343
344   VolumeManager* volume_manager = VolumeManager::Get(profile_);
345   if (volume_manager)
346     volume_manager->RemoveObserver(this);
347
348   chrome::MultiUserWindowManager* const multi_user_window_manager =
349       chrome::MultiUserWindowManager::GetInstance();
350   if (multi_user_window_manager &&
351       multi_user_window_manager_observer_registered_) {
352     multi_user_window_manager_observer_registered_ = false;
353     multi_user_window_manager->RemoveObserver(this);
354   }
355
356   profile_ = NULL;
357 }
358
359 void EventRouter::ObserveEvents() {
360   if (!profile_) {
361     NOTREACHED();
362     return;
363   }
364   if (!chromeos::LoginState::IsInitialized() ||
365       !chromeos::LoginState::Get()->IsUserLoggedIn()) {
366     return;
367   }
368
369   // VolumeManager's construction triggers DriveIntegrationService's
370   // construction, so it is necessary to call VolumeManager's Get before
371   // accessing DriveIntegrationService.
372   VolumeManager* volume_manager = VolumeManager::Get(profile_);
373   if (volume_manager)
374     volume_manager->AddObserver(this);
375
376   DriveIntegrationService* integration_service =
377       DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
378           profile_);
379   if (integration_service) {
380     integration_service->drive_service()->AddObserver(this);
381     integration_service->file_system()->AddObserver(this);
382     integration_service->job_list()->AddObserver(this);
383   }
384
385   if (NetworkHandler::IsInitialized()) {
386     NetworkHandler::Get()->network_state_handler()->AddObserver(this,
387                                                                 FROM_HERE);
388   }
389
390   pref_change_registrar_->Init(profile_->GetPrefs());
391   base::Closure callback =
392       base::Bind(&EventRouter::OnFileManagerPrefsChanged,
393                  weak_factory_.GetWeakPtr());
394   pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
395   pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
396   pref_change_registrar_->Add(prefs::kDisableDrive, callback);
397   pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
398
399   notification_registrar_.Add(this,
400                               chrome::NOTIFICATION_PROFILE_ADDED,
401                               content::NotificationService::AllSources());
402 }
403
404 // File watch setup routines.
405 void EventRouter::AddFileWatch(const base::FilePath& local_path,
406                                const base::FilePath& virtual_path,
407                                const std::string& extension_id,
408                                const BoolCallback& callback) {
409   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410   DCHECK(!callback.is_null());
411
412   base::FilePath watch_path = local_path;
413   bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
414   // Tweak watch path for remote sources - we need to drop leading /special
415   // directory from there in order to be able to pair these events with
416   // their change notifications.
417   if (is_on_drive)
418     watch_path = drive::util::ExtractDrivePath(watch_path);
419
420   WatcherMap::iterator iter = file_watchers_.find(watch_path);
421   if (iter == file_watchers_.end()) {
422     scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
423     watcher->AddExtension(extension_id);
424
425     if (is_on_drive) {
426       // For Drive, file watching is done via OnDirectoryChanged().
427       base::MessageLoopProxy::current()->PostTask(FROM_HERE,
428                                                   base::Bind(callback, true));
429     } else {
430       // For local files, start watching using FileWatcher.
431       watcher->WatchLocalFile(
432           watch_path,
433           base::Bind(&EventRouter::HandleFileWatchNotification,
434                      weak_factory_.GetWeakPtr()),
435           callback);
436     }
437
438     file_watchers_[watch_path] = watcher.release();
439   } else {
440     iter->second->AddExtension(extension_id);
441     base::MessageLoopProxy::current()->PostTask(FROM_HERE,
442                                                 base::Bind(callback, true));
443   }
444 }
445
446 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
447                                   const std::string& extension_id) {
448   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
449
450   base::FilePath watch_path = local_path;
451   // Tweak watch path for remote sources - we need to drop leading /special
452   // directory from there in order to be able to pair these events with
453   // their change notifications.
454   if (drive::util::IsUnderDriveMountPoint(watch_path)) {
455     watch_path = drive::util::ExtractDrivePath(watch_path);
456   }
457   WatcherMap::iterator iter = file_watchers_.find(watch_path);
458   if (iter == file_watchers_.end())
459     return;
460   // Remove the watcher if |watch_path| is no longer watched by any extensions.
461   iter->second->RemoveExtension(extension_id);
462   if (iter->second->GetExtensionIds().empty()) {
463     delete iter->second;
464     file_watchers_.erase(iter);
465   }
466 }
467
468 void EventRouter::OnCopyCompleted(int copy_id,
469                                   const GURL& source_url,
470                                   const GURL& destination_url,
471                                   base::File::Error error) {
472   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
473
474   file_browser_private::CopyProgressStatus status;
475   if (error == base::File::FILE_OK) {
476     // Send success event.
477     status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
478     status.source_url.reset(new std::string(source_url.spec()));
479     status.destination_url.reset(new std::string(destination_url.spec()));
480   } else {
481     // Send error event.
482     status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
483     status.error.reset(
484         new int(fileapi::FileErrorToWebFileError(error)));
485   }
486
487   BroadcastEvent(
488       profile_,
489       file_browser_private::OnCopyProgress::kEventName,
490       file_browser_private::OnCopyProgress::Create(copy_id, status));
491 }
492
493 void EventRouter::OnCopyProgress(
494     int copy_id,
495     fileapi::FileSystemOperation::CopyProgressType type,
496     const GURL& source_url,
497     const GURL& destination_url,
498     int64 size) {
499   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
500
501   file_browser_private::CopyProgressStatus status;
502   status.type = CopyProgressTypeToCopyProgressStatusType(type);
503   status.source_url.reset(new std::string(source_url.spec()));
504   if (type == fileapi::FileSystemOperation::END_COPY_ENTRY)
505     status.destination_url.reset(new std::string(destination_url.spec()));
506   if (type == fileapi::FileSystemOperation::PROGRESS)
507     status.size.reset(new double(size));
508
509   BroadcastEvent(
510       profile_,
511       file_browser_private::OnCopyProgress::kEventName,
512       file_browser_private::OnCopyProgress::Create(copy_id, status));
513 }
514
515 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
516   if (!profile_ ||
517       !extensions::ExtensionSystem::Get(profile_)->event_router()) {
518     NOTREACHED();
519     return;
520   }
521
522   BroadcastEvent(
523       profile_,
524       file_browser_private::OnDriveConnectionStatusChanged::kEventName,
525       file_browser_private::OnDriveConnectionStatusChanged::Create());
526 }
527
528 void EventRouter::OnFileManagerPrefsChanged() {
529   if (!profile_ ||
530       !extensions::ExtensionSystem::Get(profile_)->event_router()) {
531     NOTREACHED();
532     return;
533   }
534
535   BroadcastEvent(
536       profile_,
537       file_browser_private::OnPreferencesChanged::kEventName,
538       file_browser_private::OnPreferencesChanged::Create());
539 }
540
541 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
542   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543   OnJobUpdated(job_info);
544 }
545
546 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
547   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
548   if (!drive::IsActiveFileTransferJobInfo(job_info))
549     return;
550
551   bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
552
553   // Replace with the latest job info.
554   drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
555       job_info,
556       is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress);
557
558   // Fire event if needed.
559   bool always = is_new_job;
560   SendDriveFileTransferEvent(always);
561 }
562
563 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
564                             drive::FileError error) {
565   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566   if (!drive::IsActiveFileTransferJobInfo(job_info))
567     return;
568
569   // Replace with the latest job info.
570   drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
571       job_info,
572       error == drive::FILE_ERROR_OK ? kFileTransferStateCompleted
573       : kFileTransferStateFailed);
574
575   // Fire event if needed.
576   bool always = true;
577   SendDriveFileTransferEvent(always);
578
579   // Forget about the job.
580   drive_jobs_.erase(job_info.job_id);
581 }
582
583 void EventRouter::SendDriveFileTransferEvent(bool always) {
584   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
585
586   const base::Time now = base::Time::Now();
587
588   // When |always| flag is not set, we don't send the event until certain
589   // amount of time passes after the previous one. This is to avoid
590   // flooding the IPC between extensions by many onFileTransferUpdated events.
591   if (!always) {
592     const int64 delta = (now - last_file_transfer_event_).InMilliseconds();
593     // delta < 0 may rarely happen if system clock is synced and rewinded.
594     // To be conservative, we don't skip in that case.
595     if (0 <= delta && delta < kFileTransferEventFrequencyInMilliseconds)
596       return;
597   }
598
599   // Convert the current |drive_jobs_| to IDL type.
600   std::vector<linked_ptr<file_browser_private::FileTransferStatus> >
601       status_list;
602   for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
603            iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
604     linked_ptr<file_browser_private::FileTransferStatus> status(
605         new file_browser_private::FileTransferStatus());
606     JobInfoToTransferStatus(profile_,
607                             kFileManagerAppId,
608                             iter->second.status,
609                             iter->second.job_info,
610                             status.get());
611     status_list.push_back(status);
612   }
613   BroadcastEvent(
614       profile_,
615       file_browser_private::OnFileTransfersUpdated::kEventName,
616       file_browser_private::OnFileTransfersUpdated::Create(status_list));
617   last_file_transfer_event_ = now;
618 }
619
620 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
621   HandleFileWatchNotification(drive_path, false);
622 }
623
624 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
625                                    const base::FilePath& drive_path) {
626   file_browser_private::DriveSyncErrorEvent event;
627   switch (type) {
628     case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
629       event.type =
630           file_browser_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
631       break;
632     case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
633       event.type =
634           file_browser_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
635       break;
636     case drive::file_system::DRIVE_SYNC_ERROR_MISC:
637       event.type =
638           file_browser_private::DRIVE_SYNC_ERROR_TYPE_MISC;
639       break;
640   }
641   event.file_url = util::ConvertDrivePathToFileSystemUrl(
642       profile_, drive_path, kFileManagerAppId).spec();
643   BroadcastEvent(
644       profile_,
645       file_browser_private::OnDriveSyncError::kEventName,
646       file_browser_private::OnDriveSyncError::Create(event));
647 }
648
649 void EventRouter::OnRefreshTokenInvalid() {
650   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
651
652   // Raise a DriveConnectionStatusChanged event to notify the status offline.
653   BroadcastEvent(
654       profile_,
655       file_browser_private::OnDriveConnectionStatusChanged::kEventName,
656       file_browser_private::OnDriveConnectionStatusChanged::Create());
657 }
658
659 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path,
660                                               bool got_error) {
661   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
662
663   WatcherMap::const_iterator iter = file_watchers_.find(local_path);
664   if (iter == file_watchers_.end()) {
665     return;
666   }
667   DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
668                                iter->second->GetExtensionIds());
669 }
670
671 void EventRouter::DispatchDirectoryChangeEvent(
672     const base::FilePath& virtual_path,
673     bool got_error,
674     const std::vector<std::string>& extension_ids) {
675   if (!profile_) {
676     NOTREACHED();
677     return;
678   }
679
680   for (size_t i = 0; i < extension_ids.size(); ++i) {
681     const std::string& extension_id = extension_ids[i];
682     const GURL target_origin_url(
683         extensions::Extension::GetBaseURLFromExtensionId(extension_id));
684     // This will be replaced with a real Entry in custom bindings.
685     const fileapi::FileSystemInfo info =
686         fileapi::GetFileSystemInfoForChromeOS(target_origin_url.GetOrigin());
687
688     file_browser_private::FileWatchEvent event;
689     event.event_type = got_error ?
690         file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR :
691         file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
692     event.entry.additional_properties.SetString("fileSystemName", info.name);
693     event.entry.additional_properties.SetString("fileSystemRoot",
694                                                 info.root_url.spec());
695     event.entry.additional_properties.SetString("fileFullPath",
696                                                 "/" + virtual_path.value());
697     event.entry.additional_properties.SetBoolean("fileIsDirectory", true);
698
699     BroadcastEvent(
700         profile_,
701         file_browser_private::OnDirectoryChanged::kEventName,
702         file_browser_private::OnDirectoryChanged::Create(event));
703   }
704 }
705
706 void EventRouter::ShowRemovableDeviceInFileManager(
707     const base::FilePath& mount_path) {
708   // Do not attempt to open File Manager while the login is in progress or
709   // the screen is locked or running in kiosk app mode and make sure the file
710   // manager is opened only for the active user.
711   if (chromeos::LoginDisplayHostImpl::default_host() ||
712       chromeos::ScreenLocker::default_screen_locker() ||
713       chrome::IsRunningInForcedAppMode() ||
714       profile_ != ProfileManager::GetActiveUserProfile())
715     return;
716
717   // Do not pop-up the File Manager, if the recovery tool is running.
718   if (IsRecoveryToolRunning(profile_))
719     return;
720
721   // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
722   // cameras should have pictures located in the DCIM root directory.
723   const base::FilePath dcim_path = mount_path.Append(
724       FILE_PATH_LITERAL("DCIM"));
725
726   // If there is a DCIM folder and Google+ Photos is installed, then do not
727   // launch Files.app.
728   DirectoryExistsOnUIThread(
729       dcim_path,
730       IsGooglePhotosInstalled(profile_)
731           ? base::Bind(&base::DoNothing)
732           : base::Bind(&util::OpenRemovableDrive, profile_, mount_path),
733       base::Bind(&util::OpenRemovableDrive, profile_, mount_path));
734 }
735
736 void EventRouter::DispatchDeviceEvent(
737     file_browser_private::DeviceEventType type,
738     const std::string& device_path) {
739   file_browser_private::DeviceEvent event;
740   event.type = type;
741   event.device_path = device_path;
742   BroadcastEvent(profile_,
743                  file_browser_private::OnDeviceChanged::kEventName,
744                  file_browser_private::OnDeviceChanged::Create(event));
745 }
746
747 void EventRouter::OnDiskAdded(
748     const DiskMountManager::Disk& disk, bool mounting) {
749   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
750
751   if (!mounting) {
752     // If the disk is not being mounted, we don't want the Scanning
753     // notification to persist.
754     DispatchDeviceEvent(
755         file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
756         disk.system_path_prefix());
757   }
758 }
759
760 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
761   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
762   // Do nothing.
763 }
764
765 void EventRouter::OnDeviceAdded(const std::string& device_path) {
766   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
767
768   // If the policy is set instead of showing the new device notification,
769   // we show a notification that the operation is not permitted.
770   if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
771     DispatchDeviceEvent(
772         file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
773         device_path);
774     return;
775   }
776
777   DispatchDeviceEvent(
778       file_browser_private::DEVICE_EVENT_TYPE_ADDED,
779       device_path);
780 }
781
782 void EventRouter::OnDeviceRemoved(const std::string& device_path) {
783   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
784
785   DispatchDeviceEvent(
786       file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
787       device_path);
788 }
789
790 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
791                                   const VolumeInfo& volume_info,
792                                   bool is_remounting) {
793   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
794   // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
795   // happen at shutdown. This should be removed after removing Drive mounting
796   // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
797   // the only path to come here after Shutdown is called).
798   if (!profile_)
799     return;
800
801   BroadcastMountCompletedEvent(
802       profile_,
803       file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
804       error_code,
805       volume_info,
806       is_remounting);
807
808   if (volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION &&
809       !is_remounting) {
810     // If a new device was mounted, a new File manager window may need to be
811     // opened.
812     if (error_code == chromeos::MOUNT_ERROR_NONE)
813       ShowRemovableDeviceInFileManager(volume_info.mount_path);
814   }
815 }
816
817 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
818                                     const VolumeInfo& volume_info) {
819   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
820   BroadcastMountCompletedEvent(
821       profile_,
822       file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
823       error_code,
824       volume_info,
825       false);
826 }
827
828 void EventRouter::OnFormatStarted(const std::string& device_path,
829                                   bool success) {
830   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831
832   if (success) {
833     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
834                         device_path);
835   } else {
836     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
837                         device_path);
838   }
839 }
840
841 void EventRouter::OnFormatCompleted(const std::string& device_path,
842                                     bool success) {
843   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
844   DispatchDeviceEvent(success ?
845                       file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
846                       file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
847                       device_path);
848 }
849
850 void EventRouter::Observe(int type,
851                           const content::NotificationSource& source,
852                           const content::NotificationDetails& details) {
853   if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
854     Profile* const added_profile = content::Source<Profile>(source).ptr();
855     if (!added_profile->IsOffTheRecord())
856       GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
857
858     BroadcastEvent(profile_,
859                    file_browser_private::OnProfileAdded::kEventName,
860                    file_browser_private::OnProfileAdded::Create());
861   }
862 }
863
864 void EventRouter::RegisterMultiUserWindowManagerObserver() {
865   if (multi_user_window_manager_observer_registered_)
866     return;
867   chrome::MultiUserWindowManager* const multi_user_window_manager =
868       chrome::MultiUserWindowManager::GetInstance();
869   if (multi_user_window_manager) {
870     multi_user_window_manager->AddObserver(this);
871     multi_user_window_manager_observer_registered_ = true;
872   }
873 }
874
875 void EventRouter::OnOwnerEntryChanged(aura::Window* window) {
876   BroadcastEvent(profile_,
877                  file_browser_private::OnDesktopChanged::kEventName,
878                  file_browser_private::OnDesktopChanged::Create());
879 }
880
881 }  // namespace file_manager