blob: 19d5e223ae5b322f760514179a3a713139f27113 [file] [log] [blame]
// Copyright (c) 2012 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/bind.h"
#include "base/json/json_writer.h"
#include "base/message_loop.h"
#include "base/stl_util.h"
#include "base/values.h"
#include "chrome/browser/api/prefs/pref_change_registrar.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/extensions/file_browser_notifications.h"
#include "chrome/browser/chromeos/extensions/file_manager_util.h"
#include "chrome/browser/chromeos/gdata/drive_file_system_util.h"
#include "chrome/browser/chromeos/gdata/drive_service_interface.h"
#include "chrome/browser/chromeos/gdata/drive_system_service.h"
#include "chrome/browser/chromeos/login/base_login_display_host.h"
#include "chrome/browser/chromeos/login/screen_locker.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "chrome/browser/extensions/event_names.h"
#include "chrome/browser/extensions/event_router.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/prefs/pref_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_dependency_manager.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/pref_names.h"
#include "content/public/browser/notification_source.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "webkit/fileapi/file_system_types.h"
#include "webkit/fileapi/file_system_util.h"
using chromeos::disks::DiskMountManager;
using chromeos::disks::DiskMountManagerEventType;
using content::BrowserThread;
using gdata::DriveSystemService;
using gdata::DriveSystemServiceFactory;
namespace {
const char kDiskAddedEventType[] = "added";
const char kDiskRemovedEventType[] = "removed";
const char kPathChanged[] = "changed";
const char kPathWatchError[] = "error";
DictionaryValue* DiskToDictionaryValue(
const DiskMountManager::Disk* disk) {
DictionaryValue* result = new DictionaryValue();
result->SetString("mountPath", disk->mount_path());
result->SetString("devicePath", disk->device_path());
result->SetString("label", disk->device_label());
result->SetString("deviceType",
DiskMountManager::DeviceTypeToString(disk->device_type()));
result->SetInteger("totalSizeKB", disk->total_size_in_bytes() / 1024);
result->SetBoolean("readOnly", disk->is_read_only());
return result;
}
}
const char* MountErrorToString(chromeos::MountError error) {
switch (error) {
case chromeos::MOUNT_ERROR_NONE:
return "success";
case chromeos::MOUNT_ERROR_UNKNOWN:
return "error_unknown";
case chromeos::MOUNT_ERROR_INTERNAL:
return "error_internal";
case chromeos::MOUNT_ERROR_UNKNOWN_FILESYSTEM:
return "error_unknown_filesystem";
case chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM:
return "error_unsuported_filesystem";
case chromeos::MOUNT_ERROR_INVALID_ARCHIVE:
return "error_invalid_archive";
case chromeos::MOUNT_ERROR_NOT_AUTHENTICATED:
return "error_authentication";
case chromeos::MOUNT_ERROR_PATH_UNMOUNTED:
return "error_path_unmounted";
default:
NOTREACHED();
}
return "";
}
FileBrowserEventRouter::FileBrowserEventRouter(
Profile* profile)
: delegate_(new FileBrowserEventRouter::FileWatcherDelegate(this)),
notifications_(new FileBrowserNotifications(profile)),
pref_change_registrar_(new PrefChangeRegistrar),
profile_(profile),
num_remote_update_requests_(0) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
FileBrowserEventRouter::~FileBrowserEventRouter() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
void FileBrowserEventRouter::ShutdownOnUIThread() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(file_watchers_.empty());
STLDeleteValues(&file_watchers_);
if (!profile_) {
NOTREACHED();
return;
}
DiskMountManager::GetInstance()->RemoveObserver(this);
DriveSystemService* system_service =
DriveSystemServiceFactory::FindForProfile(profile_);
if (system_service) {
system_service->file_system()->RemoveObserver(this);
system_service->drive_service()->RemoveObserver(this);
}
chromeos::NetworkLibrary* network_library =
chromeos::CrosLibrary::Get()->GetNetworkLibrary();
if (network_library)
network_library->RemoveNetworkManagerObserver(this);
profile_ = NULL;
}
void FileBrowserEventRouter::ObserveFileSystemEvents() {
if (!profile_) {
NOTREACHED();
return;
}
if (!chromeos::UserManager::Get()->IsUserLoggedIn())
return;
DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
disk_mount_manager->RemoveObserver(this);
disk_mount_manager->AddObserver(this);
disk_mount_manager->RequestMountInfoRefresh();
DriveSystemService* system_service =
DriveSystemServiceFactory::GetForProfile(profile_);
if (!system_service) {
NOTREACHED();
return;
}
system_service->drive_service()->AddObserver(this);
system_service->file_system()->AddObserver(this);
chromeos::NetworkLibrary* network_library =
chromeos::CrosLibrary::Get()->GetNetworkLibrary();
if (network_library)
network_library->AddNetworkManagerObserver(this);
pref_change_registrar_->Init(profile_->GetPrefs());
pref_change_registrar_->Add(prefs::kDisableGDataOverCellular, this);
pref_change_registrar_->Add(prefs::kDisableGDataHostedFiles, this);
pref_change_registrar_->Add(prefs::kDisableGData, this);
pref_change_registrar_->Add(prefs::kExternalStorageDisabled, this);
}
// File watch setup routines.
bool FileBrowserEventRouter::AddFileWatch(
const FilePath& local_path,
const FilePath& virtual_path,
const std::string& extension_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::AutoLock lock(lock_);
FilePath watch_path = local_path;
bool is_remote_watch = false;
// Tweak watch path for remote sources - we need to drop leading /special
// directory from there in order to be able to pair these events with
// their change notifications.
if (gdata::util::GetSpecialRemoteRootPath().IsParent(watch_path)) {
watch_path = gdata::util::ExtractDrivePath(watch_path);
is_remote_watch = true;
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&FileBrowserEventRouter::HandleRemoteUpdateRequestOnUIThread,
this, true));
}
WatcherMap::iterator iter = file_watchers_.find(watch_path);
if (iter == file_watchers_.end()) {
scoped_ptr<FileWatcherExtensions>
watch(new FileWatcherExtensions(virtual_path,
extension_id,
is_remote_watch));
if (watch->Watch(watch_path, delegate_.get()))
file_watchers_[watch_path] = watch.release();
else
return false;
} else {
iter->second->AddExtension(extension_id);
}
return true;
}
void FileBrowserEventRouter::RemoveFileWatch(
const FilePath& local_path,
const std::string& extension_id) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
base::AutoLock lock(lock_);
FilePath watch_path = local_path;
// Tweak watch path for remote sources - we need to drop leading /special
// directory from there in order to be able to pair these events with
// their change notifications.
if (gdata::util::GetSpecialRemoteRootPath().IsParent(watch_path)) {
watch_path = gdata::util::ExtractDrivePath(watch_path);
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&FileBrowserEventRouter::HandleRemoteUpdateRequestOnUIThread,
this, false));
}
WatcherMap::iterator iter = file_watchers_.find(watch_path);
if (iter == file_watchers_.end())
return;
// Remove the renderer process for this watch.
iter->second->RemoveExtension(extension_id);
if (iter->second->GetRefCount() == 0) {
delete iter->second;
file_watchers_.erase(iter);
}
}
void FileBrowserEventRouter::MountDrive(
const base::Closure& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DriveSystemService* system_service =
DriveSystemServiceFactory::GetForProfile(profile_);
if (system_service) {
system_service->drive_service()->Authenticate(
base::Bind(&FileBrowserEventRouter::OnAuthenticated,
this,
callback));
}
}
void FileBrowserEventRouter::OnAuthenticated(
const base::Closure& callback,
gdata::GDataErrorCode error,
const std::string& token) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
chromeos::MountError error_code;
// For the file manager to work offline, GDATA_NO_CONNECTION is allowed.
if (error == gdata::HTTP_SUCCESS || error == gdata::GDATA_NO_CONNECTION)
error_code = chromeos::MOUNT_ERROR_NONE;
else
error_code = chromeos::MOUNT_ERROR_NOT_AUTHENTICATED;
// Pass back the gdata mount point path as source path.
const std::string& gdata_path = gdata::util::GetDriveMountPointPathAsString();
DiskMountManager::MountPointInfo mount_info(
gdata_path,
gdata_path,
chromeos::MOUNT_TYPE_GDATA,
chromeos::disks::MOUNT_CONDITION_NONE);
// Raise mount event.
MountCompleted(DiskMountManager::MOUNTING, error_code, mount_info);
if (!callback.is_null())
callback.Run();
}
void FileBrowserEventRouter::HandleRemoteUpdateRequestOnUIThread(bool start) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
gdata::DriveFileSystemInterface* file_system = GetRemoteFileSystem();
DCHECK(file_system);
if (start) {
file_system->CheckForUpdates();
if (num_remote_update_requests_ == 0)
file_system->StartUpdates();
++num_remote_update_requests_;
} else {
DCHECK_LE(1, num_remote_update_requests_);
--num_remote_update_requests_;
if (num_remote_update_requests_ == 0)
file_system->StopUpdates();
}
}
void FileBrowserEventRouter::DiskChanged(
DiskMountManagerEventType event,
const DiskMountManager::Disk* disk) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Disregard hidden devices.
if (disk->is_hidden())
return;
if (event == chromeos::disks::MOUNT_DISK_ADDED) {
OnDiskAdded(disk);
} else if (event == chromeos::disks::MOUNT_DISK_REMOVED) {
OnDiskRemoved(disk);
}
}
void FileBrowserEventRouter::DeviceChanged(
DiskMountManagerEventType event,
const std::string& device_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (event == chromeos::disks::MOUNT_DEVICE_ADDED) {
OnDeviceAdded(device_path);
} else if (event == chromeos::disks::MOUNT_DEVICE_REMOVED) {
OnDeviceRemoved(device_path);
} else if (event == chromeos::disks::MOUNT_DEVICE_SCANNED) {
OnDeviceScanned(device_path);
} else if (event == chromeos::disks::MOUNT_FORMATTING_STARTED) {
// TODO(tbarzic): get rid of '!'.
if (device_path[0] == '!') {
OnFormattingStarted(device_path.substr(1), false);
} else {
OnFormattingStarted(device_path, true);
}
} else if (event == chromeos::disks::MOUNT_FORMATTING_FINISHED) {
if (device_path[0] == '!') {
OnFormattingFinished(device_path.substr(1), false);
} else {
OnFormattingFinished(device_path, true);
}
}
}
void FileBrowserEventRouter::MountCompleted(
DiskMountManager::MountEvent event_type,
chromeos::MountError error_code,
const DiskMountManager::MountPointInfo& mount_info) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DispatchMountCompletedEvent(event_type, error_code, mount_info);
if (mount_info.mount_type == chromeos::MOUNT_TYPE_DEVICE &&
event_type == DiskMountManager::MOUNTING) {
DiskMountManager* disk_mount_manager = DiskMountManager::GetInstance();
const DiskMountManager::Disk* disk =
disk_mount_manager->FindDiskBySourcePath(mount_info.source_path);
if (!disk)
return;
notifications_->ManageNotificationsOnMountCompleted(
disk->system_path_prefix(), disk->drive_label(), disk->is_parent(),
error_code == chromeos::MOUNT_ERROR_NONE,
error_code == chromeos::MOUNT_ERROR_UNSUPPORTED_FILESYSTEM);
} else if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) {
// Clear the "mounted" state for archive files in gdata cache
// when mounting failed or unmounting succeeded.
if ((event_type == DiskMountManager::MOUNTING) !=
(error_code == chromeos::MOUNT_ERROR_NONE)) {
FilePath source_path(mount_info.source_path);
DriveSystemService* system_service =
DriveSystemServiceFactory::GetForProfile(profile_);
gdata::DriveCache* cache =
system_service ? system_service->cache() : NULL;
if (cache) {
cache->SetMountedStateOnUIThread(
source_path, false, gdata::ChangeCacheStateCallback());
}
}
}
}
void FileBrowserEventRouter::OnNetworkManagerChanged(
chromeos::NetworkLibrary* network_library) {
if (!profile_ || !profile_->GetExtensionEventRouter()) {
NOTREACHED();
return;
}
profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
extensions::event_names::kOnFileBrowserNetworkConnectionChanged,
scoped_ptr<ListValue>(new ListValue()), NULL, GURL());
}
void FileBrowserEventRouter::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
if (!profile_ || !profile_->GetExtensionEventRouter()) {
NOTREACHED();
return;
}
if (type == chrome::NOTIFICATION_PREF_CHANGED) {
std::string* pref_name = content::Details<std::string>(details).ptr();
// If the policy just got disabled we have to unmount every device currently
// mounted. The opposite is fine - we can let the user re-plug her device to
// make it available.
if (*pref_name == prefs::kExternalStorageDisabled &&
profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
DiskMountManager* manager = DiskMountManager::GetInstance();
DiskMountManager::MountPointMap mounts(manager->mount_points());
for (DiskMountManager::MountPointMap::const_iterator it = mounts.begin();
it != mounts.end(); ++it) {
LOG(INFO) << "Unmounting " << it->second.mount_path
<< " because of policy.";
manager->UnmountPath(it->second.mount_path);
}
return;
} else if (*pref_name == prefs::kDisableGDataOverCellular ||
*pref_name == prefs::kDisableGDataHostedFiles ||
*pref_name == prefs::kDisableGData) {
profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
extensions::event_names::kOnFileBrowserGDataPreferencesChanged,
scoped_ptr<ListValue>(new ListValue()), NULL, GURL());
}
}
}
void FileBrowserEventRouter::OnProgressUpdate(
const gdata::OperationProgressStatusList& list) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
scoped_ptr<ListValue> event_list(
file_manager_util::ProgressStatusVectorToListValue(
profile_,
file_manager_util::GetFileBrowserExtensionUrl().GetOrigin(),
list));
scoped_ptr<ListValue> args(new ListValue());
args->Append(event_list.release());
profile_->GetExtensionEventRouter()->DispatchEventToExtension(
std::string(kFileBrowserDomain),
extensions::event_names::kOnFileTransfersUpdated, args.Pass(), NULL,
GURL());
}
void FileBrowserEventRouter::OnDirectoryChanged(
const FilePath& directory_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
HandleFileWatchNotification(directory_path, false);
}
void FileBrowserEventRouter::OnDocumentFeedFetched(
int num_accumulated_entries) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
scoped_ptr<ListValue> args(new ListValue());
args->Append(base::Value::CreateIntegerValue(num_accumulated_entries));
profile_->GetExtensionEventRouter()->DispatchEventToExtension(
std::string(kFileBrowserDomain),
extensions::event_names::kOnDocumentFeedFetched, args.Pass(), NULL,
GURL());
}
void FileBrowserEventRouter::OnFileSystemMounted() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
MountDrive(base::Bind(&base::DoNothing)); // Callback does nothing.
}
void FileBrowserEventRouter::OnFileSystemBeingUnmounted() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Raise a MountCompleted event to notify the File Manager.
const std::string& gdata_path = gdata::util::GetDriveMountPointPathAsString();
DiskMountManager::MountPointInfo mount_info(
gdata_path,
gdata_path,
chromeos::MOUNT_TYPE_GDATA,
chromeos::disks::MOUNT_CONDITION_NONE);
MountCompleted(DiskMountManager::UNMOUNTING, chromeos::MOUNT_ERROR_NONE,
mount_info);
}
void FileBrowserEventRouter::OnAuthenticationFailed() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// Raise a MountCompleted event to notify the File Manager.
const std::string& gdata_path = gdata::util::GetDriveMountPointPathAsString();
DiskMountManager::MountPointInfo mount_info(
gdata_path,
gdata_path,
chromeos::MOUNT_TYPE_GDATA,
chromeos::disks::MOUNT_CONDITION_NONE);
MountCompleted(DiskMountManager::UNMOUNTING, chromeos::MOUNT_ERROR_NONE,
mount_info);
}
void FileBrowserEventRouter::HandleFileWatchNotification(
const FilePath& local_path, bool got_error) {
base::AutoLock lock(lock_);
WatcherMap::const_iterator iter = file_watchers_.find(local_path);
if (iter == file_watchers_.end()) {
return;
}
DispatchFolderChangeEvent(iter->second->GetVirtualPath(), got_error,
iter->second->GetExtensions());
}
void FileBrowserEventRouter::DispatchFolderChangeEvent(
const FilePath& virtual_path, bool got_error,
const FileBrowserEventRouter::ExtensionUsageRegistry& extensions) {
if (!profile_) {
NOTREACHED();
return;
}
for (ExtensionUsageRegistry::const_iterator iter = extensions.begin();
iter != extensions.end(); ++iter) {
GURL target_origin_url(extensions::Extension::GetBaseURLFromExtensionId(
iter->first));
GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url,
fileapi::kFileSystemTypeExternal);
GURL target_file_url = GURL(base_url.spec() + virtual_path.value());
scoped_ptr<ListValue> args(new ListValue());
DictionaryValue* watch_info = new DictionaryValue();
args->Append(watch_info);
watch_info->SetString("fileUrl", target_file_url.spec());
watch_info->SetString("eventType",
got_error ? kPathWatchError : kPathChanged);
profile_->GetExtensionEventRouter()->DispatchEventToExtension(
iter->first, extensions::event_names::kOnFileChanged, args.Pass(), NULL,
GURL());
}
}
// TODO(tbarzic): This is not used anymore. Remove it.
void FileBrowserEventRouter::DispatchDiskEvent(
const DiskMountManager::Disk* disk, bool added) {
if (!profile_) {
NOTREACHED();
return;
}
scoped_ptr<ListValue> args(new ListValue());
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);
profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
extensions::event_names::kOnFileBrowserDiskChanged, args.Pass(), NULL,
GURL());
}
void FileBrowserEventRouter::DispatchMountCompletedEvent(
DiskMountManager::MountEvent event,
chromeos::MountError error_code,
const DiskMountManager::MountPointInfo& mount_info) {
if (!profile_ || mount_info.mount_type == chromeos::MOUNT_TYPE_INVALID) {
NOTREACHED();
return;
}
scoped_ptr<ListValue> args(new ListValue());
DictionaryValue* mount_info_value = new DictionaryValue();
args->Append(mount_info_value);
mount_info_value->SetString("eventType",
event == DiskMountManager::MOUNTING ? "mount" : "unmount");
mount_info_value->SetString("status", MountErrorToString(error_code));
mount_info_value->SetString(
"mountType",
DiskMountManager::MountTypeToString(mount_info.mount_type));
// Add sourcePath to the event.
mount_info_value->SetString("sourcePath", mount_info.source_path);
FilePath relative_mount_path;
bool relative_mount_path_set = false;
// If there were no error or some special conditions occured, add mountPath
// to the event.
if (error_code == chromeos::MOUNT_ERROR_NONE ||
mount_info.mount_condition) {
// Convert mount point path to relative path with the external file system
// exposed within File API.
if (file_manager_util::ConvertFileToRelativeFileSystemPath(profile_,
FilePath(mount_info.mount_path),
&relative_mount_path)) {
mount_info_value->SetString("mountPath",
"/" + relative_mount_path.value());
relative_mount_path_set = true;
} else {
LOG(ERROR) << "Mount path is not accessible: " << mount_info.mount_path;
mount_info_value->SetString("status",
MountErrorToString(chromeos::MOUNT_ERROR_PATH_UNMOUNTED));
}
}
profile_->GetExtensionEventRouter()->DispatchEventToRenderers(
extensions::event_names::kOnFileBrowserMountCompleted, args.Pass(), NULL,
GURL());
// Do not attempt to open File Manager while the login is in progress or
// the screen is locked.
if (chromeos::BaseLoginDisplayHost::default_host() ||
chromeos::ScreenLocker::default_screen_locker())
return;
if (relative_mount_path_set &&
mount_info.mount_type == chromeos::MOUNT_TYPE_DEVICE &&
!mount_info.mount_condition &&
event == DiskMountManager::MOUNTING) {
file_manager_util::ViewRemovableDrive(FilePath(mount_info.mount_path));
}
}
void FileBrowserEventRouter::OnDiskAdded(
const DiskMountManager::Disk* disk) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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 not mounted yet and it has media and there is no policy
// forbidding external storage, give it a try.
if (disk->mount_path().empty() && disk->has_media() &&
!profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
// Initiate disk mount operation. MountPath auto-detects the filesystem
// format if the second argument is empty. The third argument (mount label)
// is not used in a disk mount operation.
DiskMountManager::GetInstance()->MountPath(
disk->device_path(), std::string(), std::string(),
chromeos::MOUNT_TYPE_DEVICE);
} else {
// Either the disk was mounted or it has no media. In both cases we don't
// want the Scanning notification to persist.
notifications_->HideNotification(FileBrowserNotifications::DEVICE,
disk->system_path_prefix());
}
DispatchDiskEvent(disk, true);
}
void FileBrowserEventRouter::OnDiskRemoved(
const DiskMountManager::Disk* disk) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Disk removed: " << disk->device_path();
if (!disk->mount_path().empty()) {
DiskMountManager::GetInstance()->UnmountPath(disk->mount_path());
}
DispatchDiskEvent(disk, false);
}
void FileBrowserEventRouter::OnDeviceAdded(
const std::string& device_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Device added : " << device_path;
// If the policy is set instead of showing the new device notification we show
// a notification that the operation is not permitted.
if (profile_->GetPrefs()->GetBoolean(prefs::kExternalStorageDisabled)) {
notifications_->ShowNotificationWithMessage(
FileBrowserNotifications::DEVICE_FAIL,
device_path,
l10n_util::GetStringUTF16(IDS_EXTERNAL_STORAGE_DISABLED_MESSAGE));
return;
}
notifications_->RegisterDevice(device_path);
notifications_->ShowNotificationDelayed(FileBrowserNotifications::DEVICE,
device_path,
base::TimeDelta::FromSeconds(5));
}
void FileBrowserEventRouter::OnDeviceRemoved(
const std::string& device_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Device removed : " << device_path;
notifications_->HideNotification(FileBrowserNotifications::DEVICE,
device_path);
notifications_->HideNotification(FileBrowserNotifications::DEVICE_FAIL,
device_path);
notifications_->UnregisterDevice(device_path);
}
void FileBrowserEventRouter::OnDeviceScanned(
const std::string& device_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Device scanned : " << device_path;
}
void FileBrowserEventRouter::OnFormattingStarted(
const std::string& device_path, bool success) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (success) {
notifications_->ShowNotification(FileBrowserNotifications::FORMAT_START,
device_path);
} else {
notifications_->ShowNotification(
FileBrowserNotifications::FORMAT_START_FAIL, device_path);
}
}
void FileBrowserEventRouter::OnFormattingFinished(
const std::string& device_path, bool success) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (success) {
notifications_->HideNotification(FileBrowserNotifications::FORMAT_START,
device_path);
notifications_->ShowNotification(FileBrowserNotifications::FORMAT_SUCCESS,
device_path);
// Hide it after a couple of seconds.
notifications_->HideNotificationDelayed(
FileBrowserNotifications::FORMAT_SUCCESS,
device_path,
base::TimeDelta::FromSeconds(4));
// MountPath auto-detects filesystem format if second argument is empty.
// The third argument (mount label) is not used in a disk mount operation.
DiskMountManager::GetInstance()->MountPath(device_path, std::string(),
std::string(),
chromeos::MOUNT_TYPE_DEVICE);
} else {
notifications_->HideNotification(FileBrowserNotifications::FORMAT_START,
device_path);
notifications_->ShowNotification(FileBrowserNotifications::FORMAT_FAIL,
device_path);
}
}
// FileBrowserEventRouter::WatcherDelegate methods.
FileBrowserEventRouter::FileWatcherDelegate::FileWatcherDelegate(
FileBrowserEventRouter* router) : router_(router) {
}
void FileBrowserEventRouter::FileWatcherDelegate::OnFilePathChanged(
const FilePath& local_path) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&FileWatcherDelegate::HandleFileWatchOnUIThread,
this, local_path, false));
}
void FileBrowserEventRouter::FileWatcherDelegate::OnFilePathError(
const FilePath& local_path) {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
base::Bind(&FileWatcherDelegate::HandleFileWatchOnUIThread,
this, local_path, true));
}
void
FileBrowserEventRouter::FileWatcherDelegate::HandleFileWatchOnUIThread(
const FilePath& local_path, bool got_error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
router_->HandleFileWatchNotification(local_path, got_error);
}
FileBrowserEventRouter::FileWatcherExtensions::FileWatcherExtensions(
const FilePath& path, const std::string& extension_id,
bool is_remote_file_system)
: ref_count_(0),
is_remote_file_system_(is_remote_file_system) {
if (!is_remote_file_system_)
file_watcher_.reset(new base::files::FilePathWatcher());
virtual_path_ = path;
AddExtension(extension_id);
}
FileBrowserEventRouter::FileWatcherExtensions::~FileWatcherExtensions() {}
void FileBrowserEventRouter::FileWatcherExtensions::AddExtension(
const std::string& extension_id) {
ExtensionUsageRegistry::iterator it = extensions_.find(extension_id);
if (it != extensions_.end()) {
it->second++;
} else {
extensions_.insert(ExtensionUsageRegistry::value_type(extension_id, 1));
}
ref_count_++;
}
void FileBrowserEventRouter::FileWatcherExtensions::RemoveExtension(
const std::string& extension_id) {
ExtensionUsageRegistry::iterator it = extensions_.find(extension_id);
if (it != extensions_.end()) {
// If entry found - decrease it's count and remove if necessary
if (0 == it->second--) {
extensions_.erase(it);
}
ref_count_--;
} else {
// Might be reference counting problem - e.g. if some component of
// extension subscribes/unsubscribes correctly, but other component
// only unsubscribes, developer of first one might receive this message
LOG(FATAL) << " Extension [" << extension_id
<< "] tries to unsubscribe from folder [" << local_path_.value()
<< "] it isn't subscribed";
}
}
const FileBrowserEventRouter::ExtensionUsageRegistry&
FileBrowserEventRouter::FileWatcherExtensions::GetExtensions() const {
return extensions_;
}
unsigned int
FileBrowserEventRouter::FileWatcherExtensions::GetRefCount() const {
return ref_count_;
}
const FilePath&
FileBrowserEventRouter::FileWatcherExtensions::GetVirtualPath() const {
return virtual_path_;
}
gdata::DriveFileSystemInterface*
FileBrowserEventRouter::GetRemoteFileSystem() const {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DriveSystemService* system_service =
DriveSystemServiceFactory::GetForProfile(profile_);
return (system_service ? system_service->file_system() : NULL);
}
bool FileBrowserEventRouter::FileWatcherExtensions::Watch
(const FilePath& path, FileWatcherDelegate* delegate) {
if (is_remote_file_system_)
return true;
return file_watcher_->Watch(path, delegate);
}
// static
scoped_refptr<FileBrowserEventRouter>
FileBrowserEventRouterFactory::GetForProfile(Profile* profile) {
return static_cast<FileBrowserEventRouter*>(
GetInstance()->GetServiceForProfile(profile, true).get());
}
// static
FileBrowserEventRouterFactory*
FileBrowserEventRouterFactory::GetInstance() {
return Singleton<FileBrowserEventRouterFactory>::get();
}
FileBrowserEventRouterFactory::FileBrowserEventRouterFactory()
: RefcountedProfileKeyedServiceFactory("FileBrowserEventRouter",
ProfileDependencyManager::GetInstance()) {
DependsOn(DriveSystemServiceFactory::GetInstance());
}
FileBrowserEventRouterFactory::~FileBrowserEventRouterFactory() {
}
scoped_refptr<RefcountedProfileKeyedService>
FileBrowserEventRouterFactory::BuildServiceInstanceFor(Profile* profile) const {
return scoped_refptr<RefcountedProfileKeyedService>(
new FileBrowserEventRouter(profile));
}
bool FileBrowserEventRouterFactory::ServiceHasOwnInstanceInIncognito() const {
// Explicitly and always allow this router in guest login mode. see
// chrome/browser/profiles/profile_keyed_base_factory.h comment
// for the details.
return true;
}