• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/lock/screen_locker.h"
26 #include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
27 #include "chrome/browser/drive/drive_service_interface.h"
28 #include "chrome/browser/extensions/extension_service.h"
29 #include "chrome/browser/profiles/profile.h"
30 #include "chrome/browser/profiles/profile_manager.h"
31 #include "chrome/common/pref_names.h"
32 #include "chromeos/login/login_state.h"
33 #include "chromeos/network/network_handler.h"
34 #include "chromeos/network/network_state_handler.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/notification_source.h"
37 #include "content/public/browser/render_process_host.h"
38 #include "extensions/browser/event_router.h"
39 #include "extensions/browser/extension_host.h"
40 #include "extensions/browser/extension_prefs.h"
41 #include "extensions/browser/extension_system.h"
42 #include "webkit/common/fileapi/file_system_types.h"
43 #include "webkit/common/fileapi/file_system_util.h"
44 
45 using chromeos::disks::DiskMountManager;
46 using chromeos::NetworkHandler;
47 using content::BrowserThread;
48 using drive::DriveIntegrationService;
49 using drive::DriveIntegrationServiceFactory;
50 using file_manager::util::EntryDefinition;
51 using file_manager::util::FileDefinition;
52 
53 namespace file_browser_private = extensions::api::file_browser_private;
54 
55 namespace file_manager {
56 namespace {
57 
DirectoryExistsOnBlockingPool(const base::FilePath & directory_path,const base::Closure & success_callback,const base::Closure & failure_callback)58 void DirectoryExistsOnBlockingPool(const base::FilePath& directory_path,
59                                    const base::Closure& success_callback,
60                                    const base::Closure& failure_callback) {
61   DCHECK(BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread());
62 
63   if (base::DirectoryExists(directory_path))
64     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, success_callback);
65   else
66     BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, failure_callback);
67 }
68 
DirectoryExistsOnUIThread(const base::FilePath & directory_path,const base::Closure & success_callback,const base::Closure & failure_callback)69 void DirectoryExistsOnUIThread(const base::FilePath& directory_path,
70                                const base::Closure& success_callback,
71                                const base::Closure& failure_callback) {
72   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
73 
74   content::BrowserThread::PostBlockingPoolTask(
75       FROM_HERE,
76       base::Bind(&DirectoryExistsOnBlockingPool,
77                  directory_path,
78                  success_callback,
79                  failure_callback));
80 }
81 
82 // Constants for the "transferState" field of onFileTransferUpdated event.
83 const char kFileTransferStateStarted[] = "started";
84 const char kFileTransferStateInProgress[] = "in_progress";
85 const char kFileTransferStateCompleted[] = "completed";
86 const char kFileTransferStateFailed[] = "failed";
87 
88 // Frequency of sending onFileTransferUpdated.
89 const int64 kProgressEventFrequencyInMilliseconds = 1000;
90 
91 // Utility function to check if |job_info| is a file uploading job.
IsUploadJob(drive::JobType type)92 bool IsUploadJob(drive::JobType type) {
93   return (type == drive::TYPE_UPLOAD_NEW_FILE ||
94           type == drive::TYPE_UPLOAD_EXISTING_FILE);
95 }
96 
97 // Converts the job info to a IDL generated type.
JobInfoToTransferStatus(Profile * profile,const std::string & extension_id,const std::string & job_status,const drive::JobInfo & job_info,file_browser_private::FileTransferStatus * status)98 void JobInfoToTransferStatus(
99     Profile* profile,
100     const std::string& extension_id,
101     const std::string& job_status,
102     const drive::JobInfo& job_info,
103     file_browser_private::FileTransferStatus* status) {
104   DCHECK(IsActiveFileTransferJobInfo(job_info));
105 
106   scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue);
107   GURL url = util::ConvertDrivePathToFileSystemUrl(
108       profile, job_info.file_path, extension_id);
109   status->file_url = url.spec();
110   status->transfer_state = file_browser_private::ParseTransferState(job_status);
111   status->transfer_type =
112       IsUploadJob(job_info.job_type) ?
113       file_browser_private::TRANSFER_TYPE_UPLOAD :
114       file_browser_private::TRANSFER_TYPE_DOWNLOAD;
115   // JavaScript does not have 64-bit integers. Instead we use double, which
116   // is in IEEE 754 formant and accurate up to 52-bits in JS, and in practice
117   // in C++. Larger values are rounded.
118   status->processed.reset(
119       new double(static_cast<double>(job_info.num_completed_bytes)));
120   status->total.reset(
121       new double(static_cast<double>(job_info.num_total_bytes)));
122 }
123 
124 // Checks for availability of the Google+ Photos app.
125 // TODO(mtomasz): Replace with crbug.com/341902 solution.
IsGooglePhotosInstalled(Profile * profile)126 bool IsGooglePhotosInstalled(Profile *profile) {
127   ExtensionService* service =
128       extensions::ExtensionSystem::Get(profile)->extension_service();
129   if (!service)
130     return false;
131 
132   // Google+ Photos uses several ids for different channels. Therefore, all of
133   // them should be checked.
134   const std::string kGooglePlusPhotosIds[] = {
135       "ebpbnabdhheoknfklmpddcdijjkmklkp",  // G+ Photos staging
136       "efjnaogkjbogokcnohkmnjdojkikgobo",  // G+ Photos prod
137       "ejegoaikibpmikoejfephaneibodccma"   // G+ Photos dev
138   };
139 
140   for (size_t i = 0; i < arraysize(kGooglePlusPhotosIds); ++i) {
141     if (service->GetExtensionById(kGooglePlusPhotosIds[i],
142                                   false /* include_disable */) != NULL)
143       return true;
144   }
145 
146   return false;
147 }
148 
149 // Checks if the Recovery Tool is running. This is a temporary solution.
150 // TODO(mtomasz): Replace with crbug.com/341902 solution.
IsRecoveryToolRunning(Profile * profile)151 bool IsRecoveryToolRunning(Profile* profile) {
152   extensions::ExtensionPrefs* extension_prefs =
153       extensions::ExtensionPrefs::Get(profile);
154   if (!extension_prefs)
155     return false;
156 
157   const std::string kRecoveryToolIds[] = {
158       "kkebgepbbgbcmghedmmdfcbdcodlkngh",  // Recovery tool staging
159       "jndclpdbaamdhonoechobihbbiimdgai"   // Recovery tool prod
160   };
161 
162   for (size_t i = 0; i < arraysize(kRecoveryToolIds); ++i) {
163     const std::string extension_id = kRecoveryToolIds[i];
164     if (extension_prefs->IsExtensionRunning(extension_id))
165       return true;
166   }
167 
168   return false;
169 }
170 
171 // Sends an event named |event_name| with arguments |event_args| to extensions.
BroadcastEvent(Profile * profile,const std::string & event_name,scoped_ptr<base::ListValue> event_args)172 void BroadcastEvent(Profile* profile,
173                     const std::string& event_name,
174                     scoped_ptr<base::ListValue> event_args) {
175   extensions::EventRouter::Get(profile)->BroadcastEvent(
176       make_scoped_ptr(new extensions::Event(event_name, event_args.Pass())));
177 }
178 
179 file_browser_private::MountCompletedStatus
MountErrorToMountCompletedStatus(chromeos::MountError error)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 
BroadcastMountCompletedEvent(Profile * profile,file_browser_private::MountCompletedEventType event_type,chromeos::MountError error,const VolumeInfo & volume_info,bool is_remounting)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   BroadcastEvent(
251       profile,
252       file_browser_private::OnMountCompleted::kEventName,
253       file_browser_private::OnMountCompleted::Create(event));
254 }
255 
256 file_browser_private::CopyProgressStatusType
CopyProgressTypeToCopyProgressStatusType(fileapi::FileSystemOperation::CopyProgressType type)257 CopyProgressTypeToCopyProgressStatusType(
258     fileapi::FileSystemOperation::CopyProgressType type) {
259   switch (type) {
260     case fileapi::FileSystemOperation::BEGIN_COPY_ENTRY:
261       return file_browser_private::COPY_PROGRESS_STATUS_TYPE_BEGIN_COPY_ENTRY;
262     case fileapi::FileSystemOperation::END_COPY_ENTRY:
263       return file_browser_private::COPY_PROGRESS_STATUS_TYPE_END_COPY_ENTRY;
264     case fileapi::FileSystemOperation::PROGRESS:
265       return file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
266   }
267   NOTREACHED();
268   return file_browser_private::COPY_PROGRESS_STATUS_TYPE_NONE;
269 }
270 
FileErrorToErrorName(base::File::Error error_code)271 std::string FileErrorToErrorName(base::File::Error error_code) {
272   namespace js = extensions::api::file_browser_private;
273   switch (error_code) {
274     case base::File::FILE_ERROR_NOT_FOUND:
275       return "NotFoundError";
276     case base::File::FILE_ERROR_INVALID_OPERATION:
277     case base::File::FILE_ERROR_EXISTS:
278     case base::File::FILE_ERROR_NOT_EMPTY:
279       return "InvalidModificationError";
280     case base::File::FILE_ERROR_NOT_A_DIRECTORY:
281     case base::File::FILE_ERROR_NOT_A_FILE:
282       return "TypeMismatchError";
283     case base::File::FILE_ERROR_ACCESS_DENIED:
284       return "NoModificationAllowedError";
285     case base::File::FILE_ERROR_FAILED:
286       return "InvalidStateError";
287     case base::File::FILE_ERROR_ABORT:
288       return "AbortError";
289     case base::File::FILE_ERROR_SECURITY:
290       return "SecurityError";
291     case base::File::FILE_ERROR_NO_SPACE:
292       return "QuotaExceededError";
293     case base::File::FILE_ERROR_INVALID_URL:
294       return "EncodingError";
295     default:
296       return "InvalidModificationError";
297   }
298 }
299 
GrantAccessForAddedProfileToRunningInstance(Profile * added_profile,Profile * running_profile)300 void GrantAccessForAddedProfileToRunningInstance(Profile* added_profile,
301                                                  Profile* running_profile) {
302   extensions::ProcessManager* const process_manager =
303       extensions::ExtensionSystem::Get(running_profile)->process_manager();
304   if (!process_manager)
305     return;
306 
307   extensions::ExtensionHost* const extension_host =
308       process_manager->GetBackgroundHostForExtension(kFileManagerAppId);
309   if (!extension_host || !extension_host->render_process_host())
310     return;
311 
312   const int id = extension_host->render_process_host()->GetID();
313   file_manager::util::SetupProfileFileAccessPermissions(id, added_profile);
314 }
315 
316 // Checks if we should send a progress event or not according to the
317 // |last_time| of sending an event. If |always| is true, the function always
318 // returns true. If the function returns true, the function also updates
319 // |last_time|.
ShouldSendProgressEvent(bool always,base::Time * last_time)320 bool ShouldSendProgressEvent(bool always, base::Time* last_time) {
321   const base::Time now = base::Time::Now();
322   const int64 delta = (now - *last_time).InMilliseconds();
323   // delta < 0 may rarely happen if system clock is synced and rewinded.
324   // To be conservative, we don't skip in that case.
325   if (!always && 0 <= delta && delta < kProgressEventFrequencyInMilliseconds) {
326     return false;
327   } else {
328     *last_time = now;
329     return true;
330   }
331 }
332 
333 }  // namespace
334 
335 // Pass dummy value to JobInfo's constructor for make it default constructible.
DriveJobInfoWithStatus()336 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
337     : job_info(drive::TYPE_DOWNLOAD_FILE) {
338 }
339 
DriveJobInfoWithStatus(const drive::JobInfo & info,const std::string & status)340 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
341     const drive::JobInfo& info, const std::string& status)
342     : job_info(info), status(status) {
343 }
344 
EventRouter(Profile * profile)345 EventRouter::EventRouter(Profile* profile)
346     : pref_change_registrar_(new PrefChangeRegistrar),
347       profile_(profile),
348       multi_user_window_manager_observer_registered_(false),
349       weak_factory_(this) {
350   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
351 }
352 
~EventRouter()353 EventRouter::~EventRouter() {
354 }
355 
Shutdown()356 void EventRouter::Shutdown() {
357   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
358 
359   DLOG_IF(WARNING, !file_watchers_.empty())
360       << "Not all file watchers are "
361       << "removed. This can happen when Files.app is open during shutdown.";
362   STLDeleteValues(&file_watchers_);
363   if (!profile_) {
364     NOTREACHED();
365     return;
366   }
367 
368   pref_change_registrar_->RemoveAll();
369 
370   if (NetworkHandler::IsInitialized()) {
371     NetworkHandler::Get()->network_state_handler()->RemoveObserver(this,
372                                                                    FROM_HERE);
373   }
374 
375   DriveIntegrationService* integration_service =
376       DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
377           profile_);
378   if (integration_service) {
379     integration_service->file_system()->RemoveObserver(this);
380     integration_service->drive_service()->RemoveObserver(this);
381     integration_service->job_list()->RemoveObserver(this);
382   }
383 
384   VolumeManager* volume_manager = VolumeManager::Get(profile_);
385   if (volume_manager)
386     volume_manager->RemoveObserver(this);
387 
388   chrome::MultiUserWindowManager* const multi_user_window_manager =
389       chrome::MultiUserWindowManager::GetInstance();
390   if (multi_user_window_manager &&
391       multi_user_window_manager_observer_registered_) {
392     multi_user_window_manager_observer_registered_ = false;
393     multi_user_window_manager->RemoveObserver(this);
394   }
395 
396   profile_ = NULL;
397 }
398 
ObserveEvents()399 void EventRouter::ObserveEvents() {
400   if (!profile_) {
401     NOTREACHED();
402     return;
403   }
404   if (!chromeos::LoginState::IsInitialized() ||
405       !chromeos::LoginState::Get()->IsUserLoggedIn()) {
406     return;
407   }
408 
409   // VolumeManager's construction triggers DriveIntegrationService's
410   // construction, so it is necessary to call VolumeManager's Get before
411   // accessing DriveIntegrationService.
412   VolumeManager* volume_manager = VolumeManager::Get(profile_);
413   if (volume_manager)
414     volume_manager->AddObserver(this);
415 
416   DriveIntegrationService* integration_service =
417       DriveIntegrationServiceFactory::FindForProfileRegardlessOfStates(
418           profile_);
419   if (integration_service) {
420     integration_service->drive_service()->AddObserver(this);
421     integration_service->file_system()->AddObserver(this);
422     integration_service->job_list()->AddObserver(this);
423   }
424 
425   if (NetworkHandler::IsInitialized()) {
426     NetworkHandler::Get()->network_state_handler()->AddObserver(this,
427                                                                 FROM_HERE);
428   }
429 
430   pref_change_registrar_->Init(profile_->GetPrefs());
431   base::Closure callback =
432       base::Bind(&EventRouter::OnFileManagerPrefsChanged,
433                  weak_factory_.GetWeakPtr());
434   pref_change_registrar_->Add(prefs::kDisableDriveOverCellular, callback);
435   pref_change_registrar_->Add(prefs::kDisableDriveHostedFiles, callback);
436   pref_change_registrar_->Add(prefs::kDisableDrive, callback);
437   pref_change_registrar_->Add(prefs::kUse24HourClock, callback);
438 
439   notification_registrar_.Add(this,
440                               chrome::NOTIFICATION_PROFILE_ADDED,
441                               content::NotificationService::AllSources());
442 }
443 
444 // File watch setup routines.
AddFileWatch(const base::FilePath & local_path,const base::FilePath & virtual_path,const std::string & extension_id,const BoolCallback & callback)445 void EventRouter::AddFileWatch(const base::FilePath& local_path,
446                                const base::FilePath& virtual_path,
447                                const std::string& extension_id,
448                                const BoolCallback& callback) {
449   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450   DCHECK(!callback.is_null());
451 
452   base::FilePath watch_path = local_path;
453   bool is_on_drive = drive::util::IsUnderDriveMountPoint(watch_path);
454   // Tweak watch path for remote sources - we need to drop leading /special
455   // directory from there in order to be able to pair these events with
456   // their change notifications.
457   if (is_on_drive)
458     watch_path = drive::util::ExtractDrivePath(watch_path);
459 
460   WatcherMap::iterator iter = file_watchers_.find(watch_path);
461   if (iter == file_watchers_.end()) {
462     scoped_ptr<FileWatcher> watcher(new FileWatcher(virtual_path));
463     watcher->AddExtension(extension_id);
464 
465     if (is_on_drive) {
466       // For Drive, file watching is done via OnDirectoryChanged().
467       base::MessageLoopProxy::current()->PostTask(FROM_HERE,
468                                                   base::Bind(callback, true));
469     } else {
470       // For local files, start watching using FileWatcher.
471       watcher->WatchLocalFile(
472           watch_path,
473           base::Bind(&EventRouter::HandleFileWatchNotification,
474                      weak_factory_.GetWeakPtr()),
475           callback);
476     }
477 
478     file_watchers_[watch_path] = watcher.release();
479   } else {
480     iter->second->AddExtension(extension_id);
481     base::MessageLoopProxy::current()->PostTask(FROM_HERE,
482                                                 base::Bind(callback, true));
483   }
484 }
485 
RemoveFileWatch(const base::FilePath & local_path,const std::string & extension_id)486 void EventRouter::RemoveFileWatch(const base::FilePath& local_path,
487                                   const std::string& extension_id) {
488   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
489 
490   base::FilePath watch_path = local_path;
491   // Tweak watch path for remote sources - we need to drop leading /special
492   // directory from there in order to be able to pair these events with
493   // their change notifications.
494   if (drive::util::IsUnderDriveMountPoint(watch_path)) {
495     watch_path = drive::util::ExtractDrivePath(watch_path);
496   }
497   WatcherMap::iterator iter = file_watchers_.find(watch_path);
498   if (iter == file_watchers_.end())
499     return;
500   // Remove the watcher if |watch_path| is no longer watched by any extensions.
501   iter->second->RemoveExtension(extension_id);
502   if (iter->second->GetExtensionIds().empty()) {
503     delete iter->second;
504     file_watchers_.erase(iter);
505   }
506 }
507 
OnCopyCompleted(int copy_id,const GURL & source_url,const GURL & destination_url,base::File::Error error)508 void EventRouter::OnCopyCompleted(int copy_id,
509                                   const GURL& source_url,
510                                   const GURL& destination_url,
511                                   base::File::Error error) {
512   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
513 
514   file_browser_private::CopyProgressStatus status;
515   if (error == base::File::FILE_OK) {
516     // Send success event.
517     status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_SUCCESS;
518     status.source_url.reset(new std::string(source_url.spec()));
519     status.destination_url.reset(new std::string(destination_url.spec()));
520   } else {
521     // Send error event.
522     status.type = file_browser_private::COPY_PROGRESS_STATUS_TYPE_ERROR;
523     status.error.reset(new std::string(FileErrorToErrorName(error)));
524   }
525 
526   BroadcastEvent(
527       profile_,
528       file_browser_private::OnCopyProgress::kEventName,
529       file_browser_private::OnCopyProgress::Create(copy_id, status));
530 }
531 
OnCopyProgress(int copy_id,fileapi::FileSystemOperation::CopyProgressType type,const GURL & source_url,const GURL & destination_url,int64 size)532 void EventRouter::OnCopyProgress(
533     int copy_id,
534     fileapi::FileSystemOperation::CopyProgressType type,
535     const GURL& source_url,
536     const GURL& destination_url,
537     int64 size) {
538   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
539 
540   file_browser_private::CopyProgressStatus status;
541   status.type = CopyProgressTypeToCopyProgressStatusType(type);
542   status.source_url.reset(new std::string(source_url.spec()));
543   if (type == fileapi::FileSystemOperation::END_COPY_ENTRY)
544     status.destination_url.reset(new std::string(destination_url.spec()));
545   if (type == fileapi::FileSystemOperation::PROGRESS)
546     status.size.reset(new double(size));
547 
548   // Should not skip events other than TYPE_PROGRESS.
549   const bool always =
550       status.type != file_browser_private::COPY_PROGRESS_STATUS_TYPE_PROGRESS;
551   if (!ShouldSendProgressEvent(always, &last_copy_progress_event_))
552     return;
553 
554   BroadcastEvent(
555       profile_,
556       file_browser_private::OnCopyProgress::kEventName,
557       file_browser_private::OnCopyProgress::Create(copy_id, status));
558 }
559 
DefaultNetworkChanged(const chromeos::NetworkState * network)560 void EventRouter::DefaultNetworkChanged(const chromeos::NetworkState* network) {
561   if (!profile_ || !extensions::EventRouter::Get(profile_)) {
562     NOTREACHED();
563     return;
564   }
565 
566   BroadcastEvent(
567       profile_,
568       file_browser_private::OnDriveConnectionStatusChanged::kEventName,
569       file_browser_private::OnDriveConnectionStatusChanged::Create());
570 }
571 
OnFileManagerPrefsChanged()572 void EventRouter::OnFileManagerPrefsChanged() {
573   if (!profile_ || !extensions::EventRouter::Get(profile_)) {
574     NOTREACHED();
575     return;
576   }
577 
578   BroadcastEvent(
579       profile_,
580       file_browser_private::OnPreferencesChanged::kEventName,
581       file_browser_private::OnPreferencesChanged::Create());
582 }
583 
OnJobAdded(const drive::JobInfo & job_info)584 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
585   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
586   OnJobUpdated(job_info);
587 }
588 
OnJobUpdated(const drive::JobInfo & job_info)589 void EventRouter::OnJobUpdated(const drive::JobInfo& job_info) {
590   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
591   if (!drive::IsActiveFileTransferJobInfo(job_info))
592     return;
593 
594   bool is_new_job = (drive_jobs_.find(job_info.job_id) == drive_jobs_.end());
595 
596   // Replace with the latest job info.
597   drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
598       job_info,
599       is_new_job ? kFileTransferStateStarted : kFileTransferStateInProgress);
600 
601   // Fire event if needed.
602   bool always = is_new_job;
603   SendDriveFileTransferEvent(always);
604 }
605 
OnJobDone(const drive::JobInfo & job_info,drive::FileError error)606 void EventRouter::OnJobDone(const drive::JobInfo& job_info,
607                             drive::FileError error) {
608   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
609   if (!drive::IsActiveFileTransferJobInfo(job_info))
610     return;
611 
612   // Replace with the latest job info.
613   drive_jobs_[job_info.job_id] = DriveJobInfoWithStatus(
614       job_info,
615       error == drive::FILE_ERROR_OK ? kFileTransferStateCompleted
616       : kFileTransferStateFailed);
617 
618   // Fire event if needed.
619   bool always = true;
620   SendDriveFileTransferEvent(always);
621 
622   // Forget about the job.
623   drive_jobs_.erase(job_info.job_id);
624 }
625 
SendDriveFileTransferEvent(bool always)626 void EventRouter::SendDriveFileTransferEvent(bool always) {
627   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
628 
629   // When |always| flag is not set, we don't send the event until certain
630   // amount of time passes after the previous one. This is to avoid
631   // flooding the IPC between extensions by many onFileTransferUpdated events.
632   if (!ShouldSendProgressEvent(always, &last_file_transfer_event_))
633     return;
634 
635   // Convert the current |drive_jobs_| to IDL type.
636   std::vector<linked_ptr<file_browser_private::FileTransferStatus> >
637       status_list;
638   for (std::map<drive::JobID, DriveJobInfoWithStatus>::iterator
639            iter = drive_jobs_.begin(); iter != drive_jobs_.end(); ++iter) {
640     linked_ptr<file_browser_private::FileTransferStatus> status(
641         new file_browser_private::FileTransferStatus());
642     JobInfoToTransferStatus(profile_,
643                             kFileManagerAppId,
644                             iter->second.status,
645                             iter->second.job_info,
646                             status.get());
647     status_list.push_back(status);
648   }
649   BroadcastEvent(
650       profile_,
651       file_browser_private::OnFileTransfersUpdated::kEventName,
652       file_browser_private::OnFileTransfersUpdated::Create(status_list));
653 }
654 
OnDirectoryChanged(const base::FilePath & drive_path)655 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
656   HandleFileWatchNotification(drive_path, false);
657 }
658 
OnDriveSyncError(drive::file_system::DriveSyncErrorType type,const base::FilePath & drive_path)659 void EventRouter::OnDriveSyncError(drive::file_system::DriveSyncErrorType type,
660                                    const base::FilePath& drive_path) {
661   file_browser_private::DriveSyncErrorEvent event;
662   switch (type) {
663     case drive::file_system::DRIVE_SYNC_ERROR_DELETE_WITHOUT_PERMISSION:
664       event.type =
665           file_browser_private::DRIVE_SYNC_ERROR_TYPE_DELETE_WITHOUT_PERMISSION;
666       break;
667     case drive::file_system::DRIVE_SYNC_ERROR_SERVICE_UNAVAILABLE:
668       event.type =
669           file_browser_private::DRIVE_SYNC_ERROR_TYPE_SERVICE_UNAVAILABLE;
670       break;
671     case drive::file_system::DRIVE_SYNC_ERROR_MISC:
672       event.type =
673           file_browser_private::DRIVE_SYNC_ERROR_TYPE_MISC;
674       break;
675   }
676   event.file_url = util::ConvertDrivePathToFileSystemUrl(
677       profile_, drive_path, kFileManagerAppId).spec();
678   BroadcastEvent(
679       profile_,
680       file_browser_private::OnDriveSyncError::kEventName,
681       file_browser_private::OnDriveSyncError::Create(event));
682 }
683 
OnRefreshTokenInvalid()684 void EventRouter::OnRefreshTokenInvalid() {
685   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
686 
687   // Raise a DriveConnectionStatusChanged event to notify the status offline.
688   BroadcastEvent(
689       profile_,
690       file_browser_private::OnDriveConnectionStatusChanged::kEventName,
691       file_browser_private::OnDriveConnectionStatusChanged::Create());
692 }
693 
HandleFileWatchNotification(const base::FilePath & local_path,bool got_error)694 void EventRouter::HandleFileWatchNotification(const base::FilePath& local_path,
695                                               bool got_error) {
696   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
697 
698   WatcherMap::const_iterator iter = file_watchers_.find(local_path);
699   if (iter == file_watchers_.end()) {
700     return;
701   }
702   DispatchDirectoryChangeEvent(iter->second->virtual_path(), got_error,
703                                iter->second->GetExtensionIds());
704 }
705 
DispatchDirectoryChangeEvent(const base::FilePath & virtual_path,bool got_error,const std::vector<std::string> & extension_ids)706 void EventRouter::DispatchDirectoryChangeEvent(
707     const base::FilePath& virtual_path,
708     bool got_error,
709     const std::vector<std::string>& extension_ids) {
710   if (!profile_) {
711     NOTREACHED();
712     return;
713   }
714 
715   for (size_t i = 0; i < extension_ids.size(); ++i) {
716     const std::string& extension_id = extension_ids[i];
717 
718     FileDefinition file_definition;
719     file_definition.virtual_path = virtual_path;
720     file_definition.is_directory = true;
721 
722     file_manager::util::ConvertFileDefinitionToEntryDefinition(
723         profile_,
724         extension_id,
725         file_definition,
726         base::Bind(
727             &EventRouter::DispatchDirectoryChangeEventWithEntryDefinition,
728             weak_factory_.GetWeakPtr(),
729             got_error));
730   }
731 }
732 
DispatchDirectoryChangeEventWithEntryDefinition(bool watcher_error,const EntryDefinition & entry_definition)733 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
734     bool watcher_error,
735     const EntryDefinition& entry_definition) {
736   if (entry_definition.error != base::File::FILE_OK ||
737       !entry_definition.is_directory) {
738     DVLOG(1) << "Unable to dispatch event because resolving the directory "
739              << "entry definition failed.";
740     return;
741   }
742 
743   file_browser_private::FileWatchEvent event;
744   event.event_type = watcher_error
745       ? file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR
746       : file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
747 
748   event.entry.additional_properties.SetString(
749       "fileSystemName", entry_definition.file_system_name);
750   event.entry.additional_properties.SetString(
751       "fileSystemRoot", entry_definition.file_system_root_url);
752   event.entry.additional_properties.SetString(
753       "fileFullPath", "/" + entry_definition.full_path.value());
754   event.entry.additional_properties.SetBoolean("fileIsDirectory",
755                                                entry_definition.is_directory);
756 
757   BroadcastEvent(profile_,
758                  file_browser_private::OnDirectoryChanged::kEventName,
759                  file_browser_private::OnDirectoryChanged::Create(event));
760 }
761 
ShowRemovableDeviceInFileManager(VolumeType type,const base::FilePath & mount_path)762 void EventRouter::ShowRemovableDeviceInFileManager(
763     VolumeType type,
764     const base::FilePath& mount_path) {
765   // Do not attempt to open File Manager while the login is in progress or
766   // the screen is locked or running in kiosk app mode and make sure the file
767   // manager is opened only for the active user.
768   if (chromeos::LoginDisplayHostImpl::default_host() ||
769       chromeos::ScreenLocker::default_screen_locker() ||
770       chrome::IsRunningInForcedAppMode() ||
771       profile_ != ProfileManager::GetActiveUserProfile())
772     return;
773 
774   // Do not pop-up the File Manager, if the recovery tool is running.
775   if (IsRecoveryToolRunning(profile_))
776     return;
777 
778   // Do not pop-up the File Manager, if Google+ Photos may be launched.
779   if (IsGooglePhotosInstalled(profile_)) {
780     // MTP device is handled by Photos app.
781     if (type == VOLUME_TYPE_MTP)
782       return;
783     // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
784     // cameras should have pictures located in the DCIM root directory.
785     // Such removable disks are handled by Photos app.
786     if (type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
787       DirectoryExistsOnUIThread(
788           mount_path.AppendASCII("DCIM"),
789           base::Bind(&base::DoNothing),
790           base::Bind(&util::OpenRemovableDrive, profile_, mount_path));
791       return;
792     }
793   }
794 
795   util::OpenRemovableDrive(profile_, mount_path);
796 }
797 
DispatchDeviceEvent(file_browser_private::DeviceEventType type,const std::string & device_path)798 void EventRouter::DispatchDeviceEvent(
799     file_browser_private::DeviceEventType type,
800     const std::string& device_path) {
801   file_browser_private::DeviceEvent event;
802   event.type = type;
803   event.device_path = device_path;
804   BroadcastEvent(profile_,
805                  file_browser_private::OnDeviceChanged::kEventName,
806                  file_browser_private::OnDeviceChanged::Create(event));
807 }
808 
OnDiskAdded(const DiskMountManager::Disk & disk,bool mounting)809 void EventRouter::OnDiskAdded(
810     const DiskMountManager::Disk& disk, bool mounting) {
811   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
812 
813   if (!mounting) {
814     // If the disk is not being mounted, we don't want the Scanning
815     // notification to persist.
816     DispatchDeviceEvent(
817         file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
818         disk.system_path_prefix());
819   }
820 }
821 
OnDiskRemoved(const DiskMountManager::Disk & disk)822 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
823   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
824   // Do nothing.
825 }
826 
OnDeviceAdded(const std::string & device_path)827 void EventRouter::OnDeviceAdded(const std::string& device_path) {
828   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
829 
830   // If the policy is set instead of showing the new device notification,
831   // we show a notification that the operation is not permitted.
832   if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
833     DispatchDeviceEvent(
834         file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
835         device_path);
836     return;
837   }
838 
839   DispatchDeviceEvent(
840       file_browser_private::DEVICE_EVENT_TYPE_ADDED,
841       device_path);
842 }
843 
OnDeviceRemoved(const std::string & device_path,bool hard_unplugged)844 void EventRouter::OnDeviceRemoved(const std::string& device_path,
845                                   bool hard_unplugged) {
846   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
847 
848   DispatchDeviceEvent(
849       file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
850       device_path);
851 
852   if (hard_unplugged) {
853     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_HARD_UNPLUGGED,
854                         device_path);
855   }
856 }
857 
OnVolumeMounted(chromeos::MountError error_code,const VolumeInfo & volume_info,bool is_remounting)858 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
859                                   const VolumeInfo& volume_info,
860                                   bool is_remounting) {
861   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
862   // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
863   // happen at shutdown. This should be removed after removing Drive mounting
864   // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
865   // the only path to come here after Shutdown is called).
866   if (!profile_)
867     return;
868 
869   BroadcastMountCompletedEvent(
870       profile_,
871       file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
872       error_code,
873       volume_info,
874       is_remounting);
875 
876   if ((volume_info.type == VOLUME_TYPE_MTP ||
877        volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) &&
878       !is_remounting) {
879     // If a new device was mounted, a new File manager window may need to be
880     // opened.
881     if (error_code == chromeos::MOUNT_ERROR_NONE)
882       ShowRemovableDeviceInFileManager(volume_info.type,
883                                        volume_info.mount_path);
884   }
885 }
886 
OnVolumeUnmounted(chromeos::MountError error_code,const VolumeInfo & volume_info)887 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
888                                     const VolumeInfo& volume_info) {
889   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
890   BroadcastMountCompletedEvent(
891       profile_,
892       file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
893       error_code,
894       volume_info,
895       false);
896 }
897 
OnFormatStarted(const std::string & device_path,bool success)898 void EventRouter::OnFormatStarted(const std::string& device_path,
899                                   bool success) {
900   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
901 
902   if (success) {
903     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
904                         device_path);
905   } else {
906     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
907                         device_path);
908   }
909 }
910 
OnFormatCompleted(const std::string & device_path,bool success)911 void EventRouter::OnFormatCompleted(const std::string& device_path,
912                                     bool success) {
913   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
914   DispatchDeviceEvent(success ?
915                       file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
916                       file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
917                       device_path);
918 }
919 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)920 void EventRouter::Observe(int type,
921                           const content::NotificationSource& source,
922                           const content::NotificationDetails& details) {
923   if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
924     Profile* const added_profile = content::Source<Profile>(source).ptr();
925     if (!added_profile->IsOffTheRecord())
926       GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
927 
928     BroadcastEvent(profile_,
929                    file_browser_private::OnProfileAdded::kEventName,
930                    file_browser_private::OnProfileAdded::Create());
931   }
932 }
933 
RegisterMultiUserWindowManagerObserver()934 void EventRouter::RegisterMultiUserWindowManagerObserver() {
935   if (multi_user_window_manager_observer_registered_)
936     return;
937   chrome::MultiUserWindowManager* const multi_user_window_manager =
938       chrome::MultiUserWindowManager::GetInstance();
939   if (multi_user_window_manager) {
940     multi_user_window_manager->AddObserver(this);
941     multi_user_window_manager_observer_registered_ = true;
942   }
943 }
944 
OnOwnerEntryChanged(aura::Window * window)945 void EventRouter::OnOwnerEntryChanged(aura::Window* window) {
946   BroadcastEvent(profile_,
947                  file_browser_private::OnDesktopChanged::kEventName,
948                  file_browser_private::OnDesktopChanged::Create());
949 }
950 
951 }  // namespace file_manager
952