Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2011 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_browser_event_router.h"
      6 
      7 #include "base/json/json_writer.h"
      8 #include "base/memory/singleton.h"
      9 #include "base/stl_util-inl.h"
     10 #include "base/values.h"
     11 #include "chrome/browser/chromeos/cros/cros_library.h"
     12 #include "chrome/browser/chromeos/login/user_manager.h"
     13 #include "chrome/browser/chromeos/notifications/system_notification.h"
     14 #include "chrome/browser/extensions/extension_event_names.h"
     15 #include "chrome/browser/extensions/extension_event_router.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/extensions/file_manager_util.h"
     18 #include "grit/generated_resources.h"
     19 #include "grit/theme_resources.h"
     20 #include "ui/base/l10n/l10n_util.h"
     21 
     22 const char kDiskAddedEventType[] = "added";
     23 const char kDiskRemovedEventType[] = "removed";
     24 
     25 const char* DeviceTypeToString(chromeos::DeviceType type) {
     26   switch (type) {
     27     case chromeos::FLASH:
     28       return "flash";
     29     case chromeos::HDD:
     30       return "hdd";
     31     case chromeos::OPTICAL:
     32       return "optical";
     33     default:
     34       break;
     35   }
     36   return "undefined";
     37 }
     38 
     39 DictionaryValue* DiskToDictionaryValue(
     40     const chromeos::MountLibrary::Disk* disk) {
     41   DictionaryValue* result = new DictionaryValue();
     42   result->SetString("mountPath", disk->mount_path());
     43   result->SetString("label", disk->device_label());
     44   result->SetString("deviceType", DeviceTypeToString(disk->device_type()));
     45   result->SetInteger("totalSizeKB", disk->total_size() / 1024);
     46   result->SetBoolean("readOnly", disk->is_read_only());
     47   return result;
     48 }
     49 
     50 ExtensionFileBrowserEventRouter::ExtensionFileBrowserEventRouter()
     51     : profile_(NULL) {
     52 }
     53 
     54 ExtensionFileBrowserEventRouter::~ExtensionFileBrowserEventRouter() {
     55 }
     56 
     57 void ExtensionFileBrowserEventRouter::ObserveFileSystemEvents(
     58     Profile* profile) {
     59   if (!profile)
     60     return;
     61   profile_ = profile;
     62   if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
     63     return;
     64   if (chromeos::UserManager::Get()->user_is_logged_in()) {
     65     chromeos::MountLibrary* lib =
     66         chromeos::CrosLibrary::Get()->GetMountLibrary();
     67     lib->RemoveObserver(this);
     68     lib->AddObserver(this);
     69     lib->RequestMountInfoRefresh();
     70   }
     71 }
     72 
     73 void ExtensionFileBrowserEventRouter::StopObservingFileSystemEvents() {
     74   if (!profile_)
     75     return;
     76   if (!chromeos::CrosLibrary::Get()->EnsureLoaded())
     77     return;
     78   chromeos::MountLibrary* lib =
     79       chromeos::CrosLibrary::Get()->GetMountLibrary();
     80   lib->RemoveObserver(this);
     81   profile_ = NULL;
     82 }
     83 
     84 // static
     85 ExtensionFileBrowserEventRouter*
     86     ExtensionFileBrowserEventRouter::GetInstance() {
     87   return Singleton<ExtensionFileBrowserEventRouter>::get();
     88 }
     89 
     90 void ExtensionFileBrowserEventRouter::DiskChanged(
     91     chromeos::MountLibraryEventType event,
     92     const chromeos::MountLibrary::Disk* disk) {
     93   if (event == chromeos::MOUNT_DISK_ADDED) {
     94     OnDiskAdded(disk);
     95   } else if (event == chromeos::MOUNT_DISK_REMOVED) {
     96     OnDiskRemoved(disk);
     97   } else if (event == chromeos::MOUNT_DISK_CHANGED) {
     98     OnDiskChanged(disk);
     99   }
    100 }
    101 
    102 void ExtensionFileBrowserEventRouter::DeviceChanged(
    103     chromeos::MountLibraryEventType event,
    104     const std::string& device_path) {
    105   if (event == chromeos::MOUNT_DEVICE_ADDED) {
    106     OnDeviceAdded(device_path);
    107   } else if (event == chromeos::MOUNT_DEVICE_REMOVED) {
    108     OnDeviceRemoved(device_path);
    109   } else if (event == chromeos::MOUNT_DEVICE_SCANNED) {
    110     OnDeviceScanned(device_path);
    111   }
    112 }
    113 
    114 void ExtensionFileBrowserEventRouter::DispatchEvent(
    115     const chromeos::MountLibrary::Disk* disk, bool added) {
    116   if (!profile_) {
    117     NOTREACHED();
    118     return;
    119   }
    120 
    121   ListValue args;
    122   DictionaryValue* mount_info = new DictionaryValue();
    123   args.Append(mount_info);
    124   mount_info->SetString("eventType",
    125                         added ? kDiskAddedEventType : kDiskRemovedEventType);
    126   DictionaryValue* disk_info = DiskToDictionaryValue(disk);
    127   mount_info->Set("volumeInfo", disk_info);
    128 
    129   std::string args_json;
    130   base::JSONWriter::Write(&args, false /* pretty_print */, &args_json);
    131   profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
    132       extension_event_names::kOnFileBrowserDiskChanged, args_json, NULL,
    133       GURL());
    134 }
    135 
    136 void ExtensionFileBrowserEventRouter::OnDiskAdded(
    137     const chromeos::MountLibrary::Disk* disk) {
    138   VLOG(1) << "Disk added: " << disk->device_path();
    139   if (disk->device_path().empty()) {
    140     VLOG(1) << "Empty system path for " << disk->device_path();
    141     return;
    142   }
    143   if (disk->is_parent()) {
    144     if (!disk->has_media())
    145       HideDeviceNotification(disk->system_path());
    146     return;
    147   }
    148 
    149   // If disk is not mounted yet, give it a try.
    150   if (disk->mount_path().empty()) {
    151     // Initiate disk mount operation.
    152     chromeos::MountLibrary* lib =
    153         chromeos::CrosLibrary::Get()->GetMountLibrary();
    154     lib->MountPath(disk->device_path().c_str());
    155   }
    156 }
    157 
    158 void ExtensionFileBrowserEventRouter::OnDiskRemoved(
    159     const chromeos::MountLibrary::Disk* disk) {
    160   VLOG(1) << "Disk removed: " << disk->device_path();
    161   HideDeviceNotification(disk->system_path());
    162   MountPointMap::iterator iter = mounted_devices_.find(disk->device_path());
    163   if (iter == mounted_devices_.end())
    164     return;
    165 
    166   chromeos::MountLibrary* lib =
    167       chromeos::CrosLibrary::Get()->GetMountLibrary();
    168   // TODO(zelidrag): This for some reason does not work as advertized.
    169   // we might need to clean up mount directory on FILE thread here as well.
    170   lib->UnmountPath(disk->device_path().c_str());
    171 
    172   DispatchEvent(disk, false);
    173   mounted_devices_.erase(iter);
    174 }
    175 
    176 void ExtensionFileBrowserEventRouter::OnDiskChanged(
    177     const chromeos::MountLibrary::Disk* disk) {
    178   VLOG(1) << "Disk changed : " << disk->device_path();
    179   if (!disk->mount_path().empty()) {
    180     HideDeviceNotification(disk->system_path());
    181     // Remember this mount point.
    182     if (mounted_devices_.find(disk->device_path()) == mounted_devices_.end()) {
    183       mounted_devices_.insert(
    184           std::pair<std::string, std::string>(disk->device_path(),
    185                                               disk->mount_path()));
    186       DispatchEvent(disk, true);
    187       HideDeviceNotification(disk->system_path());
    188       FileManagerUtil::ShowFullTabUrl(profile_, FilePath(disk->mount_path()));
    189     }
    190   }
    191 }
    192 
    193 void ExtensionFileBrowserEventRouter::OnDeviceAdded(
    194     const std::string& device_path) {
    195   VLOG(1) << "Device added : " << device_path;
    196   // TODO(zelidrag): Find better icon here.
    197   ShowDeviceNotification(device_path, IDR_PAGEINFO_INFO,
    198       l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_SCANNING_MESSAGE));
    199 
    200 }
    201 
    202 void ExtensionFileBrowserEventRouter::OnDeviceRemoved(
    203     const std::string& system_path) {
    204   HideDeviceNotification(system_path);
    205 }
    206 
    207 void ExtensionFileBrowserEventRouter::OnDeviceScanned(
    208     const std::string& device_path) {
    209   VLOG(1) << "Device scanned : " << device_path;
    210 }
    211 
    212 void ExtensionFileBrowserEventRouter::ShowDeviceNotification(
    213     const std::string& system_path, int icon_resource_id,
    214     const string16& message) {
    215   NotificationMap::iterator iter = FindNotificationForPath(system_path);
    216   std::string mount_path;
    217   if (iter != notifications_.end()) {
    218     iter->second->Show(message, false, false);
    219   } else {
    220     if (!profile_) {
    221       NOTREACHED();
    222       return;
    223     }
    224     chromeos::SystemNotification* notification =
    225         new chromeos::SystemNotification(
    226             profile_,
    227             system_path,
    228             icon_resource_id,
    229             l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_DETECTION_TITLE));
    230     notifications_.insert(NotificationMap::value_type(system_path,
    231         linked_ptr<chromeos::SystemNotification>(notification)));
    232     notification->Show(message, false, false);
    233   }
    234 }
    235 
    236 void ExtensionFileBrowserEventRouter::HideDeviceNotification(
    237     const std::string& system_path) {
    238   NotificationMap::iterator iter = FindNotificationForPath(system_path);
    239   if (iter != notifications_.end()) {
    240     iter->second->Hide();
    241     notifications_.erase(iter);
    242   }
    243 }
    244 
    245 ExtensionFileBrowserEventRouter::NotificationMap::iterator
    246     ExtensionFileBrowserEventRouter::FindNotificationForPath(
    247         const std::string& system_path) {
    248   for (NotificationMap::iterator iter = notifications_.begin();
    249        iter != notifications_.end();
    250        ++iter) {
    251     const std::string& notification_device_path = iter->first;
    252     // Doing a sub string match so that we find if this new one is a subdevice
    253     // of another already inserted device.
    254     if (StartsWithASCII(system_path, notification_device_path, true)) {
    255       return iter;
    256     }
    257   }
    258   return notifications_.end();
    259 }
    260