Upstream version 7.36.149.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/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
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
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.
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.
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.
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.
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",  // Recovert 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.
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
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   BroadcastEvent(
251       profile,
252       file_browser_private::OnMountCompleted::kEventName,
253       file_browser_private::OnMountCompleted::Create(event));
254 }
255
256 file_browser_private::CopyProgressStatusType
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
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
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|.
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.
336 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus()
337     : job_info(drive::TYPE_DOWNLOAD_FILE) {
338 }
339
340 EventRouter::DriveJobInfoWithStatus::DriveJobInfoWithStatus(
341     const drive::JobInfo& info, const std::string& status)
342     : job_info(info), status(status) {
343 }
344
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
353 EventRouter::~EventRouter() {
354 }
355
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
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.
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
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
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
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
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
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
584 void EventRouter::OnJobAdded(const drive::JobInfo& job_info) {
585   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
586   OnJobUpdated(job_info);
587 }
588
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
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
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
655 void EventRouter::OnDirectoryChanged(const base::FilePath& drive_path) {
656   HandleFileWatchNotification(drive_path, false);
657 }
658
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
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
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
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
733 void EventRouter::DispatchDirectoryChangeEventWithEntryDefinition(
734     bool watcher_error,
735     const EntryDefinition& entry_definition) {
736   if (entry_definition.error != base::File::FILE_OK) {
737     DVLOG(1) << "Unable to dispatch event because resolving the entry "
738              << "definition failed.";
739     return;
740   }
741
742   file_browser_private::FileWatchEvent event;
743   event.event_type = watcher_error
744       ? file_browser_private::FILE_WATCH_EVENT_TYPE_ERROR
745       : file_browser_private::FILE_WATCH_EVENT_TYPE_CHANGED;
746
747   event.entry.additional_properties.SetString(
748       "fileSystemName", entry_definition.file_system_name);
749   event.entry.additional_properties.SetString(
750       "fileSystemRoot", entry_definition.file_system_root_url);
751   event.entry.additional_properties.SetString(
752       "fileFullPath", "/" + entry_definition.full_path.value());
753   event.entry.additional_properties.SetBoolean("fileIsDirectory",
754                                                entry_definition.is_directory);
755
756   BroadcastEvent(profile_,
757                  file_browser_private::OnDirectoryChanged::kEventName,
758                  file_browser_private::OnDirectoryChanged::Create(event));
759 }
760
761 void EventRouter::ShowRemovableDeviceInFileManager(
762     VolumeType type,
763     const base::FilePath& mount_path) {
764   // Do not attempt to open File Manager while the login is in progress or
765   // the screen is locked or running in kiosk app mode and make sure the file
766   // manager is opened only for the active user.
767   if (chromeos::LoginDisplayHostImpl::default_host() ||
768       chromeos::ScreenLocker::default_screen_locker() ||
769       chrome::IsRunningInForcedAppMode() ||
770       profile_ != ProfileManager::GetActiveUserProfile())
771     return;
772
773   // Do not pop-up the File Manager, if the recovery tool is running.
774   if (IsRecoveryToolRunning(profile_))
775     return;
776
777   // Do not pop-up the File Manager, if Google+ Photos may be launched.
778   if (IsGooglePhotosInstalled(profile_)) {
779     // MTP device is handled by Photos app.
780     if (type == VOLUME_TYPE_MTP)
781       return;
782     // According to DCF (Design rule of Camera File system) by JEITA / CP-3461
783     // cameras should have pictures located in the DCIM root directory.
784     // Such removable disks are handled by Photos app.
785     if (type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) {
786       DirectoryExistsOnUIThread(
787           mount_path.AppendASCII("DCIM"),
788           base::Bind(&base::DoNothing),
789           base::Bind(&util::OpenRemovableDrive, profile_, mount_path));
790       return;
791     }
792   }
793
794   util::OpenRemovableDrive(profile_, mount_path);
795 }
796
797 void EventRouter::DispatchDeviceEvent(
798     file_browser_private::DeviceEventType type,
799     const std::string& device_path) {
800   file_browser_private::DeviceEvent event;
801   event.type = type;
802   event.device_path = device_path;
803   BroadcastEvent(profile_,
804                  file_browser_private::OnDeviceChanged::kEventName,
805                  file_browser_private::OnDeviceChanged::Create(event));
806 }
807
808 void EventRouter::OnDiskAdded(
809     const DiskMountManager::Disk& disk, bool mounting) {
810   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
811
812   if (!mounting) {
813     // If the disk is not being mounted, we don't want the Scanning
814     // notification to persist.
815     DispatchDeviceEvent(
816         file_browser_private::DEVICE_EVENT_TYPE_SCAN_CANCELED,
817         disk.system_path_prefix());
818   }
819 }
820
821 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
822   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
823   // Do nothing.
824 }
825
826 void EventRouter::OnDeviceAdded(const std::string& device_path) {
827   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
828
829   // If the policy is set instead of showing the new device notification,
830   // we show a notification that the operation is not permitted.
831   if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
832     DispatchDeviceEvent(
833         file_browser_private::DEVICE_EVENT_TYPE_DISABLED,
834         device_path);
835     return;
836   }
837
838   DispatchDeviceEvent(
839       file_browser_private::DEVICE_EVENT_TYPE_ADDED,
840       device_path);
841 }
842
843 void EventRouter::OnDeviceRemoved(const std::string& device_path,
844                                   bool hard_unplugged) {
845   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
846
847   DispatchDeviceEvent(
848       file_browser_private::DEVICE_EVENT_TYPE_REMOVED,
849       device_path);
850
851   if (hard_unplugged) {
852     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_HARD_UNPLUGGED,
853                         device_path);
854   }
855 }
856
857 void EventRouter::OnVolumeMounted(chromeos::MountError error_code,
858                                   const VolumeInfo& volume_info,
859                                   bool is_remounting) {
860   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
861   // profile_ is NULL if ShutdownOnUIThread() is called earlier. This can
862   // happen at shutdown. This should be removed after removing Drive mounting
863   // code in addMount. (addMount -> OnFileSystemMounted -> OnVolumeMounted is
864   // the only path to come here after Shutdown is called).
865   if (!profile_)
866     return;
867
868   BroadcastMountCompletedEvent(
869       profile_,
870       file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_MOUNT,
871       error_code,
872       volume_info,
873       is_remounting);
874
875   if ((volume_info.type == VOLUME_TYPE_MTP ||
876        volume_info.type == VOLUME_TYPE_REMOVABLE_DISK_PARTITION) &&
877       !is_remounting) {
878     // If a new device was mounted, a new File manager window may need to be
879     // opened.
880     if (error_code == chromeos::MOUNT_ERROR_NONE)
881       ShowRemovableDeviceInFileManager(volume_info.type,
882                                        volume_info.mount_path);
883   }
884 }
885
886 void EventRouter::OnVolumeUnmounted(chromeos::MountError error_code,
887                                     const VolumeInfo& volume_info) {
888   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
889   BroadcastMountCompletedEvent(
890       profile_,
891       file_browser_private::MOUNT_COMPLETED_EVENT_TYPE_UNMOUNT,
892       error_code,
893       volume_info,
894       false);
895 }
896
897 void EventRouter::OnFormatStarted(const std::string& device_path,
898                                   bool success) {
899   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
900
901   if (success) {
902     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_START,
903                         device_path);
904   } else {
905     DispatchDeviceEvent(file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
906                         device_path);
907   }
908 }
909
910 void EventRouter::OnFormatCompleted(const std::string& device_path,
911                                     bool success) {
912   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
913   DispatchDeviceEvent(success ?
914                       file_browser_private::DEVICE_EVENT_TYPE_FORMAT_SUCCESS :
915                       file_browser_private::DEVICE_EVENT_TYPE_FORMAT_FAIL,
916                       device_path);
917 }
918
919 void EventRouter::Observe(int type,
920                           const content::NotificationSource& source,
921                           const content::NotificationDetails& details) {
922   if (type == chrome::NOTIFICATION_PROFILE_ADDED) {
923     Profile* const added_profile = content::Source<Profile>(source).ptr();
924     if (!added_profile->IsOffTheRecord())
925       GrantAccessForAddedProfileToRunningInstance(added_profile, profile_);
926
927     BroadcastEvent(profile_,
928                    file_browser_private::OnProfileAdded::kEventName,
929                    file_browser_private::OnProfileAdded::Create());
930   }
931 }
932
933 void EventRouter::RegisterMultiUserWindowManagerObserver() {
934   if (multi_user_window_manager_observer_registered_)
935     return;
936   chrome::MultiUserWindowManager* const multi_user_window_manager =
937       chrome::MultiUserWindowManager::GetInstance();
938   if (multi_user_window_manager) {
939     multi_user_window_manager->AddObserver(this);
940     multi_user_window_manager_observer_registered_ = true;
941   }
942 }
943
944 void EventRouter::OnOwnerEntryChanged(aura::Window* window) {
945   BroadcastEvent(profile_,
946                  file_browser_private::OnDesktopChanged::kEventName,
947                  file_browser_private::OnDesktopChanged::Create());
948 }
949
950 }  // namespace file_manager