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