diff options
author | zelidrag@chromium.org <zelidrag@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-25 05:08:45 +0000 |
---|---|---|
committer | zelidrag@chromium.org <zelidrag@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-25 05:08:45 +0000 |
commit | 906625821f97a508d76810fba7b9641335f68b51 (patch) | |
tree | 4c477d7cc2bf16d1a19a24687d825bf9d1f0c9b3 | |
parent | 554abd90c658eb93fb078f3daca2a34b58291747 (diff) | |
download | chromium_src-906625821f97a508d76810fba7b9641335f68b51.zip chromium_src-906625821f97a508d76810fba7b9641335f68b51.tar.gz chromium_src-906625821f97a508d76810fba7b9641335f68b51.tar.bz2 |
Merged already reviewed CLs for r/o zip support in ChromeOS file browser:
Re-landing http://codereview.chromium.org/7457001:
Adding support for mount point different from removable
devices to MountLibrary
(work in progress..have to add GetMountPoints support)
tested on ChromeOS with these three patches:
http://gerrit.chromium.org/gerrit/#change,4447
http://gerrit.chromium.org/gerrit/#change,4449
http://gerrit.chromium.org/gerrit/#change,4544
http://codereview.chromium.org/7471039/:
UI for mounting/unmounting archive files.
BUG=chromium-os:17673, chromium-os:17783
TEST=Made sure mounting devices still works and that MountCompleted event gets through to file_browser_event_router (checking ui and chrome logs for mount completed entries)
TBR=tbarzic, dgozman
Review URL: http://codereview.chromium.org/7488044
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93838 0039d316-1c4b-4281-b951-d872f2087c98
29 files changed, 945 insertions, 157 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 60a4e531..965e792 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -8817,12 +8817,15 @@ ls' lab."> <message name="IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_LABEL" desc="/Downloads directory label."> File Shelf </message> - <message name="IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_WARNING" desc="Warning displayed to user when viewing downloads folder."> - <ph name="BEGIN_BOLD"><strong></ph>Caution:<ph name="END_BOLD"></strong></ph> These files are temporary and may be automatically deleted to free up disk space. <ph name="BEGIN_LINK"><a href="$1"></ph>Learn More<ph name="END_LINK"></a></ph> + <message name="IDS_FILE_BROWSER_ARCHIVE_DIRECTORY_LABEL" desc="/archive directory label."> + Archives </message> - <message name="IDS_FILE_BROWSER_MEDIA_DIRECTORY_LABEL" desc="/media directory label."> + <message name="IDS_FILE_BROWSER_REMOVABLE_DIRECTORY_LABEL" desc="/removable directory label."> External Storage </message> + <message name="IDS_FILE_BROWSER_DOWNLOADS_DIRECTORY_WARNING" desc="Warning displayed to user when viewing downloads folder."> + <ph name="BEGIN_BOLD"><strong></ph>Caution:<ph name="END_BOLD"></strong></ph> These files are temporary and may be automatically deleted to free up disk space. <ph name="BEGIN_LINK"><a href="$1"></ph>Learn More<ph name="END_LINK"></a></ph> + </message> <message name="IDS_FILE_BROWSER_NAME_COLUMN_LABEL" desc="Name column label."> Name </message> @@ -8880,6 +8883,16 @@ ls' lab."> Read Only </message> + <message name="IDS_FILE_BROWSER_ARCHIVE_MOUNT_FAILED" desc="Title of the error dialog when file mount operation failed."> + Unable to open open "$1": $2 + </message> + <message name="IDS_FILE_BROWSER_MOUNT_ARCHIVE" desc="Title of the action for mounting an archive file to work with its contents."> + Open archive + </message> + <message name="IDS_FILE_BROWSER_UNMOUNT_ARCHIVE" desc="Title of the action for unmounting the archive file."> + Close archive + </message> + <message name="IDS_FILE_BROWSER_CONFIRM_OVERWRITE_FILE" desc="Asks the user if they are sure they want to overwrite an existing file with another one."> A file named "$1" already exists. Do you want to replace it? </message> diff --git a/chrome/browser/chromeos/cros/mock_mount_library.cc b/chrome/browser/chromeos/cros/mock_mount_library.cc index 3e826a3..889522d 100644 --- a/chrome/browser/chromeos/cros/mock_mount_library.cc +++ b/chrome/browser/chromeos/cros/mock_mount_library.cc @@ -128,7 +128,7 @@ void MockMountLibrary::SetupDefaultReplies() { .WillRepeatedly(ReturnRef(disks_)); EXPECT_CALL(*this, RequestMountInfoRefresh()) .Times(AnyNumber()); - EXPECT_CALL(*this, MountPath(_)) + EXPECT_CALL(*this, MountPath(_, _, _)) .Times(AnyNumber()); EXPECT_CALL(*this, UnmountPath(_)) .Times(AnyNumber()); diff --git a/chrome/browser/chromeos/cros/mock_mount_library.h b/chrome/browser/chromeos/cros/mock_mount_library.h index c648569..5965f2c 100644 --- a/chrome/browser/chromeos/cros/mock_mount_library.h +++ b/chrome/browser/chromeos/cros/mock_mount_library.h @@ -25,9 +25,11 @@ class MockMountLibrary : public MountLibrary { MOCK_METHOD1(AddObserver, void(MountLibrary::Observer*)); MOCK_METHOD1(RemoveObserver, void(MountLibrary::Observer*)); MOCK_CONST_METHOD0(disks, const MountLibrary::DiskMap&(void)); + MOCK_CONST_METHOD0(mount_points, const MountLibrary::MountPointMap&(void)); MOCK_METHOD0(RequestMountInfoRefresh, void(void)); - MOCK_METHOD1(MountPath, void(const char*)); + MOCK_METHOD3(MountPath, void(const char*, MountType, + const MountPathOptions&)); MOCK_METHOD1(UnmountPath, void(const char*)); MOCK_METHOD3(UnmountDeviceRecursive, void(const char*, MountLibrary::UnmountDeviceRecursiveCallbackType, void*)); diff --git a/chrome/browser/chromeos/cros/mount_library.cc b/chrome/browser/chromeos/cros/mount_library.cc index 79e723a..df0b2c0 100644 --- a/chrome/browser/chromeos/cros/mount_library.cc +++ b/chrome/browser/chromeos/cros/mount_library.cc @@ -16,6 +16,38 @@ const char* kDeviceNotFound = "Device could not be found"; namespace chromeos { +// static +std::string MountLibrary::MountTypeToString(MountType type) { + switch (type) { + case MOUNT_TYPE_DEVICE: + return "device"; + case MOUNT_TYPE_ARCHIVE: + return "file"; + case MOUNT_TYPE_NETWORK_STORAGE: + return "network"; + case MOUNT_TYPE_INVALID: + return "invalid"; + default: + NOTREACHED(); + } + return ""; +} + +// static +MountType MountLibrary::MountTypeFromString( + const std::string& type_str) + OVERRIDE { + if (type_str == "device") { + return MOUNT_TYPE_DEVICE; + } else if (type_str == "network") { + return MOUNT_TYPE_NETWORK_STORAGE; + } else if (type_str == "file") { + return MOUNT_TYPE_ARCHIVE; + } else { + return MOUNT_TYPE_INVALID; + } +} + MountLibrary::Disk::Disk(const std::string& device_path, const std::string& mount_path, const std::string& system_path, @@ -90,31 +122,27 @@ class MountLibraryImpl : public MountLibrary { observers_.RemoveObserver(observer); } - virtual void MountPath(const char* device_path) OVERRIDE { + virtual void MountPath(const char* source_path, + MountType type, + const MountPathOptions& options) OVERRIDE { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!CrosLibrary::Get()->EnsureLoaded()) { - OnMountRemovableDevice(device_path, - NULL, - MOUNT_METHOD_ERROR_LOCAL, - kLibraryNotLoaded); + OnMountCompleted(MOUNT_ERROR_LIBRARY_NOT_LOADED, + MountPointInfo(source_path, NULL, type)); return; } - MountRemovableDevice(device_path, - &MountLibraryImpl::MountRemovableDeviceCallback, - this); + MountSourcePath(source_path, type, options, &MountCompletedHandler, this); } - virtual void UnmountPath(const char* device_path) OVERRIDE { + virtual void UnmountPath(const char* path) OVERRIDE { CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!CrosLibrary::Get()->EnsureLoaded()) { - OnUnmountRemovableDevice(device_path, - MOUNT_METHOD_ERROR_LOCAL, - kLibraryNotLoaded); + OnUnmountPath(path, + MOUNT_METHOD_ERROR_LOCAL, + kLibraryNotLoaded); return; } - UnmountRemovableDevice(device_path, - &MountLibraryImpl::UnmountRemovableDeviceCallback, - this); + UnmountMountPoint(path, &MountLibraryImpl::UnmountMountPointCallback, this); } virtual void UnmountDeviceRecursive(const char* device_path, @@ -151,9 +179,10 @@ class MountLibraryImpl : public MountLibrary { cb_data = new UnmountDeviceRecursiveCallbackData(this, user_data, callback, devices_to_unmount.size()); for (std::vector<const char*>::iterator it = devices_to_unmount.begin(); - it != devices_to_unmount.end(); - ++it) { - UnmountRemovableDevice(*it, + it != devices_to_unmount.end(); + ++it) { + UnmountMountPoint( + *it, &MountLibraryImpl::UnmountDeviceRecursiveCallback, cb_data); } @@ -178,39 +207,34 @@ class MountLibraryImpl : public MountLibrary { } const DiskMap& disks() const OVERRIDE { return disks_; } + const MountPointMap& mount_points() const OVERRIDE { return mount_points_; } private: - // Callback for MountRemovableDevice method. - static void MountRemovableDeviceCallback(void* object, - const char* device_path, - const char* mount_path, - MountMethodErrorType error, - const char* error_message) { + // Callback for MountComplete signal and MountSourcePath method. + static void MountCompletedHandler(void* object, + MountError error_code, + const char* source_path, + MountType type, + const char* mount_path) { DCHECK(object); MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); - self->OnMountRemovableDevice(device_path, - mount_path, - error, - error_message); + self->OnMountCompleted(static_cast<MountError>(error_code), + MountPointInfo(source_path, mount_path, type)); } // Callback for UnmountRemovableDevice method. - static void UnmountRemovableDeviceCallback(void* object, - const char* device_path, - const char* mount_path, - MountMethodErrorType error, - const char* error_message) { + static void UnmountMountPointCallback(void* object, + const char* device_path, + MountMethodErrorType error, + const char* error_message) { DCHECK(object); MountLibraryImpl* self = static_cast<MountLibraryImpl*>(object); - self->OnUnmountRemovableDevice(device_path, - error, - error_message); + self->OnUnmountPath(device_path, error, error_message); } // Callback for UnmountDeviceRecursive. static void UnmountDeviceRecursiveCallback(void* object, const char* device_path, - const char* mount_path, MountMethodErrorType error, const char* error_message) { DCHECK(object); @@ -218,9 +242,9 @@ class MountLibraryImpl : public MountLibrary { static_cast<UnmountDeviceRecursiveCallbackData*>(object); // Do standard processing for Unmount event. - cb_data->object->OnUnmountRemovableDevice(device_path, - error, - error_message); + cb_data->object->OnUnmountPath(device_path, + error, + error_message); if (error == MOUNT_METHOD_ERROR_LOCAL) { cb_data->success = false; } else if (error == MOUNT_METHOD_ERROR_NONE) { @@ -275,14 +299,26 @@ class MountLibraryImpl : public MountLibrary { } - void OnMountRemovableDevice(const char* device_path, - const char* mount_path, - MountMethodErrorType error, - const char* error_message) { - DCHECK(device_path); + void OnMountCompleted(MountError error_code, + const MountPointInfo& mount_info) { + DCHECK(!mount_info.source_path.empty()); - if (error == MOUNT_METHOD_ERROR_NONE && device_path && mount_path) { - std::string path(device_path); + FireMountCompleted(MOUNTING, + error_code, + mount_info); + + if (error_code == MOUNT_ERROR_NONE && + mount_points_.find(mount_info.source_path) == mount_points_.end()) { + mount_points_.insert(MountPointMap::value_type( + mount_info.source_path.c_str(), + mount_info)); + } + + if (error_code == MOUNT_ERROR_NONE && + mount_info.mount_type == MOUNT_TYPE_DEVICE && + !mount_info.source_path.empty() && + !mount_info.mount_path.empty()) { + std::string path(mount_info.source_path); DiskMap::iterator iter = disks_.find(path); if (iter == disks_.end()) { // disk might have been removed by now? @@ -290,21 +326,30 @@ class MountLibraryImpl : public MountLibrary { } Disk* disk = iter->second; DCHECK(disk); - disk->set_mount_path(mount_path); + disk->set_mount_path(mount_info.mount_path.c_str()); FireDiskStatusUpdate(MOUNT_DISK_MOUNTED, disk); - } else { - LOG(WARNING) << "Mount request failed for device " - << device_path << ", with error: " - << (error_message ? error_message : "Unknown"); } } - void OnUnmountRemovableDevice(const char* device_path, - MountMethodErrorType error, - const char* error_message) { - DCHECK(device_path); - if (error == MOUNT_METHOD_ERROR_NONE && device_path) { - std::string path(device_path); + void OnUnmountPath(const char* source_path, + MountMethodErrorType error, + const char* error_message) { + DCHECK(source_path); + + if (error == MOUNT_METHOD_ERROR_NONE && source_path) { + MountPointMap::iterator mount_points_it = mount_points_.find(source_path); + if (mount_points_it == mount_points_.end()) + return; + // TODO(tbarzic): Add separate, PathUnmounted event to Observer. + FireMountCompleted( + UNMOUNTING, + MOUNT_ERROR_NONE, + MountPointInfo(mount_points_it->second.source_path.c_str(), + mount_points_it->second.mount_path.c_str(), + mount_points_it->second.mount_type)); + mount_points_.erase(mount_points_it); + + std::string path(source_path); DiskMap::iterator iter = disks_.find(path); if (iter == disks_.end()) { // disk might have been removed by now? @@ -316,7 +361,7 @@ class MountLibraryImpl : public MountLibrary { FireDiskStatusUpdate(MOUNT_DISK_UNMOUNTED, disk); } else { LOG(WARNING) << "Unmount request failed for device " - << device_path << ", with error: " + << source_path << ", with error: " << (error_message ? error_message : "Unknown"); } } @@ -476,16 +521,17 @@ class MountLibraryImpl : public MountLibrary { type = MOUNT_DEVICE_SCANNED; break; } - default: + default: { return; + } } FireDeviceStatusUpdate(type, std::string(device_path)); } void Init() { // Getting the monitor status so that the daemon starts up. - mount_status_connection_ = MonitorMountEvents( - &MonitorMountEventsHandler, this); + mount_status_connection_ = MonitorAllMountEvents( + &MonitorMountEventsHandler, &MountCompletedHandler, this); } void FireDiskStatusUpdate(MountLibraryEventType evt, @@ -504,6 +550,17 @@ class MountLibraryImpl : public MountLibrary { Observer, observers_, DeviceChanged(evt, device_path)); } + void FireMountCompleted(MountEvent event_type, + MountError error_code, + const MountPointInfo& mount_info) { + // Make sure we run on UI thread. + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + FOR_EACH_OBSERVER( + Observer, observers_, MountCompleted(event_type, + error_code, + mount_info)); + } + // Mount event change observers. ObserverList<Observer> observers_; @@ -514,6 +571,8 @@ class MountLibraryImpl : public MountLibrary { // The list of disks found. MountLibrary::DiskMap disks_; + MountLibrary::MountPointMap mount_points_; + DISALLOW_COPY_AND_ASSIGN(MountLibraryImpl); }; @@ -526,9 +585,20 @@ class MountLibraryStubImpl : public MountLibrary { virtual void AddObserver(Observer* observer) OVERRIDE {} virtual void RemoveObserver(Observer* observer) OVERRIDE {} virtual const DiskMap& disks() const OVERRIDE { return disks_; } + virtual const MountPointMap& mount_points() const OVERRIDE { + return mount_points_; + } + virtual std::string MountTypeToString(MountType type) const OVERRIDE { + return ""; + } + virtual MountType MountTypeFromString(const std::string& type_str) const + OVERRIDE { + return MOUNT_TYPE_INVALID; + } virtual void RequestMountInfoRefresh() OVERRIDE {} - virtual void MountPath(const char* device_path) OVERRIDE {} - virtual void UnmountPath(const char* device_path) OVERRIDE {} + virtual void MountPath(const char* source_path, MountType type, + const MountPathOptions& options) OVERRIDE {} + virtual void UnmountPath(const char* path) OVERRIDE {} virtual void UnmountDeviceRecursive(const char* device_path, UnmountDeviceRecursiveCallbackType callback, void* user_data) OVERRIDE {} @@ -536,6 +606,7 @@ class MountLibraryStubImpl : public MountLibrary { private: // The list of disks found. DiskMap disks_; + MountPointMap mount_points_; DISALLOW_COPY_AND_ASSIGN(MountLibraryStubImpl); }; diff --git a/chrome/browser/chromeos/cros/mount_library.h b/chrome/browser/chromeos/cros/mount_library.h index b735d3d..8be7a8a 100644 --- a/chrome/browser/chromeos/cros/mount_library.h +++ b/chrome/browser/chromeos/cros/mount_library.h @@ -6,8 +6,8 @@ #define CHROME_BROWSER_CHROMEOS_CROS_MOUNT_LIBRARY_H_ #pragma once -#include <string> #include <map> +#include <string> #include "base/memory/singleton.h" #include "base/observer_list.h" @@ -32,6 +32,10 @@ typedef enum MountLibraryEventType { // library class like this: chromeos::CrosLibrary::Get()->GetMountLibrary() class MountLibrary { public: + enum MountEvent { + MOUNTING, + UNMOUNTING + }; // Used to house an instance of each found mount device. class Disk { public: @@ -97,6 +101,22 @@ class MountLibrary { }; typedef std::map<std::string, Disk*> DiskMap; + // MountPointInfo: {mount_path, mount_type}. + struct MountPointInfo { + std::string source_path; + std::string mount_path; + MountType mount_type; + + MountPointInfo(const char* source, const char* mount, const MountType type) + : source_path(source ? source : ""), + mount_path(mount ? mount : ""), + mount_type(type) { + } + }; + + // MountPointMap key is source_path. + typedef std::map<std::string, MountPointInfo> MountPointMap; + typedef void(*UnmountDeviceRecursiveCallbackType)(void*, bool); class Observer { @@ -107,21 +127,32 @@ class MountLibrary { const Disk* disk) = 0; virtual void DeviceChanged(MountLibraryEventType event, const std::string& device_path ) = 0; + virtual void MountCompleted(MountEvent event_type, + MountError error_code, + const MountPointInfo& mount_info) = 0; }; virtual ~MountLibrary() {} virtual void AddObserver(Observer* observer) = 0; virtual void RemoveObserver(Observer* observer) = 0; virtual const DiskMap& disks() const = 0; + virtual const MountPointMap& mount_points() const = 0; virtual void RequestMountInfoRefresh() = 0; - virtual void MountPath(const char* device_path) = 0; - virtual void UnmountPath(const char* device_path) = 0; + virtual void MountPath(const char* source_path, + MountType type, + const MountPathOptions& options) = 0; + // |path| may be source od mount path. + virtual void UnmountPath(const char* path) = 0; // Unmounts device_poath and all of its known children. virtual void UnmountDeviceRecursive(const char* device_path, UnmountDeviceRecursiveCallbackType callback, void* user_data) = 0; + // Helper functions for parameter conversions. + static std::string MountTypeToString(MountType type); + static MountType MountTypeFromString(const std::string& type_str); + // Factory function, creates a new instance and returns ownership. // For normal usage, access the singleton via CrosLibrary::Get(). static MountLibrary* GetImpl(bool stub); diff --git a/chrome/browser/chromeos/extensions/file_browser_event_router.cc b/chrome/browser/chromeos/extensions/file_browser_event_router.cc index df0d61e..91df6b3 100644 --- a/chrome/browser/chromeos/extensions/file_browser_event_router.cc +++ b/chrome/browser/chromeos/extensions/file_browser_event_router.cc @@ -42,6 +42,28 @@ const char* DeviceTypeToString(chromeos::DeviceType type) { return "undefined"; } +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_UNSUPORTED_FILESYSTEM: + return "error_unsuported_filesystem"; + case chromeos::MOUNT_ERROR_INVALID_ARCHIVE: + return "error_invalid_archive"; + case chromeos::MOUNT_ERROR_LIBRARY_NOT_LOADED: + return "error_libcros_missing"; + default: + NOTREACHED(); + } + return ""; +} + DictionaryValue* DiskToDictionaryValue( const chromeos::MountLibrary::Disk* disk) { DictionaryValue* result = new DictionaryValue(); @@ -152,6 +174,12 @@ void ExtensionFileBrowserEventRouter::DeviceChanged( OnDeviceScanned(device_path); } } +void ExtensionFileBrowserEventRouter::MountCompleted( + chromeos::MountLibrary::MountEvent event_type, + chromeos::MountError error_code, + const chromeos::MountLibrary::MountPointInfo& mount_info) { + DispatchMountCompletedEvent(event_type, error_code, mount_info); +} void ExtensionFileBrowserEventRouter::HandleFileWatchNotification( const FilePath& local_path, bool got_error) { @@ -218,6 +246,58 @@ void ExtensionFileBrowserEventRouter::DispatchMountEvent( GURL()); } +void ExtensionFileBrowserEventRouter::DispatchMountCompletedEvent( + chromeos::MountLibrary::MountEvent event, + chromeos::MountError error_code, + const chromeos::MountLibrary::MountPointInfo& mount_info) { + if (!profile_ || mount_info.mount_type == chromeos::MOUNT_TYPE_INVALID) { + NOTREACHED(); + return; + } + + ListValue args; + DictionaryValue* mount_info_value = new DictionaryValue(); + args.Append(mount_info_value); + if (event == chromeos::MountLibrary::MOUNTING) { + mount_info_value->SetString("eventType", "mount"); + } else { + mount_info_value->SetString("eventType", "unmount"); + } + mount_info_value->SetString("status", MountErrorToString(error_code)); + mount_info_value->SetString("mountType", + chromeos::MountLibrary::MountTypeToString(mount_info.mount_type)); + + if (mount_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) { + GURL source_url; + if (FileManagerUtil::ConvertFileToFileSystemUrl(profile_, + FilePath(mount_info.source_path), + FileManagerUtil::GetFileBrowserExtensionUrl().GetOrigin(), + &source_url)) { + mount_info_value->SetString("sourceUrl", source_url.spec()); + } + } else { + mount_info_value->SetString("sourceUrl", mount_info.source_path); + } + + // If there were no error, add mountPath to the event. + if (error_code == chromeos::MOUNT_ERROR_NONE) { + FilePath relative_mount_path; + // Convert mount point path to relative path with the external file system + // exposed within File API. + if (FileManagerUtil::ConvertFileToRelativeFileSystemPath(profile_, + FilePath(mount_info.mount_path), + &relative_mount_path)) { + mount_info_value->SetString("mountPath", relative_mount_path.value()); + } + } + + std::string args_json; + base::JSONWriter::Write(&args, false /* pretty_print */, &args_json); + profile_->GetExtensionEventRouter()->DispatchEventToRenderers( + extension_event_names::kOnFileBrowserMountCompleted, args_json, NULL, + GURL()); +} + void ExtensionFileBrowserEventRouter::OnDiskAdded( const chromeos::MountLibrary::Disk* disk) { VLOG(1) << "Disk added: " << disk->device_path(); @@ -237,7 +317,9 @@ void ExtensionFileBrowserEventRouter::OnDiskAdded( // Initiate disk mount operation. chromeos::MountLibrary* lib = chromeos::CrosLibrary::Get()->GetMountLibrary(); - lib->MountPath(disk->device_path().c_str()); + lib->MountPath(disk->device_path().c_str(), + chromeos::MOUNT_TYPE_DEVICE, + chromeos::MountPathOptions()); // Unused. } } diff --git a/chrome/browser/chromeos/extensions/file_browser_event_router.h b/chrome/browser/chromeos/extensions/file_browser_event_router.h index fb8047d..ebc7f6c 100644 --- a/chrome/browser/chromeos/extensions/file_browser_event_router.h +++ b/chrome/browser/chromeos/extensions/file_browser_event_router.h @@ -47,6 +47,10 @@ class ExtensionFileBrowserEventRouter virtual void DeviceChanged(chromeos::MountLibraryEventType event, const std::string& device_path) OVERRIDE; + virtual void MountCompleted(chromeos::MountLibrary::MountEvent event_type, + chromeos::MountError error_code, + const chromeos::MountLibrary::MountPointInfo& mount_info) OVERRIDE; + private: typedef std::map<std::string, linked_ptr<chromeos::SystemNotification> > NotificationMap; @@ -103,6 +107,10 @@ class ExtensionFileBrowserEventRouter // Sends filesystem changed extension message to all renderers. void DispatchMountEvent(const chromeos::MountLibrary::Disk* disk, bool added); + void DispatchMountCompletedEvent(chromeos::MountLibrary::MountEvent event, + chromeos::MountError error_code, + const chromeos::MountLibrary::MountPointInfo& mount_info); + void RemoveBrowserFromVector(const std::string& path); // Used to create a window of a standard size, and add it to a list diff --git a/chrome/browser/extensions/extension_event_names.cc b/chrome/browser/extensions/extension_event_names.cc index 76c0d8f..fc226f3 100644 --- a/chrome/browser/extensions/extension_event_names.cc +++ b/chrome/browser/extensions/extension_event_names.cc @@ -25,6 +25,8 @@ const char kOnExtensionDisabled[] = "management.onDisabled"; const char kOnFileBrowserDiskChanged[] = "fileBrowserPrivate.onDiskChanged"; const char kOnFileChanged[] = "fileBrowserPrivate.onFileChanged"; +const char kOnFileBrowserMountCompleted[] = + "fileBrowserPrivate.onMountCompleted"; const char kOnInputMethodChanged[] = "inputMethodPrivate.onChanged"; } // namespace extension_event_names diff --git a/chrome/browser/extensions/extension_event_names.h b/chrome/browser/extensions/extension_event_names.h index b6958e2..0b26e4f 100644 --- a/chrome/browser/extensions/extension_event_names.h +++ b/chrome/browser/extensions/extension_event_names.h @@ -33,6 +33,7 @@ extern const char kOnExtensionDisabled[]; // FileBrowser. extern const char kOnFileBrowserDiskChanged[]; extern const char kOnFileChanged[]; +extern const char kOnFileBrowserMountCompleted[]; // InputMethod. extern const char kOnInputMethodChanged[]; diff --git a/chrome/browser/extensions/extension_file_browser_private_api.cc b/chrome/browser/extensions/extension_file_browser_private_api.cc index a5f386f..95dbc0c 100644 --- a/chrome/browser/extensions/extension_file_browser_private_api.cc +++ b/chrome/browser/extensions/extension_file_browser_private_api.cc @@ -238,6 +238,40 @@ void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id) { base::Time::kMicrosecondsPerSecond))); } +#ifdef OS_CHROMEOS +base::DictionaryValue* MountPointToValue(Profile* profile, + const chromeos::MountLibrary::MountPointInfo& mount_point_info) { + + base::DictionaryValue *mount_info = new base::DictionaryValue(); + + mount_info->SetString("mountType", + chromeos::MountLibrary::MountTypeToString(mount_point_info.mount_type)); + + if (mount_point_info.mount_type == chromeos::MOUNT_TYPE_ARCHIVE) { + GURL source_url; + if (FileManagerUtil::ConvertFileToFileSystemUrl(profile, + FilePath(mount_point_info.source_path), + FileManagerUtil::GetFileBrowserExtensionUrl().GetOrigin(), + &source_url)) { + mount_info->SetString("sourceUrl", source_url.spec()); + } + } else { + mount_info->SetString("sourceUrl", mount_point_info.source_path); + } + + FilePath relative_mount_path; + // Convert mount point path to relative path with the external file system + // exposed within File API. + if (FileManagerUtil::ConvertFileToRelativeFileSystemPath(profile, + FilePath(mount_point_info.mount_path), + &relative_mount_path)) { + mount_info->SetString("mountPath", relative_mount_path.value()); + } + + return mount_info; +} +#endif + class LocalFileSystemCallbackDispatcher : public fileapi::FileSystemCallbackDispatcher { @@ -869,13 +903,13 @@ void ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread( SendResponse(true); } -FileDialogFunction::FileDialogFunction() { +FileBrowserFunction::FileBrowserFunction() { } -FileDialogFunction::~FileDialogFunction() { +FileBrowserFunction::~FileBrowserFunction() { } -int32 FileDialogFunction::GetTabId() const { +int32 FileBrowserFunction::GetTabId() const { int32 tab_id = 0; if (!dispatcher()) { NOTREACHED(); @@ -884,8 +918,9 @@ int32 FileDialogFunction::GetTabId() const { // TODO(jamescook): This is going to fail when we switch to tab-modal // dialogs. Figure out a way to find which SelectFileDialog::Listener - // to call from inside these extension FileDialogFunctions. - Browser* browser = const_cast<FileDialogFunction*>(this)->GetCurrentBrowser(); + // to call from inside these extension FileBrowserFunctions. + Browser* browser = + const_cast<FileBrowserFunction*>(this)->GetCurrentBrowser(); if (browser) { TabContents* contents = browser->GetSelectedTabContents(); if (contents) @@ -898,8 +933,8 @@ int32 FileDialogFunction::GetTabId() const { // so here we are. This function takes a vector of virtual paths, converts // them to local paths and calls GetLocalPathsResponseOnUIThread with the // result vector, on the UI thread. -void FileDialogFunction::GetLocalPathsOnFileThread(const UrlList& file_urls, - const std::string& task_id) { +void FileBrowserFunction::GetLocalPathsOnFileThread(const UrlList& file_urls, + void* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); FilePathList selected_files; @@ -938,13 +973,11 @@ void FileDialogFunction::GetLocalPathsOnFileThread(const UrlList& file_urls, } #endif - if (!selected_files.empty()) { - BrowserThread::PostTask( - BrowserThread::UI, FROM_HERE, - NewRunnableMethod(this, - &FileDialogFunction::GetLocalPathsResponseOnUIThread, - selected_files, task_id)); - } + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(this, + &FileBrowserFunction::GetLocalPathsResponseOnUIThread, + selected_files, context)); } bool SelectFileFunction::RunImpl() { @@ -960,14 +993,15 @@ bool SelectFileFunction::RunImpl() { BrowserThread::FILE, FROM_HERE, NewRunnableMethod(this, &SelectFileFunction::GetLocalPathsOnFileThread, - file_paths, std::string())); + file_paths, reinterpret_cast<void*>(NULL))); return true; } void SelectFileFunction::GetLocalPathsResponseOnUIThread( - const FilePathList& files, const std::string& task_id) { + const FilePathList& files, void* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!context); if (files.size() != 1) { SendResponse(false); return; @@ -1011,18 +1045,23 @@ bool ViewFilesFunction::RunImpl() { BrowserThread::FILE, FROM_HERE, NewRunnableMethod(this, &ViewFilesFunction::GetLocalPathsOnFileThread, - file_urls, internal_task_id)); + file_urls, + reinterpret_cast<void*>(new std::string(internal_task_id)))); return true; } void ViewFilesFunction::GetLocalPathsResponseOnUIThread( - const FilePathList& files, const std::string& internal_task_id) { + const FilePathList& files, void* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(context); + scoped_ptr<std::string> internal_task_id( + reinterpret_cast<std::string*>(context)); for (FilePathList::const_iterator iter = files.begin(); iter != files.end(); ++iter) { - FileManagerUtil::ViewItem(*iter, internal_task_id == kEnqueueTaskId); + FileManagerUtil::ViewItem(*iter, + *(internal_task_id.get()) == kEnqueueTaskId); } SendResponse(true); } @@ -1055,14 +1094,15 @@ bool SelectFilesFunction::RunImpl() { BrowserThread::FILE, FROM_HERE, NewRunnableMethod(this, &SelectFilesFunction::GetLocalPathsOnFileThread, - file_urls, std::string())); + file_urls, reinterpret_cast<void*>(NULL))); return true; } void SelectFilesFunction::GetLocalPathsResponseOnUIThread( - const FilePathList& files, const std::string& internal_task_id) { + const FilePathList& files, void* context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(!context); int32 tab_id = GetTabId(); FileManagerDialog::OnMultiFilesSelected(tab_id, files); SendResponse(true); @@ -1075,31 +1115,150 @@ bool CancelFileDialogFunction::RunImpl() { return true; } -UnmountVolumeFunction::UnmountVolumeFunction() { +AddMountFunction::AddMountFunction() { +} + +AddMountFunction::~AddMountFunction() { +} + +bool AddMountFunction::RunImpl() { + if (args_->GetSize() != 2 && args_->GetSize() != 3) { + error_ = "Invalid argument count"; + return false; + } + + std::string file_url; + if (!args_->GetString(0, &file_url)) { + return false; + } + + std::string mount_type_str; + if (!args_->GetString(1, &mount_type_str)) { + return false; + } + + UrlList file_paths; + file_paths.push_back(GURL(file_url)); + + chromeos::MountPathOptions options; + if (args_->GetSize() == 3) { + DictionaryValue *dict; + if (!args_->GetDictionary(2, &dict)) { + NOTREACHED(); + } + + for (base::DictionaryValue::key_iterator it = dict->begin_keys(); + it != dict->end_keys(); + ++it) { + std::string value; + if (!dict->GetString(*it, &value)) { + NOTREACHED(); + } + + options.push_back(chromeos::MountPathOptions::value_type((*it).c_str(), + value.c_str())); + } + } + + MountParamaters* params = new MountParamaters(mount_type_str, options); + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &AddMountFunction::GetLocalPathsOnFileThread, + file_paths, reinterpret_cast<void*>(params))); + + return true; +} + +void AddMountFunction::GetLocalPathsResponseOnUIThread( + const FilePathList& files, void* context) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + DCHECK(context); + scoped_ptr<MountParamaters> params( + reinterpret_cast<MountParamaters*>(context)); + + if (!files.size()) { + SendResponse(false); + return; + } + + const std::string& mount_type_str = params->mount_type; + const chromeos::MountPathOptions& options = params->mount_options; + FilePath::StringType source_file = files[0].value(); + +#ifdef OS_CHROMEOS + chromeos::MountLibrary *mount_lib = + chromeos::CrosLibrary::Get()->GetMountLibrary(); + + chromeos::MountType mount_type = + mount_lib->MountTypeFromString(mount_type_str); + if (mount_type == chromeos::MOUNT_TYPE_INVALID) { + error_ = "Invalid mount type"; + SendResponse(false); + return; + } + + mount_lib->MountPath(source_file.data(), mount_type, options); +#endif + + SendResponse(true); +} + +RemoveMountFunction::RemoveMountFunction() { } -UnmountVolumeFunction::~UnmountVolumeFunction() { +RemoveMountFunction::~RemoveMountFunction() { } -bool UnmountVolumeFunction::RunImpl() { +bool RemoveMountFunction::RunImpl() { if (args_->GetSize() != 1) { return false; } - std::string volume_device_path; - if (!args_->GetString(0, &volume_device_path)) { - NOTREACHED(); + std::string mount_path; + if (!args_->GetString(0, &mount_path)) { + return false; } #ifdef OS_CHROMEOS chromeos::CrosLibrary::Get()->GetMountLibrary()->UnmountPath( - volume_device_path.c_str()); + mount_path.c_str()); #endif SendResponse(true); return true; } +GetMountPointsFunction::GetMountPointsFunction() { +} + +GetMountPointsFunction::~GetMountPointsFunction() { +} + +bool GetMountPointsFunction::RunImpl() { + if (args_->GetSize()) + return false; + + base::ListValue *mounts = new base::ListValue(); + result_.reset(mounts); + + #ifdef OS_CHROMEOS + chromeos::MountLibrary *mount_lib = + chromeos::CrosLibrary::Get()->GetMountLibrary(); + chromeos::MountLibrary::MountPointMap mount_points = + mount_lib->mount_points(); + + for (chromeos::MountLibrary::MountPointMap::const_iterator it = + mount_points.begin(); + it != mount_points.end(); + ++it) { + mounts->Append(MountPointToValue(profile_, it->second)); + } +#endif + SendResponse(true); + return true; +} + GetVolumeMetadataFunction::GetVolumeMetadataFunction() { } @@ -1141,7 +1300,6 @@ bool GetVolumeMetadataFunction::RunImpl() { volume_info->SetBoolean("hasMedia", volume->has_media()); volume_info->SetBoolean("isOnBootDevice", volume->on_boot_device()); - SendResponse(true); return true; } #endif @@ -1178,8 +1336,8 @@ bool FileDialogStringsFunction::RunImpl() { SET_STRING(IDS_FILE_BROWSER, ROOT_DIRECTORY_LABEL); SET_STRING(IDS_FILE_BROWSER, DOWNLOADS_DIRECTORY_LABEL); - SET_STRING(IDS_FILE_BROWSER, DOWNLOADS_DIRECTORY_WARNING); - SET_STRING(IDS_FILE_BROWSER, MEDIA_DIRECTORY_LABEL); + SET_STRING(IDS_FILE_BROWSER, ARCHIVE_DIRECTORY_LABEL); + SET_STRING(IDS_FILE_BROWSER, REMOVABLE_DIRECTORY_LABEL); SET_STRING(IDS_FILE_BROWSER, NAME_COLUMN_LABEL); SET_STRING(IDS_FILE_BROWSER, SIZE_COLUMN_LABEL); SET_STRING(IDS_FILE_BROWSER, DATE_COLUMN_LABEL); @@ -1201,6 +1359,10 @@ bool FileDialogStringsFunction::RunImpl() { SET_STRING(IDS_FILE_BROWSER, VOLUME_LABEL); SET_STRING(IDS_FILE_BROWSER, READ_ONLY); + SET_STRING(IDS_FILE_BROWSER, ARCHIVE_MOUNT_FAILED); + SET_STRING(IDS_FILE_BROWSER, MOUNT_ARCHIVE); + SET_STRING(IDS_FILE_BROWSER, UNMOUNT_ARCHIVE); + SET_STRING(IDS_FILE_BROWSER, CONFIRM_OVERWRITE_FILE); SET_STRING(IDS_FILE_BROWSER, FILE_ALREADY_EXISTS); SET_STRING(IDS_FILE_BROWSER, DIRECTORY_ALREADY_EXISTS); diff --git a/chrome/browser/extensions/extension_file_browser_private_api.h b/chrome/browser/extensions/extension_file_browser_private_api.h index 065b9e2..69bd537 100644 --- a/chrome/browser/extensions/extension_file_browser_private_api.h +++ b/chrome/browser/extensions/extension_file_browser_private_api.h @@ -118,24 +118,24 @@ class ExecuteTasksFileBrowserFunction : public AsyncExtensionFunction { }; // Parent class for the chromium extension APIs for the file dialog. -class FileDialogFunction +class FileBrowserFunction : public AsyncExtensionFunction { public: - FileDialogFunction(); + FileBrowserFunction(); protected: typedef std::vector<GURL> UrlList; typedef std::vector<FilePath> FilePathList; - virtual ~FileDialogFunction(); + virtual ~FileBrowserFunction(); // Convert virtual paths to local paths on the file thread. void GetLocalPathsOnFileThread(const UrlList& file_urls, - const std::string& internal_task_id); + void* context); // Callback with converted local paths. virtual void GetLocalPathsResponseOnUIThread(const FilePathList& files, - const std::string& internal_task_id) {} + void* context) { NOTREACHED(); } // Figure out the tab_id of the hosting tab. int32 GetTabId() const; @@ -143,7 +143,7 @@ class FileDialogFunction // Select a single file. Closes the dialog window. class SelectFileFunction - : public FileDialogFunction { + : public FileBrowserFunction { public: SelectFileFunction() {} @@ -153,9 +153,9 @@ class SelectFileFunction // AsyncExtensionFunction overrides. virtual bool RunImpl() OVERRIDE; - // FileDialogFunction overrides. - virtual void GetLocalPathsResponseOnUIThread( - const FilePathList& files, const std::string& internal_task_id) OVERRIDE; + // FileBrowserFunction overrides. + virtual void GetLocalPathsResponseOnUIThread(const FilePathList& files, + void* context) OVERRIDE; private: DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.selectFile"); @@ -163,7 +163,7 @@ class SelectFileFunction // View multiple selected files. Window stays open. class ViewFilesFunction - : public FileDialogFunction { + : public FileBrowserFunction { public: ViewFilesFunction(); @@ -173,9 +173,9 @@ class ViewFilesFunction // AsyncExtensionFunction overrides. virtual bool RunImpl() OVERRIDE; - // FileDialogFunction overrides. + // FileBrowserFunction overrides. virtual void GetLocalPathsResponseOnUIThread( - const FilePathList& files, const std::string& internal_task_id) OVERRIDE; + const FilePathList& files, void* context) OVERRIDE; private: DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.viewFiles"); @@ -183,7 +183,7 @@ class ViewFilesFunction // Select multiple files. Closes the dialog window. class SelectFilesFunction - : public FileDialogFunction { + : public FileBrowserFunction { public: SelectFilesFunction(); @@ -193,9 +193,9 @@ class SelectFilesFunction // AsyncExtensionFunction overrides. virtual bool RunImpl() OVERRIDE; - // FileDialogFunction overrides. + // FileBrowserFunction overrides. virtual void GetLocalPathsResponseOnUIThread( - const FilePathList& files, const std::string& internal_task_id) OVERRIDE; + const FilePathList& files, void* context) OVERRIDE; private: DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.selectFiles"); @@ -203,7 +203,7 @@ class SelectFilesFunction // Cancel file selection Dialog. Closes the dialog window. class CancelFileDialogFunction - : public FileDialogFunction { + : public FileBrowserFunction { public: CancelFileDialogFunction() {} @@ -217,19 +217,60 @@ class CancelFileDialogFunction DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.cancelDialog"); }; -// Unmounts selected device. Expects volume's device path as an argument. -class UnmountVolumeFunction +// Mount a device or a file. +class AddMountFunction + : public FileBrowserFunction { + public: + AddMountFunction(); + + protected: + virtual ~AddMountFunction(); + + virtual bool RunImpl() OVERRIDE; + + // FileBrowserFunction overrides. + virtual void GetLocalPathsResponseOnUIThread( + const FilePathList& files, void* context) OVERRIDE; + + private: + struct MountParamaters { + MountParamaters(const std::string& type, + const chromeos::MountPathOptions& options) + : mount_type(type), mount_options(options) {} + std::string mount_type; + chromeos::MountPathOptions mount_options; + }; + + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.addMount"); +}; + +// Unmounts selected device. Expects mount point path as an argument. +class RemoveMountFunction : public SyncExtensionFunction { - public: - UnmountVolumeFunction(); + public: + RemoveMountFunction(); + + protected: + virtual ~RemoveMountFunction(); + + virtual bool RunImpl() OVERRIDE; + + private: + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.removeMount"); +}; + +class GetMountPointsFunction + : public AsyncExtensionFunction { + public: + GetMountPointsFunction(); protected: - virtual ~UnmountVolumeFunction(); + virtual ~GetMountPointsFunction(); virtual bool RunImpl() OVERRIDE; private: - DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.unmountVolume"); + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.getMountPoints"); }; // Retrieves devices meta-data. Expects volume's device path as an argument. diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index e3ce123..640492f 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -347,7 +347,9 @@ void FactoryRegistry::ResetFunctions() { RegisterFunction<RemoveFileWatchBrowserFunction>(); RegisterFunction<SelectFileFunction>(); RegisterFunction<SelectFilesFunction>(); - RegisterFunction<UnmountVolumeFunction>(); + RegisterFunction<AddMountFunction>(); + RegisterFunction<RemoveMountFunction>(); + RegisterFunction<GetMountPointsFunction>(); RegisterFunction<ViewFilesFunction>(); // Mediaplayer diff --git a/chrome/browser/extensions/file_manager_util.cc b/chrome/browser/extensions/file_manager_util.cc index fb19e6c..76b5c43 100644 --- a/chrome/browser/extensions/file_manager_util.cc +++ b/chrome/browser/extensions/file_manager_util.cc @@ -27,6 +27,7 @@ // This is the "well known" url for the file manager extension from // browser/resources/file_manager. In the future we may provide a way to swap // out this file manager for an aftermarket part, but not yet. +const char kFileBrowserExtensionUrl[] = FILEBROWSER_URL(""); const char kBaseFileBrowserUrl[] = FILEBROWSER_URL("main.html"); const char kMediaPlayerUrl[] = FILEBROWSER_URL("mediaplayer.html"); const char kMediaPlayerPlaylistUrl[] = FILEBROWSER_URL("playlist.html"); @@ -67,6 +68,11 @@ bool IsSupportedAVExtension(const char* ext) { } // static +GURL FileManagerUtil::GetFileBrowserExtensionUrl() { + return GURL(kFileBrowserExtensionUrl); +} + +// static GURL FileManagerUtil::GetFileBrowserUrl() { return GURL(kBaseFileBrowserUrl); } @@ -85,6 +91,21 @@ GURL FileManagerUtil::GetMediaPlayerPlaylistUrl() { bool FileManagerUtil::ConvertFileToFileSystemUrl( Profile* profile, const FilePath& full_file_path, const GURL& origin_url, GURL* url) { + FilePath virtual_path; + if (!ConvertFileToRelativeFileSystemPath(profile, full_file_path, + &virtual_path)) { + return false; + } + + GURL base_url = fileapi::GetFileSystemRootURI(origin_url, + fileapi::kFileSystemTypeExternal); + *url = GURL(base_url.spec() + virtual_path.value()); + return true; +} + +// static +bool FileManagerUtil::ConvertFileToRelativeFileSystemPath( + Profile* profile, const FilePath& full_file_path, FilePath* virtual_path) { fileapi::FileSystemPathManager* path_manager = profile->GetFileSystemContext()->path_manager(); fileapi::ExternalFileSystemMountPointProvider* provider = @@ -93,13 +114,9 @@ bool FileManagerUtil::ConvertFileToFileSystemUrl( return false; // Find if this file path is managed by the external provider. - FilePath virtual_path; - if (!provider->GetVirtualPath(full_file_path, &virtual_path)) + if (!provider->GetVirtualPath(full_file_path, virtual_path)) return false; - GURL base_url = fileapi::GetFileSystemRootURI(origin_url, - fileapi::kFileSystemTypeExternal); - *url = GURL(base_url.spec() + virtual_path.value()); return true; } diff --git a/chrome/browser/extensions/file_manager_util.h b/chrome/browser/extensions/file_manager_util.h index 205b893..877eba8 100644 --- a/chrome/browser/extensions/file_manager_util.h +++ b/chrome/browser/extensions/file_manager_util.h @@ -16,6 +16,7 @@ class Profile; class FileManagerUtil { public: // Gets base file browser url. + static GURL GetFileBrowserExtensionUrl(); static GURL GetFileBrowserUrl(); static GURL GetMediaPlayerUrl(); static GURL GetMediaPlayerPlaylistUrl(); @@ -24,6 +25,13 @@ class FileManagerUtil { // if |full_file_path| is not managed by the external filesystem provider. static bool ConvertFileToFileSystemUrl(Profile* profile, const FilePath& full_file_path, const GURL& origin_url, GURL* url); + + // Converts |full_file_path| into |relative_path| within the external provider + // in File API. Returns false if |full_file_path| is not managed by the + // external filesystem provider. + static bool ConvertFileToRelativeFileSystemPath(Profile* profile, + const FilePath& full_file_path, FilePath* relative_path); + // Gets base file browser url for. static GURL GetFileBrowserUrlWithParams( SelectFileDialog::Type type, diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 63e7bd5..4603d6b 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -515,9 +515,24 @@ void ProfileImpl::RegisterComponentExtensions() { IDR_BOOKMARKS_MANIFEST)); #if defined(FILE_MANAGER_EXTENSION) +#ifndef NDEBUG + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kFileManagerExtensionPath)) { + FilePath filemgr_extension_path = + command_line->GetSwitchValuePath(switches::kFileManagerExtensionPath); + component_extensions.push_back(std::make_pair( + filemgr_extension_path.value(), + IDR_FILEMANAGER_MANIFEST)); + } else { component_extensions.push_back(std::make_pair( FILE_PATH_LITERAL("file_manager"), IDR_FILEMANAGER_MANIFEST)); + } +#else + component_extensions.push_back(std::make_pair( + FILE_PATH_LITERAL("file_manager"), + IDR_FILEMANAGER_MANIFEST)); +#endif #endif #if defined(TOUCH_UI) @@ -532,7 +547,6 @@ void ProfileImpl::RegisterComponentExtensions() { IDR_MOBILE_MANIFEST)); #ifndef NDEBUG - const CommandLine* command_line = CommandLine::ForCurrentProcess(); if (command_line->HasSwitch(switches::kAuthExtensionPath)) { FilePath auth_extension_path = command_line->GetSwitchValuePath(switches::kAuthExtensionPath); diff --git a/chrome/browser/resources/component_extension_resources.grd b/chrome/browser/resources/component_extension_resources.grd index bdff237..54bd3c8 100644 --- a/chrome/browser/resources/component_extension_resources.grd +++ b/chrome/browser/resources/component_extension_resources.grd @@ -38,6 +38,8 @@ <include name="IDR_FILE_MANAGER_PREVIEW_ICON" file="file_manager/images/icon_preview_16x16.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_MEDIA_PLAY_ICON" file="file_manager/images/icon_play_16x16.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_MEDIA_ENQUEUE_ICON" file="file_manager/images/icon_add_to_queue_16x16.png" type="BINDATA" /> + <include name="IDR_FILE_MANAGER_ARCHIVE_MOUNT_ICON" file="file_manager/images/icon_mount_archive_16x16.png" type="BINDATA" /> + <include name="IDR_FILE_MANAGER_ARCHIVE_UNMOUNT_ICON" file="file_manager/images/icon_unmount_archive_16x16.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_IMG_FILETYPE_AUDIO" file="file_manager/images/filetype_audio.png" type="BINDATA" /> <include name="IDR_FILE_MANAGER_IMG_FILETYPE_DOC" file="file_manager/images/filetype_doc.png" type="BINDATA" /> diff --git a/chrome/browser/resources/file_manager/images/icon_mount_archive_16x16.png b/chrome/browser/resources/file_manager/images/icon_mount_archive_16x16.png Binary files differnew file mode 100644 index 0000000..c47e27f --- /dev/null +++ b/chrome/browser/resources/file_manager/images/icon_mount_archive_16x16.png diff --git a/chrome/browser/resources/file_manager/images/icon_unmount_archive_16x16.png b/chrome/browser/resources/file_manager/images/icon_unmount_archive_16x16.png Binary files differnew file mode 100644 index 0000000..51eddc5 --- /dev/null +++ b/chrome/browser/resources/file_manager/images/icon_unmount_archive_16x16.png diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js index 42814da..c079ffe 100644 --- a/chrome/browser/resources/file_manager/js/file_manager.js +++ b/chrome/browser/resources/file_manager/js/file_manager.js @@ -105,6 +105,18 @@ function FileManager(dialogDom, rootEntries, params) { this.addEventListener('selection-summarized', this.onSelectionSummarized_.bind(this)); + // The list of archives requested to mount. We will show contents once + // archive is mounted, but only for mounts from within this filebrowser tab. + this.mountRequests_ = []; + chrome.fileBrowserPrivate.onMountCompleted.addListener( + this.onMountCompleted_.bind(this)); + + var self = this; + // The list of active mount points to distinct them from other directories. + chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { + self.mountPoints_ = mountPoints; + }); + chrome.fileBrowserHandler.onExecute.addListener( this.onFileTaskExecute_.bind(this)); @@ -144,10 +156,16 @@ FileManager.prototype = { const RIGHT_TRIANGLE = '\u25b8'; /** - * The DirectoryEntry.fullPath value of the directory containing external - * storage volumes. + * The DirectoryEntry.fullPath value of the directory containing externally + * mounted removable storage volumes. */ - const MEDIA_DIRECTORY = '/media'; + const REMOVABLE_DIRECTORY = 'removable'; + + /** + * The DirectoryEntry.fullPath value of the directory containing externally + * mounted archive file volumes. + */ + const ARCHIVE_DIRECTORY = 'archive'; /** * Mnemonics for the second parameter of the changeDirectory method. @@ -640,8 +658,10 @@ FileManager.prototype = { !this.renameInput_.currentEntry && // Not in root directory. this.currentDirEntry_.fullPath != '/' && - // Not in media directory. - this.currentDirEntry_.fullPath != MEDIA_DIRECTORY && + // Not in media/archive directory. + this.currentDirEntry_.fullPath != ARCHIVE_DIRECTORY && + // Not in media/removable directory. + this.currentDirEntry_.fullPath != REMOVABLE_DIRECTORY && // Only one file selected. this.selection.totalCount == 1); }; @@ -657,8 +677,10 @@ FileManager.prototype = { !this.renameInput_.currentEntry && // Not in root directory. this.currentDirEntry_.fullPath != '/' && - // Not in media directory. - this.currentDirEntry_.fullPath != MEDIA_DIRECTORY); + // Not in media/removable directory. + this.currentDirEntry_.fullPath != REMOVABLE_DIRECTORY && + // Not in media/archive directory. + this.currentDirEntry_.fullPath != ARCHIVE_DIRECTORY); }; FileManager.prototype.setListType = function(type) { @@ -1078,8 +1100,11 @@ FileManager.prototype = { if (path == 'Downloads') return str('DOWNLOADS_DIRECTORY_LABEL'); - if (path == 'media') - return str('MEDIA_DIRECTORY_LABEL'); + if (path == 'archive') + return str('ARCHIVE_DIRECTORY_LABEL'); + + if (path == 'removable') + return str('REMOVABLE_DIRECTORY_LABEL'); return path || str('ROOT_DIRECTORY_LABEL'); }; @@ -1144,8 +1169,8 @@ FileManager.prototype = { var self = this; cacheEntryDate(entry, function(entry) { - if (self.currentDirEntry_.fullPath == MEDIA_DIRECTORY && - entry.cachedMtime_.getTime() == 0) { + if (self.currentDirEntry_.fullPath == ARCHIVE_DIRECTORY || + self.currentDirEntry_.fullPath == REMOVABLE_DIRECTORY) { // Mount points for FAT volumes have this time associated with them. // We'd rather display nothing than this bogus date. div.textContent = '---'; @@ -1248,8 +1273,12 @@ FileManager.prototype = { }; if (this.dialogType_ == FileManager.DialogType.FULL_PAGE) { - chrome.fileBrowserPrivate.getFileTasks(selection.urls, - this.onTasksFound_.bind(this)); + // Since unmount task cannot be defined in terms of file patterns, + // we manually include it here, if all selected items are mount points. + chrome.fileBrowserPrivate.getFileTasks( + selection.urls, + this.onTasksFound_.bind(this, + this.shouldShowUnmount_(selection.urls))); } cacheNextFile(); @@ -1302,7 +1331,20 @@ FileManager.prototype = { this[methodName].apply(this, data.arguments); }; - FileManager.prototype.onTasksFound_ = function(tasksList) { + /** + * Callback called when tasks for selected files are determined. + * @param {boolean} unmount Whether unmount task should be included. + * @param {Array.<Task>} tasksList The tasks list. + */ + FileManager.prototype.onTasksFound_ = function(unmount, tasksList) { + if (unmount) { + tasksList.push({ + taskId: this.getExtensionId_() + '|unmount-archive', + iconUrl: '', + title: '' + }); + } + this.taskButtons_.innerHTML = ''; for (var i = 0; i < tasksList.length; i++) { var task = tasksList[i]; @@ -1329,6 +1371,14 @@ FileManager.prototype = { task.iconUrl = chrome.extension.getURL('images/icon_add_to_queue_16x16.png'); task.title = str('ENQUEUE'); + } else if (task_parts[1] == 'mount-archive') { + task.iconUrl = + chrome.extension.getURL('images/icon_mount_archive_16x16.png'); + task.title = str('MOUNT_ARCHIVE'); + } else if (task_parts[1] == 'unmount-archive') { + task.iconUrl = + chrome.extension.getURL('images/icon_unmount_archive_16x16.png'); + task.title = str('UNMOUNT_ARCHIVE'); } } @@ -1356,6 +1406,41 @@ FileManager.prototype = { this.selection.urls); }; + /** + * Event handler called when some volume was mounted or unmouted. + */ + FileManager.prototype.onMountCompleted_ = function(event) { + var self = this; + chrome.fileBrowserPrivate.getMountPoints(function(mountPoints) { + self.mountPoints_ = mountPoints; + if (event.eventType == 'mount') { + for (var index = 0; index < self.mountRequests_.length; ++index) { + if (self.mountRequests_[index] == event.sourceUrl) { + self.mountRequests_.splice(index, 1); + if (event.status == 'success') { + self.changeDirectory(event.mountPath); + } else { + // Report mount error. + if (event.mountType == 'file') { + var fileName = event.sourceUrl.substr( + event.sourceUrl.lastIndexOf('/') + 1); + window.alert(strf('ARCHIVE_MOUNT_FAILED', fileName, + event.status)); + } + } + return; + } + } + } + // TODO(dgozman): rescan directory, only if it contains mounted points, + // when mounts location will be decided. + this.rescanDirectory_(); + }); + }; + + /** + * Event handler called when some internal task should be executed. + */ FileManager.prototype.onFileTaskExecute_ = function(id, details) { var urls = details.entries.map(function(entry) { return entry.toURL(); @@ -1372,7 +1457,31 @@ FileManager.prototype = { this.imageEditor_.getBuffer().load(urls[0]); } else if (id == 'play' || id == 'enqueue') { chrome.fileBrowserPrivate.viewFiles(urls, id); + } else if (id == 'mount-archive') { + for (var index = 0; index < urls.length; ++index) { + this.mountRequests_.push(urls[index]); + chrome.fileBrowserPrivate.addMount(urls[index], 'file', {}); + } + } else if (id == 'unmount-archive') { + for (var index = 0; index < urls.length; ++index) { + chrome.fileBrowserPrivate.removeMount(urls[index]); + } + } + }; + + /** + * Determines whether unmount task should present for selected files. + */ + FileManager.prototype.shouldShowUnmount_ = function(urls) { + for (var index = 0; index < urls.length; ++index) { + // Each url should be a mount point. + var path = urls[index]; + if (!this.mountPoints_.hasOwnProperty(path) || + this.mountPoints_[path].type != 'file') { + return false; + } } + return true; }; FileManager.prototype.onImageEditorSave = function(canvas) { @@ -1836,7 +1945,8 @@ FileManager.prototype = { this.selection.fileCount >= 1); } else if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { if (this.currentDirEntry_.fullPath == '/' || - this.currentDirEntry_.fullPath == MEDIA_DIRECTORY) { + this.currentDirEntry_.fullPath == ARCHIVE_DIRECTORY || + this.currentDirEntry_.fullPath == REMOVABLE_DIRECTORY) { // Nothing can be saved in to the root or media/ directories. selectable = false; } else { @@ -1894,7 +2004,8 @@ FileManager.prototype = { // New folder should never be enabled in the root or media/ directories. this.newFolderButton_.disabled = (this.currentDirEntry_.fullPath == '/' || - this.currentDirEntry_.fullPath == MEDIA_DIRECTORY); + this.currentDirEntry_.fullPath == ARCHIVE_DIRECTORY || + this.currentDirEntry_.fullPath == REMOVABLE_DIRECTORY); this.document_.title = this.currentDirEntry_.fullPath; diff --git a/chrome/browser/resources/file_manager/js/main.js b/chrome/browser/resources/file_manager/js/main.js index 7181285..1a278c9 100644 --- a/chrome/browser/resources/file_manager/js/main.js +++ b/chrome/browser/resources/file_manager/js/main.js @@ -15,7 +15,7 @@ var fileManager; function init() { var params; - var rootPaths = ['Downloads', 'media']; + var rootPaths = ['Downloads', 'removable', 'archive']; if (document.location.search) { var json = decodeURIComponent(document.location.search.substr(1)); diff --git a/chrome/browser/resources/file_manager/js/mock_chrome.js b/chrome/browser/resources/file_manager/js/mock_chrome.js index 82bc0c8..ded229c 100644 --- a/chrome/browser/resources/file_manager/js/mock_chrome.js +++ b/chrome/browser/resources/file_manager/js/mock_chrome.js @@ -58,6 +58,9 @@ chrome.fileBrowserPrivate = { addListener: function(cb) { this.callbacks.push(cb) } }, + /** + * Returns common tasks for a given list of files. + */ getFileTasks: function(urlList, callback) { if (urlList.length == 0) return callback([]); @@ -76,6 +79,11 @@ chrome.fileBrowserPrivate = { regexp: /\.(jpe?g|png|cr2?|tiff)$/i, iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sEEBcOAw9XftIAAADFSURBVCjPrZKxCsIwEIa/FHFwsvYxROjSQXAoqLiIL+xgBtvZ91A6uOnQc2hT0zRqkR4c3P25+/PfJTCwLU6wEpgBWkDXuInDPSwF5r7mJIeNQFTnIiCeONpVdYlLoK9wEUhNg8+B9FDVaZcgCKAovjTXfvPJFwGZtKW60pt8bOGBzfLouemnFY/MAs8wDeEI4NzaybewBu4AysKVgrK0gfe5iB9vjdAUqQ/S1Y/R3IX9Zc1zxc7zxe2/0Iskt7AsG0hhx14W8XV43FgV4gAAAABJRU5ErkJggg==', }, + { taskId: 'mount-archive', + title: 'Mount', + regexp: /\.(zip)$/i, + iconUrl: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAOCAYAAAAmL5yKAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sEEBcOAw9XftIAAADFSURBVCjPrZKxCsIwEIa/FHFwsvYxROjSQXAoqLiIL+xgBtvZ91A6uOnQc2hT0zRqkR4c3P25+/PfJTCwLU6wEpgBWkDXuInDPSwF5r7mJIeNQFTnIiCeONpVdYlLoK9wEUhNg8+B9FDVaZcgCKAovjTXfvPJFwGZtKW60pt8bOGBzfLouemnFY/MAs8wDeEI4NzaybewBu4AysKVgrK0gfe5iB9vjdAUqQ/S1Y/R3IX9Zc1zxc7zxe2/0Iskt7AsG0hhx14W8XV43FgV4gAAAABJRU5ErkJggg==', + }, ]; // Copy all tasks, then remove the ones that don't match. @@ -98,8 +106,72 @@ chrome.fileBrowserPrivate = { callback(candidateTasks); }, + /** + * Executes a task. + */ executeTask: function(taskId, urlList) { console.log('executing task: ' + taskId + ': ' + urlList.length + ' urls'); + var parts = taskId.split('|'); + taskId = parts[parts.length - 1]; + function createEntry(url) { + return { + toURL: function() { return url; } + }; + } + var details = {entries: urlList.map(createEntry)}; + var listeners = chrome.fileBrowserHandler.onExecute.listeners_; + for (var listener, index = 0; listener = listeners[index]; ++index) { + listener(taskId, details); + } + }, + + /** + * Event fired on mount and unmount operations. + */ + onDiskChanged: { + listeners_: [], + addListener: function(listener) { + chrome.fileBrowserPrivate.onDiskChanged.listeners_.push(listener); + } + }, + + addMount: function(source, type, options) { + var event = { + eventType: 'added', + volumeInfo: { + devicePath: source, + type: type, + mountPath: '/' + } + }; + var listeners = chrome.fileBrowserPrivate.onDiskChanged.listeners_; + for (var listener, index = 0; listener = listeners[index]; ++index) { + listener(event); + } + }, + + getMountPoints: function() { + // This will work in harness. + var path = 'filesystem:file:///persistent/media'; + var result = {}; + result[path] = {mountPath: path, type: 'file'}; + return result; + }, + + removeMount: function(mountPoint) { + console.log('unmounted: ' + mountPoint); + var event = { + eventType: 'removed', + volumeInfo: { + devicePath: '', + type: '', + mountPath: mountPoint + } + }; + var listeners = chrome.fileBrowserPrivate.onDiskChanged.listeners_; + for (var listener, index = 0; listener = listeners[index]; ++index) { + listener(event); + } }, /** @@ -143,6 +215,9 @@ chrome.fileBrowserPrivate = { VOLUME_LABEL: 'Volume Label', READ_ONLY: 'Read Only', + MOUNT_ARCHIVE: 'Open archive', + UNMOUNT_ARCHIVE: 'Close archive', + CONFIRM_OVERWRITE_FILE: 'A file named "$1" already exists. Do you want to replace it?', FILE_ALREADY_EXISTS: 'The file named "$1" already exists. Please choose a different name.', DIRECTORY_ALREADY_EXISTS: 'The directory named "$1" already exists. Please choose a different name.', @@ -203,3 +278,12 @@ chrome.test = { console.log('chrome.test.sendMessage: ' + msg); } }; + +chrome.fileBrowserHandler = { + onExecute: { + listeners_: [], + addListener: function(listener) { + chrome.fileBrowserHandler.onExecute.listeners_.push(listener); + } + } +}; diff --git a/chrome/browser/resources/file_manager/manifest.json b/chrome/browser/resources/file_manager/manifest.json index fcb79e5..572c1ee 100644 --- a/chrome/browser/resources/file_manager/manifest.json +++ b/chrome/browser/resources/file_manager/manifest.json @@ -85,6 +85,22 @@ "filesystem:*.mp3", "filesystem:*.m4a" ] + }, + { + "id": "mount-archive", + "default_title": "__MSG_MOUNT_ARCHIVE__", + "default_icon": "images/icon_mount_archive_16x16.png", + "file_filters": [ + "filesystem:*.zip" + ] + }, + { + "id": "unmount-archive", + "default_title": "__MSG_UNMOUNT_ARCHIVE__", + "default_icon": "images/icon_unmount_archive_16x16.png", + "file_filters": [ + "filesystem:*.mounted_zip" + ] } ], "chrome_url_overrides": { diff --git a/chrome/browser/ui/webui/chromeos/imageburner/webui_handler.h b/chrome/browser/ui/webui/chromeos/imageburner/webui_handler.h index c75bb2b..dee9f72 100644 --- a/chrome/browser/ui/webui/chromeos/imageburner/webui_handler.h +++ b/chrome/browser/ui/webui/chromeos/imageburner/webui_handler.h @@ -95,6 +95,10 @@ class WebUIHandler virtual void DeviceChanged(chromeos::MountLibraryEventType event, const std::string& device_path) OVERRIDE { } + virtual void MountCompleted(chromeos::MountLibrary::MountEvent event_type, + chromeos::MountError error_code, + const chromeos::MountLibrary::MountPointInfo& mount_info) OVERRIDE { + } // chromeos::BurnLibrary::Observer interface. virtual void BurnProgressUpdated(chromeos::BurnLibrary* object, diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 1011697..6ca4187 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -1118,6 +1118,9 @@ const char kOobeSkipPostLogin[] = "oobe-skip-postlogin"; // Enables overriding the path for the default authentication extension. const char kAuthExtensionPath[] = "auth-ext-path"; + +// Enables overriding the path off file manager extension. +const char kFileManagerExtensionPath[] = "filemgr-ext-path"; #endif #endif diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index 3fe3cfd..5b0b95a 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -309,6 +309,7 @@ extern const char kCompressSystemFeedback[]; #ifndef NDEBUG extern const char kOobeSkipPostLogin[]; extern const char kAuthExtensionPath[]; +extern const char kFileManagerExtensionPath[]; #endif #endif diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index d341192..c037325 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -5176,6 +5176,27 @@ } }, { + "id": "MountPointInfo", + "type": "object", + "description": "Mounted point information.", + "properties": { + "mountPath": { + "type": "string", + "optional": true, + "description": "Disk volume mount point path. The value corresponds to its Entry.fullPath in File API." + }, + "sourceUrl": { + "type": "string", + "description": "The path to the mounted devicem, url of the mounted archive file or network resource." + }, + "mountType": { + "type": "string", + "enum": ["device", "file", "network"], + "description": "Type of the mount." + } + } + }, + { "id": "VolumeMetadata", "type": "object", "description": "Mounted disk volume metadata.", @@ -5248,6 +5269,39 @@ } }, { + "id": "MountCompletedEvent", + "type": "object", + "description": "Payload data for mount event.", + "properties": { + "eventType": { + "type": "string", + "enum": ["mount", "unmount"], + "description": "Is the event raised for mounting or unmounting." + }, + "status": { + "type": "string", + "enum": ["success", "error_unknown", "error_internal", + "error_unknown_filesystem", "error_unsuported_filesystem", + "error_invalid_archive", "error_libcros_missing"], + "description": "Event type that tells listeners if mount was successfull or an error occurred. It also specifies the error." + }, + "sourceUrl": { + "type": "string", + "description": "Path that has been mounted." + }, + "mountPath": { + "type": "string", + "optional": true, + "description": "Path that sourcePath was mounted to." + }, + "mountType": { + "type": "string", + "enum": ["device", "file", "network"], + "description": "Type of the mount." + } + } + }, + { "id": "FileWatchEvent", "type": "object", "description": "Payload data for disk mount / unmount event.", @@ -5481,13 +5535,55 @@ ] }, { - "name": "unmountVolume", - "description": "Unmounts specified volume", + "name": "addMount", + "description": "Mount a resource or a file.", "parameters": [ { - "name": "devicePath", + "name": "source", "type": "string", - "description": "Device path of the volume." + "description": "Mount point source. For compressed files it is relative file path within external file system" + }, + { + "name": "mountType", + "type": "string", + "enum": ["device", "file", "network"], + "description": "Mount point type. 'file' for compressed files" + }, + { + "name": "options", + "type": "object", + "description": "Name/value pairs for source specific options", + "optional": "true" + } + ] + }, + { + "name": "removeMount", + "description": "Unmounts a mounted resource.", + "parameters": [ + { + "name": "mountPath", + "type": "string", + "description": "A path of the mount." + } + ] + }, + { + "name": "getMountPoints", + "description": "Get the list of mount points.", + "parameters": [ + { + "name": "callback", + "type": "function", + "optional": "false", + "parameters": [ + { + "name" : "mountPoints", + "type": "array", + "items": {"$ref": "MountPointInfo"}, + "description": "The list of MountPointInfo representing mounted devices." + } + ] } ] } @@ -5506,6 +5602,18 @@ ] }, { + "name": "onMountCompleted", + "type": "function", + "description": "Fired when mount event is detected.", + "parameters": [ + { + "$ref": "MountCompletedEvent", + "name": "event", + "description": "MountCompleted event information." + } + ] + }, + { "name": "onFileChanged", "type": "function", "description": "Fired when watched file change event is detected.", @@ -6385,7 +6493,7 @@ "title": { "type": "string", "optional": true, - "description": "The title of the notification." + "description": "The title of the notification." }, "bodyText": { "type": "string", diff --git a/chrome/renderer/resources/extension_process_bindings.js b/chrome/renderer/resources/extension_process_bindings.js index 741d2fd..98e31cb 100644 --- a/chrome/renderer/resources/extension_process_bindings.js +++ b/chrome/renderer/resources/extension_process_bindings.js @@ -77,6 +77,8 @@ var chrome = chrome || {}; throw new Error(message); } else if (!schemas[i].optional) { + console.log(chromeHidden.JSON.stringify(args)); + console.log(chromeHidden.JSON.stringify(schemas)); throw new Error("Parameter " + (i + 1) + " is required."); } } diff --git a/chrome/test/data/extensions/api_test/webstore_private/common.js b/chrome/test/data/extensions/api_test/webstore_private/common.js index 8972e67..933b032 100644 --- a/chrome/test/data/extensions/api_test/webstore_private/common.js +++ b/chrome/test/data/extensions/api_test/webstore_private/common.js @@ -24,6 +24,7 @@ function checkInstalled(callback) { } var cachedIcon = null; +var img = null; // This returns the base64-encoded content of the extension's image. function getIconData(callback) { @@ -34,8 +35,9 @@ function getIconData(callback) { canvas.style.display = "none"; canvas.width = 128; canvas.height = 128; - var img = new Image(); + img = new Image(); img.onload = function() { + console.log('img.onload called'); var ctx = canvas.getContext("2d"); ctx.drawImage(img, 0, 0); var tmp = canvas.toDataURL(); diff --git a/webkit/chromeos/fileapi/cros_mount_point_provider.cc b/webkit/chromeos/fileapi/cros_mount_point_provider.cc index 38a68d0..63c9829 100644 --- a/webkit/chromeos/fileapi/cros_mount_point_provider.cc +++ b/webkit/chromeos/fileapi/cros_mount_point_provider.cc @@ -34,7 +34,8 @@ const char kChromeUIScheme[] = "chrome"; // Top level file system elements exposed in FileAPI in ChromeOS: FixedExposedPaths fixed_exposed_paths[] = { {"/home/chronos/user/", "Downloads"}, - {"/", "media"}, + {"/media", "archive"}, + {"/media", "removable"}, }; CrosMountPointProvider::CrosMountPointProvider( |