Home | History | Annotate | Download | only in file_manager
      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 
     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",  // 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.
    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       !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 
    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 
    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 
    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 
    822 void EventRouter::OnDiskRemoved(const DiskMountManager::Disk& disk) {
    823   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    824   // Do nothing.
    825 }
    826 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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