// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/chromeos/extensions/file_browser_event_router.h" #include "base/json/json_writer.h" #include "base/memory/singleton.h" #include "base/stl_util-inl.h" #include "base/values.h" #include "chrome/browser/chromeos/cros/cros_library.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/notifications/system_notification.h" #include "chrome/browser/extensions/extension_event_names.h" #include "chrome/browser/extensions/extension_event_router.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/extensions/file_manager_util.h" #include "grit/generated_resources.h" #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" const char kDiskAddedEventType[] = "added"; const char kDiskRemovedEventType[] = "removed"; const char* DeviceTypeToString(chromeos::DeviceType type) { switch (type) { case chromeos::FLASH: return "flash"; case chromeos::HDD: return "hdd"; case chromeos::OPTICAL: return "optical"; default: break; } return "undefined"; } DictionaryValue* DiskToDictionaryValue( const chromeos::MountLibrary::Disk* disk) { DictionaryValue* result = new DictionaryValue(); result->SetString("mountPath", disk->mount_path()); result->SetString("label", disk->device_label()); result->SetString("deviceType", DeviceTypeToString(disk->device_type())); result->SetInteger("totalSizeKB", disk->total_size() / 1024); result->SetBoolean("readOnly", disk->is_read_only()); return result; } ExtensionFileBrowserEventRouter::ExtensionFileBrowserEventRouter() : profile_(NULL) { } ExtensionFileBrowserEventRouter::~ExtensionFileBrowserEventRouter() { } void ExtensionFileBrowserEventRouter::ObserveFileSystemEvents( Profile* profile) { if (!profile) return; profile_ = profile; if (!chromeos::CrosLibrary::Get()->EnsureLoaded()) return; if (chromeos::UserManager::Get()->user_is_logged_in()) { chromeos::MountLibrary* lib = chromeos::CrosLibrary::Get()->GetMountLibrary(); lib->RemoveObserver(this); lib->AddObserver(this); lib->RequestMountInfoRefresh(); } } void ExtensionFileBrowserEventRouter::StopObservingFileSystemEvents() { if (!profile_) return; if (!chromeos::CrosLibrary::Get()->EnsureLoaded()) return; chromeos::MountLibrary* lib = chromeos::CrosLibrary::Get()->GetMountLibrary(); lib->RemoveObserver(this); profile_ = NULL; } // static ExtensionFileBrowserEventRouter* ExtensionFileBrowserEventRouter::GetInstance() { return Singleton<ExtensionFileBrowserEventRouter>::get(); } void ExtensionFileBrowserEventRouter::DiskChanged( chromeos::MountLibraryEventType event, const chromeos::MountLibrary::Disk* disk) { if (event == chromeos::MOUNT_DISK_ADDED) { OnDiskAdded(disk); } else if (event == chromeos::MOUNT_DISK_REMOVED) { OnDiskRemoved(disk); } else if (event == chromeos::MOUNT_DISK_CHANGED) { OnDiskChanged(disk); } } void ExtensionFileBrowserEventRouter::DeviceChanged( chromeos::MountLibraryEventType event, const std::string& device_path) { if (event == chromeos::MOUNT_DEVICE_ADDED) { OnDeviceAdded(device_path); } else if (event == chromeos::MOUNT_DEVICE_REMOVED) { OnDeviceRemoved(device_path); } else if (event == chromeos::MOUNT_DEVICE_SCANNED) { OnDeviceScanned(device_path); } } void ExtensionFileBrowserEventRouter::DispatchEvent( const chromeos::MountLibrary::Disk* disk, bool added) { if (!profile_) { NOTREACHED(); return; } ListValue args; DictionaryValue* mount_info = new DictionaryValue(); args.Append(mount_info); mount_info->SetString("eventType", added ? kDiskAddedEventType : kDiskRemovedEventType); DictionaryValue* disk_info = DiskToDictionaryValue(disk); mount_info->Set("volumeInfo", disk_info); std::string args_json; base::JSONWriter::Write(&args, false /* pretty_print */, &args_json); profile_->GetExtensionEventRouter()->DispatchEventToRenderers( extension_event_names::kOnFileBrowserDiskChanged, args_json, NULL, GURL()); } void ExtensionFileBrowserEventRouter::OnDiskAdded( const chromeos::MountLibrary::Disk* disk) { VLOG(1) << "Disk added: " << disk->device_path(); if (disk->device_path().empty()) { VLOG(1) << "Empty system path for " << disk->device_path(); return; } if (disk->is_parent()) { if (!disk->has_media()) HideDeviceNotification(disk->system_path()); return; } // If disk is not mounted yet, give it a try. if (disk->mount_path().empty()) { // Initiate disk mount operation. chromeos::MountLibrary* lib = chromeos::CrosLibrary::Get()->GetMountLibrary(); lib->MountPath(disk->device_path().c_str()); } } void ExtensionFileBrowserEventRouter::OnDiskRemoved( const chromeos::MountLibrary::Disk* disk) { VLOG(1) << "Disk removed: " << disk->device_path(); HideDeviceNotification(disk->system_path()); MountPointMap::iterator iter = mounted_devices_.find(disk->device_path()); if (iter == mounted_devices_.end()) return; chromeos::MountLibrary* lib = chromeos::CrosLibrary::Get()->GetMountLibrary(); // TODO(zelidrag): This for some reason does not work as advertized. // we might need to clean up mount directory on FILE thread here as well. lib->UnmountPath(disk->device_path().c_str()); DispatchEvent(disk, false); mounted_devices_.erase(iter); } void ExtensionFileBrowserEventRouter::OnDiskChanged( const chromeos::MountLibrary::Disk* disk) { VLOG(1) << "Disk changed : " << disk->device_path(); if (!disk->mount_path().empty()) { HideDeviceNotification(disk->system_path()); // Remember this mount point. if (mounted_devices_.find(disk->device_path()) == mounted_devices_.end()) { mounted_devices_.insert( std::pair<std::string, std::string>(disk->device_path(), disk->mount_path())); DispatchEvent(disk, true); HideDeviceNotification(disk->system_path()); FileManagerUtil::ShowFullTabUrl(profile_, FilePath(disk->mount_path())); } } } void ExtensionFileBrowserEventRouter::OnDeviceAdded( const std::string& device_path) { VLOG(1) << "Device added : " << device_path; // TODO(zelidrag): Find better icon here. ShowDeviceNotification(device_path, IDR_PAGEINFO_INFO, l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_SCANNING_MESSAGE)); } void ExtensionFileBrowserEventRouter::OnDeviceRemoved( const std::string& system_path) { HideDeviceNotification(system_path); } void ExtensionFileBrowserEventRouter::OnDeviceScanned( const std::string& device_path) { VLOG(1) << "Device scanned : " << device_path; } void ExtensionFileBrowserEventRouter::ShowDeviceNotification( const std::string& system_path, int icon_resource_id, const string16& message) { NotificationMap::iterator iter = FindNotificationForPath(system_path); std::string mount_path; if (iter != notifications_.end()) { iter->second->Show(message, false, false); } else { if (!profile_) { NOTREACHED(); return; } chromeos::SystemNotification* notification = new chromeos::SystemNotification( profile_, system_path, icon_resource_id, l10n_util::GetStringUTF16(IDS_REMOVABLE_DEVICE_DETECTION_TITLE)); notifications_.insert(NotificationMap::value_type(system_path, linked_ptr<chromeos::SystemNotification>(notification))); notification->Show(message, false, false); } } void ExtensionFileBrowserEventRouter::HideDeviceNotification( const std::string& system_path) { NotificationMap::iterator iter = FindNotificationForPath(system_path); if (iter != notifications_.end()) { iter->second->Hide(); notifications_.erase(iter); } } ExtensionFileBrowserEventRouter::NotificationMap::iterator ExtensionFileBrowserEventRouter::FindNotificationForPath( const std::string& system_path) { for (NotificationMap::iterator iter = notifications_.begin(); iter != notifications_.end(); ++iter) { const std::string& notification_device_path = iter->first; // Doing a sub string match so that we find if this new one is a subdevice // of another already inserted device. if (StartsWithASCII(system_path, notification_device_path, true)) { return iter; } } return notifications_.end(); }