summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-09 22:25:31 +0000
committermtomasz@chromium.org <mtomasz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-09 22:25:31 +0000
commit582f60ba6bb5ef030f9de06538266fda90cdf12c (patch)
treec6b134f70d2ea3771b7611e1f3e67e344e11c0de
parentefb2a4e8a6d5c546cc38eaf907e24bd3cee54e82 (diff)
downloadchromium_src-582f60ba6bb5ef030f9de06538266fda90cdf12c.zip
chromium_src-582f60ba6bb5ef030f9de06538266fda90cdf12c.tar.gz
chromium_src-582f60ba6bb5ef030f9de06538266fda90cdf12c.tar.bz2
[fsp] Add requestUnmount() method together with the request manager.
This patch introduces a requestUnmount() method, which is invoked by Files app's private api, and then emits an onUnmountRequested event. When the providing extension receives it, it should handle unmounting, and call the success or failure callback. Unmounting is the first implemented request in the file system provider implementation. Other requests will be: listing directories, fetching files, etc. These requests are invoked by Files app and are routed to the providing extension, then the response goes back to Files app. To make the code clean, a RequestManager class has been introduced. It acts as a glue between requests as responses. TEST=unit_tests: *FileSystemProviderServiceTest.Unmount*, *FileSystemProviderRequestManagerTest*; browser_tests: *FileSystemProviderApiTest.Unmount* BUG=248427 Review URL: https://codereview.chromium.org/194693002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@262840 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc31
-rw-r--r--chrome/browser/chromeos/extensions/file_manager/private_api_mount.h4
-rw-r--r--chrome/browser/chromeos/extensions/file_manager/private_api_util.cc1
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc146
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h33
-rw-r--r--chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc6
-rw-r--r--chrome/browser/chromeos/file_manager/volume_manager.cc39
-rw-r--r--chrome/browser/chromeos/file_manager/volume_manager.h17
-rw-r--r--chrome/browser/chromeos/file_system_provider/observer.h21
-rw-r--r--chrome/browser/chromeos/file_system_provider/request_manager.cc110
-rw-r--r--chrome/browser/chromeos/file_system_provider/request_manager.h88
-rw-r--r--chrome/browser/chromeos/file_system_provider/request_manager_unittest.cc348
-rw-r--r--chrome/browser/chromeos/file_system_provider/service.cc142
-rw-r--r--chrome/browser/chromeos/file_system_provider/service.h53
-rw-r--r--chrome/browser/chromeos/file_system_provider/service_unittest.cc138
-rw-r--r--chrome/chrome_browser_chromeos.gypi2
-rw-r--r--chrome/chrome_tests_unit.gypi1
-rw-r--r--chrome/common/extensions/api/_api_features.json5
-rw-r--r--chrome/common/extensions/api/api.gyp1
-rw-r--r--chrome/common/extensions/api/file_system_provider.idl59
-rw-r--r--chrome/common/extensions/api/file_system_provider_internal.idl26
-rw-r--r--chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js51
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/mount/test.js12
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/unmount/manifest.json13
-rw-r--r--chrome/test/data/extensions/api_test/file_system_provider/unmount/test.js123
-rw-r--r--extensions/browser/extension_function_histogram_value.h3
-rw-r--r--tools/metrics/histograms/histograms.xml9
27 files changed, 1362 insertions, 120 deletions
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
index 4d2ae46..16fa5ac 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.cc
@@ -4,6 +4,8 @@
#include "chrome/browser/chromeos/extensions/file_manager/private_api_mount.h"
+#include <string>
+
#include "base/format_macros.h"
#include "chrome/browser/chromeos/drive/file_system_interface.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
@@ -105,8 +107,7 @@ bool FileBrowserPrivateRemoveMountFunction::RunImpl() {
using file_manager::VolumeManager;
using file_manager::VolumeInfo;
VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
- if (!volume_manager)
- return false;
+ DCHECK(volume_manager);
VolumeInfo volume_info;
if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
@@ -115,10 +116,28 @@ bool FileBrowserPrivateRemoveMountFunction::RunImpl() {
// TODO(tbarzic): Send response when callback is received, it would make more
// sense than remembering issued unmount requests in file manager and showing
// errors for them when MountCompleted event is received.
- DiskMountManager::GetInstance()->UnmountPath(
- volume_info.mount_path.value(),
- chromeos::UNMOUNT_OPTIONS_NONE,
- DiskMountManager::UnmountPathCallback());
+ switch (volume_info.type) {
+ case file_manager::VOLUME_TYPE_REMOVABLE_DISK_PARTITION:
+ case file_manager::VOLUME_TYPE_MOUNTED_ARCHIVE_FILE: {
+ DiskMountManager::GetInstance()->UnmountPath(
+ volume_info.mount_path.value(),
+ chromeos::UNMOUNT_OPTIONS_NONE,
+ DiskMountManager::UnmountPathCallback());
+ break;
+ }
+ case file_manager::VOLUME_TYPE_PROVIDED: {
+ chromeos::file_system_provider::Service* service =
+ chromeos::file_system_provider::Service::Get(GetProfile());
+ DCHECK(service);
+ // TODO(mtomasz): Pass a more detailed error than just a bool.
+ if (!service->RequestUnmount(volume_info.file_system_id))
+ return false;
+ break;
+ }
+ default:
+ // Requested unmounting a device which is not unmountable.
+ return false;
+ }
SendResponse(true);
return true;
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
index 2109734..b66f8e0 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_mount.h
@@ -20,7 +20,7 @@ struct SelectedFileInfo;
namespace extensions {
// Implements chrome.fileBrowserPrivate.addMount method.
-// Mounts a device or a file.
+// Mounts removable devices and archive files.
class FileBrowserPrivateAddMountFunction : public LoggedAsyncExtensionFunction {
public:
DECLARE_EXTENSION_FUNCTION("fileBrowserPrivate.addMount",
@@ -41,7 +41,7 @@ class FileBrowserPrivateAddMountFunction : public LoggedAsyncExtensionFunction {
};
// Implements chrome.fileBrowserPrivate.removeMount method.
-// Unmounts selected device. Expects mount point path as an argument.
+// Unmounts selected volume. Expects volume id as an argument.
class FileBrowserPrivateRemoveMountFunction
: public LoggedAsyncExtensionFunction {
public:
diff --git a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
index f463f4b..4f935e2 100644
--- a/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
+++ b/chrome/browser/chromeos/extensions/file_manager/private_api_util.cc
@@ -125,7 +125,6 @@ void VolumeInfoToVolumeMetadata(
const VolumeInfo& volume_info,
file_browser_private::VolumeMetadata* volume_metadata) {
DCHECK(volume_metadata);
- DCHECK(!volume_info.mount_path.empty());
volume_metadata->volume_id = volume_info.volume_id;
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
index e891b1c..29b08b2 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.cc
@@ -4,8 +4,12 @@
#include "chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h"
+#include <string>
+
+#include "base/values.h"
#include "chrome/browser/chromeos/file_system_provider/service.h"
#include "chrome/common/extensions/api/file_system_provider.h"
+#include "chrome/common/extensions/api/file_system_provider_internal.h"
namespace extensions {
namespace {
@@ -16,8 +20,10 @@ const char kSecurityErrorName[] = "SecurityError";
// Error messages.
const char kEmptyNameErrorMessage[] = "Empty display name is not allowed.";
-const char kRegisteringFailedErrorMessage[] =
- "Registering the file system failed.";
+const char kMountFailedErrorMessage[] = "Mounting the file system failed.";
+const char kUnmountFailedErrorMessage[] = "Unmounting the file system failed.";
+const char kResponseFailedErrorMessage[] =
+ "Sending a response for the request failed.";
// Creates a dictionary, which looks like a DOMError. The returned dictionary
// will be converted to a real DOMError object in
@@ -30,10 +36,53 @@ base::DictionaryValue* CreateError(const std::string& name,
return error;
}
+// Converts ProviderError to base::File::Error. This could be redundant, if it
+// was possible to create DOMError instances in Javascript easily.
+base::File::Error ProviderErrorToFileError(
+ api::file_system_provider::ProviderError error) {
+ switch (error) {
+ case api::file_system_provider::PROVIDER_ERROR_OK:
+ return base::File::FILE_OK;
+ case api::file_system_provider::PROVIDER_ERROR_IN_USE:
+ return base::File::FILE_ERROR_IN_USE;
+ case api::file_system_provider::PROVIDER_ERROR_EXISTS:
+ return base::File::FILE_ERROR_EXISTS;
+ case api::file_system_provider::PROVIDER_ERROR_NOT_FOUND:
+ return base::File::FILE_ERROR_NOT_FOUND;
+ case api::file_system_provider::PROVIDER_ERROR_ACCESS_DENIED:
+ return base::File::FILE_ERROR_ACCESS_DENIED;
+ case api::file_system_provider::PROVIDER_ERROR_TOO_MANY_OPENED:
+ return base::File::FILE_ERROR_TOO_MANY_OPENED;
+ case api::file_system_provider::PROVIDER_ERROR_NO_MEMORY:
+ return base::File::FILE_ERROR_NO_MEMORY;
+ case api::file_system_provider::PROVIDER_ERROR_NO_SPACE:
+ return base::File::FILE_ERROR_NO_SPACE;
+ case api::file_system_provider::PROVIDER_ERROR_NOT_A_DIRECTORY:
+ return base::File::FILE_ERROR_NOT_A_DIRECTORY;
+ case api::file_system_provider::PROVIDER_ERROR_INVALID_OPERATION:
+ return base::File::FILE_ERROR_INVALID_OPERATION;
+ case api::file_system_provider::PROVIDER_ERROR_SECURITY:
+ return base::File::FILE_ERROR_SECURITY;
+ case api::file_system_provider::PROVIDER_ERROR_ABORT:
+ return base::File::FILE_ERROR_ABORT;
+ case api::file_system_provider::PROVIDER_ERROR_NOT_A_FILE:
+ return base::File::FILE_ERROR_NOT_A_FILE;
+ case api::file_system_provider::PROVIDER_ERROR_NOT_EMPTY:
+ return base::File::FILE_ERROR_NOT_EMPTY;
+ case api::file_system_provider::PROVIDER_ERROR_INVALID_URL:
+ return base::File::FILE_ERROR_INVALID_URL;
+ case api::file_system_provider::PROVIDER_ERROR_IO:
+ return base::File::FILE_ERROR_IO;
+ default:
+ NOTREACHED();
+ }
+ return base::File::FILE_ERROR_FAILED;
+}
+
} // namespace
bool FileSystemProviderMountFunction::RunImpl() {
- using extensions::api::file_system_provider::Mount::Params;
+ using api::file_system_provider::Mount::Params;
const scoped_ptr<Params> params(Params::Create(*args_));
EXTENSION_FUNCTION_VALIDATE(params);
@@ -52,7 +101,7 @@ bool FileSystemProviderMountFunction::RunImpl() {
DCHECK(service);
int file_system_id =
- service->RegisterFileSystem(extension_id(), params->display_name);
+ service->MountFileSystem(extension_id(), params->display_name);
// If the |file_system_id| is zero, then it means that registering the file
// system failed.
@@ -60,8 +109,7 @@ bool FileSystemProviderMountFunction::RunImpl() {
if (!file_system_id) {
base::ListValue* result = new base::ListValue();
result->Append(new base::FundamentalValue(0));
- result->Append(
- CreateError(kSecurityErrorName, kRegisteringFailedErrorMessage));
+ result->Append(CreateError(kSecurityErrorName, kMountFailedErrorMessage));
SetResult(result);
return false;
}
@@ -75,4 +123,90 @@ bool FileSystemProviderMountFunction::RunImpl() {
return true;
}
+bool FileSystemProviderUnmountFunction::RunImpl() {
+ using api::file_system_provider::Unmount::Params;
+ const scoped_ptr<Params> params(Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ chromeos::file_system_provider::Service* service =
+ chromeos::file_system_provider::Service::Get(GetProfile());
+ DCHECK(service);
+
+ if (!service->UnmountFileSystem(extension_id(), params->file_system_id)) {
+ // TODO(mtomasz): Pass more detailed errors, rather than just a bool.
+ base::ListValue* result = new base::ListValue();
+ result->Append(CreateError(kSecurityErrorName, kUnmountFailedErrorMessage));
+ SetResult(result);
+ return false;
+ }
+
+ base::ListValue* result = new base::ListValue();
+ SetResult(result);
+ SendResponse(true);
+ return true;
+}
+
+bool FileSystemProviderInternalUnmountRequestedSuccessFunction::RunImpl() {
+ using api::file_system_provider_internal::UnmountRequestedSuccess::Params;
+ const scoped_ptr<Params> params(Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ chromeos::file_system_provider::Service* service =
+ chromeos::file_system_provider::Service::Get(GetProfile());
+ DCHECK(service);
+
+ if (!service->FulfillRequest(extension_id(),
+ params->file_system_id,
+ params->request_id,
+ scoped_ptr<base::DictionaryValue>(),
+ false /* has_more */)) {
+ // TODO(mtomasz): Pass more detailed errors, rather than just a bool.
+ base::ListValue* result = new base::ListValue();
+ result->Append(
+ CreateError(kSecurityErrorName, kResponseFailedErrorMessage));
+ SetResult(result);
+ return false;
+ }
+
+ base::ListValue* result = new base::ListValue();
+ SetResult(result);
+ SendResponse(true);
+ return true;
+}
+
+bool FileSystemProviderInternalUnmountRequestedErrorFunction::RunImpl() {
+ using api::file_system_provider_internal::UnmountRequestedError::Params;
+ const scoped_ptr<Params> params(Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params);
+
+ chromeos::file_system_provider::Service* service =
+ chromeos::file_system_provider::Service::Get(GetProfile());
+ DCHECK(service);
+
+ // Currently it is not possible to refer to types/enums defined in a different
+ // IDL file. Therefore we need to convert DOMString to ProviderError, since
+ // UnmountRequestedErrorFunction() is defined in a different namespace than
+ // ProvidedError.
+ // TODO(mtomasz): Remove this trick, once IDL supports namespaces correctly.
+ const api::file_system_provider::ProviderError provider_error =
+ api::file_system_provider::ParseProviderError(params->error);
+
+ if (!service->RejectRequest(extension_id(),
+ params->file_system_id,
+ params->request_id,
+ ProviderErrorToFileError(provider_error))) {
+ // TODO(mtomasz): Pass more detailed errors, rather than just a bool.
+ base::ListValue* result = new base::ListValue();
+ result->Append(
+ CreateError(kSecurityErrorName, kResponseFailedErrorMessage));
+ SetResult(result);
+ return false;
+ }
+
+ base::ListValue* result = new base::ListValue();
+ SetResult(result);
+ SendResponse(true);
+ return true;
+}
+
} // namespace extensions
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h
index ae14ca2..48b32bd 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h
@@ -20,6 +20,39 @@ class FileSystemProviderMountFunction : public ChromeAsyncExtensionFunction {
virtual bool RunImpl() OVERRIDE;
};
+class FileSystemProviderUnmountFunction : public ChromeAsyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("fileSystemProvider.unmount",
+ FILESYSTEMPROVIDER_UNMOUNT)
+
+ protected:
+ virtual ~FileSystemProviderUnmountFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+};
+
+class FileSystemProviderInternalUnmountRequestedSuccessFunction
+ : public ChromeAsyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION(
+ "fileSystemProviderInternal.unmountRequestedSuccess",
+ FILESYSTEMPROVIDERINTERNAL_UNMOUNTREQUESTEDSUCCESS)
+
+ protected:
+ virtual ~FileSystemProviderInternalUnmountRequestedSuccessFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+};
+
+class FileSystemProviderInternalUnmountRequestedErrorFunction
+ : public ChromeAsyncExtensionFunction {
+ public:
+ DECLARE_EXTENSION_FUNCTION("fileSystemProviderInternal.unmountRequestedError",
+ FILESYSTEMPROVIDERINTERNAL_UNMOUNTREQUESTEDERROR)
+
+ protected:
+ virtual ~FileSystemProviderInternalUnmountRequestedErrorFunction() {}
+ virtual bool RunImpl() OVERRIDE;
+};
+
} // namespace extensions
#endif // CHROME_BROWSER_CHROMEOS_EXTENSIONS_FILE_SYSTEM_PROVIDER_FILE_SYSTEM_PROVIDER_API_H_
diff --git a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
index 78eeb35..c1317e0 100644
--- a/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
+++ b/chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_apitest.cc
@@ -23,4 +23,10 @@ IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, Mount) {
<< message_;
}
+IN_PROC_BROWSER_TEST_F(FileSystemProviderApiTest, Unmount) {
+ ASSERT_TRUE(RunPlatformAppTestWithFlags("file_system_provider/unmount",
+ kFlagLoadAsComponent))
+ << message_;
+}
+
} // namespace extensions
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.cc b/chrome/browser/chromeos/file_manager/volume_manager.cc
index 4750b7b..a8a4f33 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.cc
+++ b/chrome/browser/chromeos/file_manager/volume_manager.cc
@@ -207,13 +207,18 @@ VolumeInfo CreateProvidedFileSystemVolumeInfo(
volume_info.is_parent = true;
volume_info.is_read_only = true;
volume_info.volume_id = GenerateVolumeId(volume_info);
+ volume_info.file_system_id = file_system.file_system_id();
return volume_info;
}
} // namespace
-VolumeInfo::VolumeInfo() {
-}
+VolumeInfo::VolumeInfo()
+ : file_system_id(0),
+ type(VOLUME_TYPE_GOOGLE_DRIVE),
+ mount_condition(chromeos::disks::MOUNT_CONDITION_NONE),
+ is_parent(false),
+ is_read_only(false) {}
VolumeInfo::~VolumeInfo() {
}
@@ -292,7 +297,7 @@ void VolumeManager::Initialize() {
file_system_provider_service_->AddObserver(this);
std::vector<ProvidedFileSystem> provided_file_systems =
- file_system_provider_service_->GetRegisteredFileSystems();
+ file_system_provider_service_->GetMountedFileSystems();
for (size_t i = 0; i < provided_file_systems.size(); ++i) {
VolumeInfo volume_info =
CreateProvidedFileSystemVolumeInfo(provided_file_systems[i]);
@@ -615,16 +620,28 @@ void VolumeManager::OnFormatEvent(
NOTREACHED();
}
-void VolumeManager::OnProvidedFileSystemRegistered(
- const chromeos::file_system_provider::ProvidedFileSystem& file_system) {
+void VolumeManager::OnProvidedFileSystemMount(
+ const chromeos::file_system_provider::ProvidedFileSystem& file_system,
+ base::File::Error error) {
VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system);
- DoMountEvent(chromeos::MOUNT_ERROR_NONE, volume_info, false /* remounting */);
-}
-
-void VolumeManager::OnProvidedFileSystemUnregistered(
- const chromeos::file_system_provider::ProvidedFileSystem& file_system) {
+ // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
+ // since it is related to cros disks only.
+ const chromeos::MountError mount_error = error == base::File::FILE_OK
+ ? chromeos::MOUNT_ERROR_NONE
+ : chromeos::MOUNT_ERROR_UNKNOWN;
+ DoMountEvent(mount_error, volume_info, false /* remounting */);
+}
+
+void VolumeManager::OnProvidedFileSystemUnmount(
+ const chromeos::file_system_provider::ProvidedFileSystem& file_system,
+ base::File::Error error) {
+ // TODO(mtomasz): Introduce own type, and avoid using MountError internally,
+ // since it is related to cros disks only.
+ const chromeos::MountError mount_error = error == base::File::FILE_OK
+ ? chromeos::MOUNT_ERROR_NONE
+ : chromeos::MOUNT_ERROR_UNKNOWN;
VolumeInfo volume_info = CreateProvidedFileSystemVolumeInfo(file_system);
- DoUnmountEvent(chromeos::MOUNT_ERROR_NONE, volume_info);
+ DoUnmountEvent(mount_error, volume_info);
}
void VolumeManager::OnExternalStorageDisabledChanged() {
diff --git a/chrome/browser/chromeos/file_manager/volume_manager.h b/chrome/browser/chromeos/file_manager/volume_manager.h
index 61b0e8a..bd3b11e 100644
--- a/chrome/browser/chromeos/file_manager/volume_manager.h
+++ b/chrome/browser/chromeos/file_manager/volume_manager.h
@@ -5,10 +5,12 @@
#ifndef CHROME_BROWSER_CHROMEOS_FILE_MANAGER_VOLUME_MANAGER_H_
#define CHROME_BROWSER_CHROMEOS_FILE_MANAGER_VOLUME_MANAGER_H_
+#include <map>
#include <string>
#include <vector>
#include "base/basictypes.h"
+#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
@@ -57,6 +59,9 @@ struct VolumeInfo {
VolumeInfo();
~VolumeInfo();
+ // The ID for provided file system. If other type, then equal to zero.
+ int file_system_id;
+
// The ID of the volume.
std::string volume_id;
@@ -170,12 +175,12 @@ class VolumeManager : public KeyedService,
const std::string& device_path) OVERRIDE;
// chromeos::file_system_provider::Observer overrides.
- virtual void OnProvidedFileSystemRegistered(
- const chromeos::file_system_provider::ProvidedFileSystem& file_system)
- OVERRIDE;
- virtual void OnProvidedFileSystemUnregistered(
- const chromeos::file_system_provider::ProvidedFileSystem& file_system)
- OVERRIDE;
+ virtual void OnProvidedFileSystemMount(
+ const chromeos::file_system_provider::ProvidedFileSystem& file_system,
+ base::File::Error error) OVERRIDE;
+ virtual void OnProvidedFileSystemUnmount(
+ const chromeos::file_system_provider::ProvidedFileSystem& file_system,
+ base::File::Error error) OVERRIDE;
// Called on change to kExternalStorageDisabled pref.
void OnExternalStorageDisabledChanged();
diff --git a/chrome/browser/chromeos/file_system_provider/observer.h b/chrome/browser/chromeos/file_system_provider/observer.h
index d2147e6..be4dc66 100644
--- a/chrome/browser/chromeos/file_system_provider/observer.h
+++ b/chrome/browser/chromeos/file_system_provider/observer.h
@@ -7,6 +7,8 @@
#include <string>
+#include "base/files/file.h"
+
namespace chromeos {
namespace file_system_provider {
@@ -15,13 +17,18 @@ class ProvidedFileSystem;
// Observes file_system_provider::Service for mounting and unmounting events.
class Observer {
public:
- // Called when a provided |file_system| is registered successfully.
- virtual void OnProvidedFileSystemRegistered(
- const ProvidedFileSystem& file_system) = 0;
-
- // Called when a provided |file_system| is unregistered successfully.
- virtual void OnProvidedFileSystemUnregistered(
- const ProvidedFileSystem& file_system) = 0;
+ // Called when a file system mounting has been invoked. For success, the
+ // |error| argument is set to FILE_OK. Otherwise, |error| contains a specific
+ // error code.
+ virtual void OnProvidedFileSystemMount(const ProvidedFileSystem& file_system,
+ base::File::Error error) = 0;
+
+ // Called when a file system unmounting has been invoked. For success, the
+ // |error| argument is set to FILE_OK. Otherwise, |error| contains a specific
+ // error code.
+ virtual void OnProvidedFileSystemUnmount(
+ const ProvidedFileSystem& file_system,
+ base::File::Error error) = 0;
};
} // namespace file_system_provider
diff --git a/chrome/browser/chromeos/file_system_provider/request_manager.cc b/chrome/browser/chromeos/file_system_provider/request_manager.cc
new file mode 100644
index 0000000..4083f57
--- /dev/null
+++ b/chrome/browser/chromeos/file_system_provider/request_manager.cc
@@ -0,0 +1,110 @@
+// Copyright 2014 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/file_system_provider/request_manager.h"
+
+#include "base/values.h"
+
+namespace chromeos {
+namespace file_system_provider {
+
+RequestManager::RequestManager() : next_id_(1) {}
+
+RequestManager::~RequestManager() {}
+
+int RequestManager::CreateRequest(const ProvidedFileSystem& file_system,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback) {
+ // The request id is unique per request manager, so per service, thereof
+ // per profile.
+ int request_id = next_id_++;
+
+ // If cycled the int, then signal an error.
+ if (requests_.find(request_id) != requests_.end())
+ return 0;
+
+ Request request;
+ request.file_system = file_system;
+ request.success_callback = success_callback;
+ request.error_callback = error_callback;
+ requests_[request_id] = request;
+
+ return request_id;
+}
+
+bool RequestManager::FulfillRequest(const ProvidedFileSystem& file_system,
+ int request_id,
+ scoped_ptr<base::DictionaryValue> response,
+ bool has_next) {
+ RequestMap::iterator request_it = requests_.find(request_id);
+
+ if (request_it == requests_.end())
+ return false;
+
+ // Check if the request belongs to the same provided file system.
+ if (request_it->second.file_system.file_system_id() !=
+ file_system.file_system_id()) {
+ return false;
+ }
+
+ if (!request_it->second.success_callback.is_null())
+ request_it->second.success_callback.Run(response.Pass(), has_next);
+ if (!has_next)
+ requests_.erase(request_it);
+
+ return true;
+}
+
+bool RequestManager::RejectRequest(const ProvidedFileSystem& file_system,
+ int request_id,
+ base::File::Error error) {
+ RequestMap::iterator request_it = requests_.find(request_id);
+
+ if (request_it == requests_.end())
+ return false;
+
+ // Check if the request belongs to the same provided file system.
+ if (request_it->second.file_system.file_system_id() !=
+ file_system.file_system_id()) {
+ return false;
+ }
+
+ if (!request_it->second.error_callback.is_null())
+ request_it->second.error_callback.Run(error);
+ requests_.erase(request_it);
+
+ return true;
+}
+
+void RequestManager::OnProvidedFileSystemMount(
+ const ProvidedFileSystem& file_system,
+ base::File::Error error) {}
+
+void RequestManager::OnProvidedFileSystemUnmount(
+ const ProvidedFileSystem& file_system,
+ base::File::Error error) {
+ // Do not continue on error, since the volume may be still mounted.
+ if (error != base::File::FILE_OK)
+ return;
+
+ // Remove all requests for this provided file system.
+ RequestMap::iterator it = requests_.begin();
+ while (it != requests_.begin()) {
+ if (it->second.file_system.file_system_id() ==
+ file_system.file_system_id()) {
+ RejectRequest(
+ it->second.file_system, it->first, base::File::FILE_ERROR_ABORT);
+ requests_.erase(it++);
+ } else {
+ it++;
+ }
+ }
+}
+
+RequestManager::Request::Request() {}
+
+RequestManager::Request::~Request() {}
+
+} // namespace file_system_provider
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/file_system_provider/request_manager.h b/chrome/browser/chromeos/file_system_provider/request_manager.h
new file mode 100644
index 0000000..6eb2f45
--- /dev/null
+++ b/chrome/browser/chromeos/file_system_provider/request_manager.h
@@ -0,0 +1,88 @@
+// Copyright 2014 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.
+
+#ifndef CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_REQUEST_MANAGER_H_
+#define CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_REQUEST_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include "base/callback.h"
+#include "base/files/file.h"
+#include "base/memory/scoped_ptr.h"
+#include "chrome/browser/chromeos/file_system_provider/observer.h"
+#include "chrome/browser/chromeos/file_system_provider/provided_file_system.h"
+
+namespace base {
+class DictionaryValue;
+} // namespace base
+
+namespace chromeos {
+namespace file_system_provider {
+
+typedef base::Callback<void(scoped_ptr<base::DictionaryValue>, bool has_next)>
+ SuccessCallback;
+typedef base::Callback<void(base::File::Error)> ErrorCallback;
+
+// Manages requests between the service, async utils and the providing
+// extensions.
+class RequestManager : public Observer {
+ public:
+ RequestManager();
+ virtual ~RequestManager();
+
+ // Creates a request and returns its request id (greater than 0). Returns 0 in
+ // case of an error (eg. too many requests). The passed callbacks can be NULL.
+ int CreateRequest(const ProvidedFileSystem& file_system,
+ const SuccessCallback& success_callback,
+ const ErrorCallback& error_callback);
+
+ // Handles successful response for the |request_id|. If |has_next| is false,
+ // then the request is disposed, after handling the |response|. On error,
+ // returns false, and the request is disposed.
+ bool FulfillRequest(const ProvidedFileSystem& file_system,
+ int request_id,
+ scoped_ptr<base::DictionaryValue> response,
+ bool has_next);
+
+ // Handles error response for the |request_id|. If handling the error fails,
+ // returns false. Always disposes the request.
+ bool RejectRequest(const ProvidedFileSystem& file_system,
+ int request_id,
+ base::File::Error error);
+
+ // file_system_provider::Observer overrides.
+ virtual void OnProvidedFileSystemMount(const ProvidedFileSystem& file_system,
+ base::File::Error error) OVERRIDE;
+ virtual void OnProvidedFileSystemUnmount(
+ const ProvidedFileSystem& file_system,
+ base::File::Error error) OVERRIDE;
+
+ private:
+ struct Request {
+ Request();
+ ~Request();
+
+ // Provided file system handling the request.
+ ProvidedFileSystem file_system;
+
+ // Callback to be called on success.
+ SuccessCallback success_callback;
+
+ // Callback to be called on error.
+ ErrorCallback error_callback;
+ };
+
+ typedef std::map<int, Request> RequestMap;
+
+ RequestMap requests_;
+ int next_id_;
+
+ DISALLOW_COPY_AND_ASSIGN(RequestManager);
+};
+
+} // namespace file_system_provider
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_FILE_SYSTEM_PROVIDER_REQUEST_MANAGER_H_
diff --git a/chrome/browser/chromeos/file_system_provider/request_manager_unittest.cc b/chrome/browser/chromeos/file_system_provider/request_manager_unittest.cc
new file mode 100644
index 0000000..2ca0113
--- /dev/null
+++ b/chrome/browser/chromeos/file_system_provider/request_manager_unittest.cc
@@ -0,0 +1,348 @@
+// Copyright 2014 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 <string>
+
+#include "base/bind.h"
+#include "base/files/file.h"
+#include "base/files/file_path.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "base/memory/weak_ptr.h"
+#include "base/values.h"
+#include "chrome/browser/chromeos/file_system_provider/request_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+namespace file_system_provider {
+namespace {
+
+const char kExtensionId[] = "mbflcebpggnecokmikipoihdbecnjfoj";
+const int kFileSystemId = 1;
+const char kFileSystemName[] = "Camera Pictures";
+const base::FilePath::CharType kMountPath[] = FILE_PATH_LITERAL(
+ "/provided/mbflcebpggnecokmikipoihdbecnjfoj-1-testing_profile-hash");
+
+// Logs calls of the success and error callbacks on requests.
+class EventLogger {
+ public:
+ class SuccessEvent {
+ public:
+ SuccessEvent(scoped_ptr<base::DictionaryValue> result, bool has_next)
+ : result_(result.Pass()), has_next_(has_next) {}
+ ~SuccessEvent() {}
+
+ base::DictionaryValue* result() { return result_.get(); }
+ bool has_next() { return has_next_; }
+
+ private:
+ scoped_ptr<base::DictionaryValue> result_;
+ bool has_next_;
+ };
+
+ class ErrorEvent {
+ public:
+ explicit ErrorEvent(base::File::Error error) : error_(error) {}
+ ~ErrorEvent() {}
+
+ base::File::Error error() { return error_; }
+
+ private:
+ base::File::Error error_;
+ };
+
+ EventLogger() : weak_ptr_factory_(this) {}
+ virtual ~EventLogger() {}
+
+ void OnSuccess(scoped_ptr<base::DictionaryValue> result, bool has_next) {
+ success_events_.push_back(new SuccessEvent(result.Pass(), has_next));
+ }
+
+ void OnError(base::File::Error error) {
+ error_events_.push_back(new ErrorEvent(error));
+ }
+
+ ScopedVector<SuccessEvent>& success_events() { return success_events_; }
+ ScopedVector<ErrorEvent>& error_events() { return error_events_; }
+
+ base::WeakPtr<EventLogger> GetWeakPtr() {
+ return weak_ptr_factory_.GetWeakPtr();
+ }
+
+ private:
+ ScopedVector<SuccessEvent> success_events_;
+ ScopedVector<ErrorEvent> error_events_;
+ base::WeakPtrFactory<EventLogger> weak_ptr_factory_;
+};
+
+} // namespace
+
+class FileSystemProviderRequestManagerTest : public testing::Test {
+ protected:
+ FileSystemProviderRequestManagerTest() {}
+ virtual ~FileSystemProviderRequestManagerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ request_manager_.reset(new RequestManager());
+
+ // Configure the testing file system.
+ file_system = ProvidedFileSystem(kExtensionId,
+ kFileSystemId,
+ kFileSystemName,
+ base::FilePath(kMountPath));
+ }
+
+ scoped_ptr<RequestManager> request_manager_;
+ ProvidedFileSystem file_system;
+};
+
+TEST_F(FileSystemProviderRequestManagerTest, CreateAndFulFill) {
+ EventLogger logger;
+
+ int request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ EXPECT_EQ(1, request_id);
+ EXPECT_EQ(0u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+
+ scoped_ptr<base::DictionaryValue> response(new base::DictionaryValue());
+ const bool has_next = false;
+ response->SetString("path", "i-like-vanilla");
+
+ bool result = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), has_next);
+ EXPECT_TRUE(result);
+
+ // Validate if the callback has correct arguments.
+ ASSERT_EQ(1u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+ EventLogger::SuccessEvent* event = logger.success_events()[0];
+ ASSERT_TRUE(event->result());
+ std::string response_test_string;
+ EXPECT_TRUE(event->result()->GetString("path", &response_test_string));
+ EXPECT_EQ("i-like-vanilla", response_test_string);
+ EXPECT_EQ(has_next, event->has_next());
+
+ // Confirm, that the request is removed. Basically, fulfilling again for the
+ // same request, should fail.
+ {
+ bool retry = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), has_next);
+ EXPECT_FALSE(retry);
+ }
+
+ // Rejecting should also fail.
+ {
+ bool retry = request_manager_->RejectRequest(
+ file_system, request_id, base::File::FILE_ERROR_FAILED);
+ EXPECT_FALSE(retry);
+ }
+}
+
+TEST_F(FileSystemProviderRequestManagerTest, CreateAndFulFill_WithHasNext) {
+ EventLogger logger;
+
+ int request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ EXPECT_EQ(1, request_id);
+ EXPECT_EQ(0u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+
+ scoped_ptr<base::DictionaryValue> response;
+ const bool has_next = true;
+
+ bool result = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), has_next);
+ EXPECT_TRUE(result);
+
+ // Validate if the callback has correct arguments.
+ ASSERT_EQ(1u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+ EventLogger::SuccessEvent* event = logger.success_events()[0];
+ EXPECT_FALSE(event->result());
+ EXPECT_EQ(has_next, event->has_next());
+
+ // Confirm, that the request is not removed (since it has has_next == true).
+ // Basically, fulfilling again for the same request, should not fail.
+ {
+ bool new_has_next = false;
+ bool retry = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), new_has_next);
+ EXPECT_TRUE(retry);
+ }
+
+ // Since |new_has_next| is false, then the request should be removed. To check
+ // it, try to fulfill again, what should fail.
+ {
+ bool new_has_next = false;
+ bool retry = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), new_has_next);
+ EXPECT_FALSE(retry);
+ }
+}
+
+TEST_F(FileSystemProviderRequestManagerTest, CreateAndReject) {
+ EventLogger logger;
+
+ int request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ EXPECT_EQ(1, request_id);
+ EXPECT_EQ(0u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+
+ base::File::Error error = base::File::FILE_ERROR_NO_MEMORY;
+ bool result = request_manager_->RejectRequest(file_system, request_id, error);
+ EXPECT_TRUE(result);
+
+ // Validate if the callback has correct arguments.
+ ASSERT_EQ(1u, logger.error_events().size());
+ EXPECT_EQ(0u, logger.success_events().size());
+ EventLogger::ErrorEvent* event = logger.error_events()[0];
+ EXPECT_EQ(error, event->error());
+
+ // Confirm, that the request is removed. Basically, fulfilling again for the
+ // same request, should fail.
+ {
+ scoped_ptr<base::DictionaryValue> response;
+ bool has_next = false;
+ bool retry = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), has_next);
+ EXPECT_FALSE(retry);
+ }
+
+ // Rejecting should also fail.
+ {
+ bool retry =
+ request_manager_->RejectRequest(file_system, request_id, error);
+ EXPECT_FALSE(retry);
+ }
+}
+
+TEST_F(FileSystemProviderRequestManagerTest,
+ CreateAndFulfillWithWrongRequestId) {
+ EventLogger logger;
+
+ int request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ EXPECT_EQ(1, request_id);
+ EXPECT_EQ(0u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+
+ base::File::Error error = base::File::FILE_ERROR_NO_MEMORY;
+ bool result =
+ request_manager_->RejectRequest(file_system, request_id + 1, error);
+ EXPECT_FALSE(result);
+
+ // Callbacks should not be called.
+ EXPECT_EQ(0u, logger.error_events().size());
+ EXPECT_EQ(0u, logger.success_events().size());
+
+ // Confirm, that the request hasn't been removed, by rejecting it correctly.
+ {
+ bool retry =
+ request_manager_->RejectRequest(file_system, request_id, error);
+ EXPECT_TRUE(retry);
+ }
+}
+
+TEST_F(FileSystemProviderRequestManagerTest,
+ CreateAndRejectWithWrongRequestId) {
+ EventLogger logger;
+
+ int request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ EXPECT_EQ(1, request_id);
+ EXPECT_EQ(0u, logger.success_events().size());
+ EXPECT_EQ(0u, logger.error_events().size());
+
+ base::File::Error error = base::File::FILE_ERROR_NO_MEMORY;
+ bool result =
+ request_manager_->RejectRequest(file_system, request_id + 1, error);
+ EXPECT_FALSE(result);
+
+ // Callbacks should not be called.
+ EXPECT_EQ(0u, logger.error_events().size());
+ EXPECT_EQ(0u, logger.success_events().size());
+
+ // Confirm, that the request hasn't been removed, by rejecting it correctly.
+ {
+ bool retry =
+ request_manager_->RejectRequest(file_system, request_id, error);
+ EXPECT_TRUE(retry);
+ }
+}
+
+TEST_F(FileSystemProviderRequestManagerTest,
+ CreateAndFulfillWithUnownedRequestId) {
+ EventLogger logger;
+
+ int request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+ EXPECT_EQ(1, request_id);
+
+ // Create another file system, which has just a different fiel system id
+ // (1 -> 2).
+ ProvidedFileSystem another_file_system(
+ kExtensionId,
+ 2, // file_system_id
+ "Music",
+ base::FilePath::FromUTF8Unsafe(
+ "/provided/mbflcebpggnecokmikipoihdbecnjfoj-2-testing_profile-hash"));
+
+ scoped_ptr<base::DictionaryValue> response;
+ const bool has_next = false;
+
+ bool result = request_manager_->FulfillRequest(
+ another_file_system, request_id, response.Pass(), has_next);
+ EXPECT_FALSE(result);
+
+ // Callbacks should not be called.
+ EXPECT_EQ(0u, logger.error_events().size());
+ EXPECT_EQ(0u, logger.success_events().size());
+
+ // Confirm, that the request hasn't been removed, by fulfilling it again, but
+ // with a correct file system.
+ {
+ bool retry = request_manager_->FulfillRequest(
+ file_system, request_id, response.Pass(), has_next);
+ EXPECT_TRUE(retry);
+ }
+}
+
+TEST_F(FileSystemProviderRequestManagerTest, UniqueIds) {
+ EventLogger logger;
+
+ int first_request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ int second_request_id = request_manager_->CreateRequest(
+ file_system,
+ base::Bind(&EventLogger::OnSuccess, logger.GetWeakPtr()),
+ base::Bind(&EventLogger::OnError, logger.GetWeakPtr()));
+
+ EXPECT_EQ(1, first_request_id);
+ EXPECT_EQ(2, second_request_id);
+}
+
+} // namespace file_system_provider
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/file_system_provider/service.cc b/chrome/browser/chromeos/file_system_provider/service.cc
index fcb7d5a..f8e7136 100644
--- a/chrome/browser/chromeos/file_system_provider/service.cc
+++ b/chrome/browser/chromeos/file_system_provider/service.cc
@@ -6,12 +6,16 @@
#include "base/files/file_path.h"
#include "base/strings/string_number_conversions.h"
+#include "base/values.h"
#include "chrome/browser/chromeos/file_system_provider/observer.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system.h"
#include "chrome/browser/chromeos/file_system_provider/service_factory.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/common/extensions/api/file_system_provider.h"
#include "content/public/browser/browser_thread.h"
+#include "extensions/browser/event_router.h"
+#include "extensions/browser/extension_system.h"
#include "webkit/browser/fileapi/external_mount_points.h"
namespace chromeos {
@@ -40,9 +44,22 @@ base::FilePath GetMountPointPath(Profile* profile,
extension_id + "-" + base::IntToString(file_system_id) + user_suffix);
}
+// Creates values to be passed to request events. These values can be extended
+// by additional fields.
+scoped_ptr<base::ListValue> CreateRequestValues(int file_system_id,
+ int request_id) {
+ scoped_ptr<base::ListValue> values(new base::ListValue());
+ values->AppendInteger(file_system_id);
+ values->AppendInteger(request_id);
+ return values.Pass();
+}
+
} // namespace
-Service::Service(Profile* profile) : profile_(profile), next_id_(1) {}
+Service::Service(Profile* profile)
+ : profile_(profile), next_id_(1), weak_ptr_factory_(this) {
+ AddObserver(&request_manager_);
+}
Service::~Service() {}
@@ -52,24 +69,28 @@ Service* Service::Get(content::BrowserContext* context) {
}
void Service::AddObserver(Observer* observer) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(observer);
observers_.AddObserver(observer);
}
void Service::RemoveObserver(Observer* observer) {
- DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
DCHECK(observer);
observers_.RemoveObserver(observer);
}
-int Service::RegisterFileSystem(const std::string& extension_id,
- const std::string& file_system_name) {
+int Service::MountFileSystem(const std::string& extension_id,
+ const std::string& file_system_name) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
// Restrict number of file systems to prevent system abusing.
- if (file_systems_.size() + 1 > kMaxFileSystems)
+ if (file_systems_.size() + 1 > kMaxFileSystems) {
+ FOR_EACH_OBSERVER(
+ Observer,
+ observers_,
+ OnProvidedFileSystemMount(ProvidedFileSystem(),
+ base::File::FILE_ERROR_TOO_MANY_OPENED));
return 0;
+ }
// The file system id is unique per service, so per profile.
int file_system_id = next_id_;
@@ -89,6 +110,11 @@ int Service::RegisterFileSystem(const std::string& extension_id,
fileapi::kFileSystemTypeProvided,
fileapi::FileSystemMountOption(),
mount_point_path)) {
+ FOR_EACH_OBSERVER(
+ Observer,
+ observers_,
+ OnProvidedFileSystemMount(ProvidedFileSystem(),
+ base::File::FILE_ERROR_INVALID_OPERATION));
return 0;
}
@@ -103,19 +129,26 @@ int Service::RegisterFileSystem(const std::string& extension_id,
file_systems_[file_system_id] = file_system;
FOR_EACH_OBSERVER(
- Observer, observers_, OnProvidedFileSystemRegistered(file_system));
+ Observer,
+ observers_,
+ OnProvidedFileSystemMount(file_system, base::File::FILE_OK));
next_id_++;
return file_system_id;
}
-bool Service::UnregisterFileSystem(const std::string& extension_id,
- int file_system_id) {
+bool Service::UnmountFileSystem(const std::string& extension_id,
+ int file_system_id) {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
FileSystemMap::iterator file_system_it = file_systems_.find(file_system_id);
if (file_system_it == file_systems_.end() ||
file_system_it->second.extension_id() != extension_id) {
+ const ProvidedFileSystem empty_file_system;
+ FOR_EACH_OBSERVER(Observer,
+ observers_,
+ OnProvidedFileSystemUnmount(
+ empty_file_system, base::File::FILE_ERROR_NOT_FOUND));
return false;
}
@@ -125,18 +158,25 @@ bool Service::UnregisterFileSystem(const std::string& extension_id,
const std::string mount_point_name =
file_system_it->second.mount_path().BaseName().value();
- if (!mount_points->RevokeFileSystem(mount_point_name))
+ if (!mount_points->RevokeFileSystem(mount_point_name)) {
+ FOR_EACH_OBSERVER(
+ Observer,
+ observers_,
+ OnProvidedFileSystemUnmount(file_system_it->second,
+ base::File::FILE_ERROR_INVALID_OPERATION));
return false;
+ }
- FOR_EACH_OBSERVER(Observer,
- observers_,
- OnProvidedFileSystemUnregistered(file_system_it->second));
+ FOR_EACH_OBSERVER(
+ Observer,
+ observers_,
+ OnProvidedFileSystemUnmount(file_system_it->second, base::File::FILE_OK));
file_systems_.erase(file_system_it);
return true;
}
-std::vector<ProvidedFileSystem> Service::GetRegisteredFileSystems() {
+std::vector<ProvidedFileSystem> Service::GetMountedFileSystems() {
DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
std::vector<ProvidedFileSystem> result;
@@ -148,5 +188,79 @@ std::vector<ProvidedFileSystem> Service::GetRegisteredFileSystems() {
return result;
}
+bool Service::FulfillRequest(const std::string& extension_id,
+ int file_system_id,
+ int request_id,
+ scoped_ptr<base::DictionaryValue> result,
+ bool has_next) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ FileSystemMap::iterator file_system_it = file_systems_.find(file_system_id);
+ if (file_system_it == file_systems_.end() ||
+ file_system_it->second.extension_id() != extension_id) {
+ return false;
+ }
+
+ return request_manager_.FulfillRequest(
+ file_system_it->second, request_id, result.Pass(), has_next);
+}
+
+bool Service::RejectRequest(const std::string& extension_id,
+ int file_system_id,
+ int request_id,
+ base::File::Error error) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ FileSystemMap::iterator file_system_it = file_systems_.find(file_system_id);
+ if (file_system_it == file_systems_.end() ||
+ file_system_it->second.extension_id() != extension_id) {
+ return false;
+ }
+
+ return request_manager_.RejectRequest(
+ file_system_it->second, request_id, error);
+}
+
+bool Service::RequestUnmount(int file_system_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ FileSystemMap::iterator file_system_it = file_systems_.find(file_system_id);
+ if (file_system_it == file_systems_.end())
+ return false;
+
+ int request_id =
+ request_manager_.CreateRequest(file_system_it->second,
+ SuccessCallback(),
+ base::Bind(&Service::OnRequestUnmountError,
+ weak_ptr_factory_.GetWeakPtr(),
+ file_system_it->second));
+
+ if (!request_id)
+ return false;
+
+ scoped_ptr<base::ListValue> values(
+ CreateRequestValues(file_system_id, request_id));
+
+ extensions::EventRouter* event_router =
+ extensions::ExtensionSystem::Get(profile_)->event_router();
+ DCHECK(event_router);
+
+ event_router->DispatchEventToExtension(
+ file_system_it->second.extension_id(),
+ make_scoped_ptr(new extensions::Event(
+ extensions::api::file_system_provider::OnUnmountRequested::kEventName,
+ values.Pass())));
+
+ return true;
+}
+
+void Service::Shutdown() { RemoveObserver(&request_manager_); }
+
+void Service::OnRequestUnmountError(const ProvidedFileSystem& file_system,
+ base::File::Error error) {
+ FOR_EACH_OBSERVER(
+ Observer, observers_, OnProvidedFileSystemUnmount(file_system, error));
+}
+
} // namespace file_system_provider
} // namespace chromeos
diff --git a/chrome/browser/chromeos/file_system_provider/service.h b/chrome/browser/chromeos/file_system_provider/service.h
index f3cb080..735e05f 100644
--- a/chrome/browser/chromeos/file_system_provider/service.h
+++ b/chrome/browser/chromeos/file_system_provider/service.h
@@ -9,8 +9,13 @@
#include <string>
#include <vector>
+#include "base/files/file.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "base/values.h"
#include "chrome/browser/chromeos/file_system_provider/observer.h"
+#include "chrome/browser/chromeos/file_system_provider/request_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/file_system_provider.h"
#include "components/keyed_service/core/keyed_service.h"
@@ -27,19 +32,39 @@ class Service : public KeyedService {
explicit Service(Profile* profile);
virtual ~Service();
- // Registers a file system provided by an extension with the |extension_id|.
+ // Mounts a file system provided by an extension with the |extension_id|.
// For success, it returns a numeric file system id, which is an
// auto-incremented non-zero value. For failures, it returns zero.
- int RegisterFileSystem(const std::string& extension_id,
- const std::string& file_system_name);
+ int MountFileSystem(const std::string& extension_id,
+ const std::string& file_system_name);
- // Unregisters a file system with the specified |file_system_id| for the
+ // Unmounts a file system with the specified |file_system_id| for the
// |extension_id|. For success returns true, otherwise false.
- bool UnregisterFileSystem(const std::string& extension_id,
- int file_system_id);
-
- // Returns a list of currently registered file systems. All items are copied.
- std::vector<ProvidedFileSystem> GetRegisteredFileSystems();
+ bool UnmountFileSystem(const std::string& extension_id, int file_system_id);
+
+ // Returns a list of currently mounted file systems. All items are copied.
+ std::vector<ProvidedFileSystem> GetMountedFileSystems();
+
+ // Handles successful response for the |request_id|. If |has_next| is false,
+ // then the request is disposed, after handling the |response|. On error,
+ // returns false, and the request is disposed.
+ bool FulfillRequest(const std::string& extension_id,
+ int file_system_id,
+ int request_id,
+ scoped_ptr<base::DictionaryValue> result,
+ bool has_next);
+
+ // Handles error response for the |request_id|. If handling the error fails,
+ // returns false. Always disposes the request.
+ bool RejectRequest(const std::string& extension_id,
+ int file_system_id,
+ int request_id,
+ base::File::Error error);
+
+ // Requests unmounting of a file system with the passed |file_system_id|.
+ // Returns true is unmounting has been requested. False, if the request is
+ // invalid (eg. already unmounted).
+ bool RequestUnmount(int file_system_id);
// Adds and removes observers.
void AddObserver(Observer* observer);
@@ -48,13 +73,23 @@ class Service : public KeyedService {
// Gets the singleton instance for the |context|.
static Service* Get(content::BrowserContext* context);
+ // BrowserContextKeyedService overrides.
+ virtual void Shutdown() OVERRIDE;
+
private:
typedef std::map<int, ProvidedFileSystem> FileSystemMap;
+ // Called when the providing extension calls the success callback for the
+ // onUnmountRequested event.
+ void OnRequestUnmountError(const ProvidedFileSystem& file_system,
+ base::File::Error error);
+
+ RequestManager request_manager_;
Profile* profile_;
ObserverList<Observer> observers_;
FileSystemMap file_systems_;
int next_id_;
+ base::WeakPtrFactory<Service> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(Service);
};
diff --git a/chrome/browser/chromeos/file_system_provider/service_unittest.cc b/chrome/browser/chromeos/file_system_provider/service_unittest.cc
index 0e848a5..d72dd01 100644
--- a/chrome/browser/chromeos/file_system_provider/service_unittest.cc
+++ b/chrome/browser/chromeos/file_system_provider/service_unittest.cc
@@ -3,9 +3,10 @@
// found in the LICENSE file.
#include <string>
+#include <vector>
+#include "base/files/file.h"
#include "base/memory/scoped_ptr.h"
-#include "base/memory/scoped_vector.h"
#include "chrome/browser/chromeos/file_system_provider/observer.h"
#include "chrome/browser/chromeos/file_system_provider/provided_file_system.h"
#include "chrome/browser/chromeos/file_system_provider/service.h"
@@ -25,22 +26,37 @@ const char kFileSystemName[] = "Camera Pictures";
// Utility observer, logging events from file_system_provider::Service.
class LoggingObserver : public Observer {
public:
+ class Event {
+ public:
+ Event(const ProvidedFileSystem& file_system, base::File::Error error)
+ : file_system_(file_system), error_(error) {}
+ ~Event() {}
+
+ const ProvidedFileSystem& file_system() { return file_system_; }
+ base::File::Error error() { return error_; }
+
+ private:
+ ProvidedFileSystem file_system_;
+ base::File::Error error_;
+ };
+
LoggingObserver() {}
virtual ~LoggingObserver() {}
// file_system_provider::Observer overrides.
- virtual void OnProvidedFileSystemRegistered(
- const ProvidedFileSystem& file_system) OVERRIDE {
- registered.push_back(new const ProvidedFileSystem(file_system));
+ virtual void OnProvidedFileSystemMount(const ProvidedFileSystem& file_system,
+ base::File::Error error) OVERRIDE {
+ mounts.push_back(Event(file_system, error));
}
- virtual void OnProvidedFileSystemUnregistered(
- const ProvidedFileSystem& file_system) OVERRIDE {
- unregistered.push_back(new const ProvidedFileSystem(file_system));
+ virtual void OnProvidedFileSystemUnmount(
+ const ProvidedFileSystem& file_system,
+ base::File::Error error) OVERRIDE {
+ unmounts.push_back(Event(file_system, error));
}
- ScopedVector<const ProvidedFileSystem> registered;
- ScopedVector<const ProvidedFileSystem> unregistered;
+ std::vector<Event> mounts;
+ std::vector<Event> unmounts;
};
} // namespace
@@ -69,121 +85,135 @@ class FileSystemProviderServiceTest : public testing::Test {
scoped_ptr<Service> file_system_provider_service_;
};
-TEST_F(FileSystemProviderServiceTest, RegisterFileSystem) {
+TEST_F(FileSystemProviderServiceTest, MountFileSystem) {
LoggingObserver observer;
file_system_provider_service_->AddObserver(&observer);
- int file_system_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
EXPECT_LT(0, file_system_id);
- ASSERT_EQ(1u, observer.registered.size());
- EXPECT_EQ(kExtensionId, observer.registered[0]->extension_id());
- EXPECT_EQ(1, observer.registered[0]->file_system_id());
+ ASSERT_EQ(1u, observer.mounts.size());
+ EXPECT_EQ(kExtensionId, observer.mounts[0].file_system().extension_id());
+ EXPECT_EQ(1, observer.mounts[0].file_system().file_system_id());
EXPECT_EQ("/provided/mbflcebpggnecokmikipoihdbecnjfoj-1-testing_profile-hash",
- observer.registered[0]->mount_path().AsUTF8Unsafe());
- EXPECT_EQ(kFileSystemName, observer.registered[0]->file_system_name());
- ASSERT_EQ(0u, observer.unregistered.size());
+ observer.mounts[0].file_system().mount_path().AsUTF8Unsafe());
+ EXPECT_EQ(kFileSystemName,
+ observer.mounts[0].file_system().file_system_name());
+ EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error());
+ ASSERT_EQ(0u, observer.unmounts.size());
std::vector<ProvidedFileSystem> provided_file_systems =
- file_system_provider_service_->GetRegisteredFileSystems();
+ file_system_provider_service_->GetMountedFileSystems();
ASSERT_EQ(1u, provided_file_systems.size());
file_system_provider_service_->RemoveObserver(&observer);
}
-TEST_F(FileSystemProviderServiceTest, RegisterFileSystem_UniqueIds) {
+TEST_F(FileSystemProviderServiceTest, MountFileSystem_UniqueIds) {
LoggingObserver observer;
file_system_provider_service_->AddObserver(&observer);
- int file_system_first_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_first_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
- ASSERT_LT(0, file_system_first_id);
+ EXPECT_LT(0, file_system_first_id);
- int file_system_second_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_second_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
- ASSERT_LT(0, file_system_second_id);
+ EXPECT_LT(0, file_system_second_id);
- ASSERT_NE(file_system_first_id, file_system_second_id);
- ASSERT_EQ(2u, observer.registered.size());
+ EXPECT_NE(file_system_first_id, file_system_second_id);
+ ASSERT_EQ(2u, observer.mounts.size());
+ EXPECT_EQ(base::File::FILE_OK, observer.mounts[0].error());
+ EXPECT_EQ(base::File::FILE_OK, observer.mounts[1].error());
std::vector<ProvidedFileSystem> provided_file_systems =
- file_system_provider_service_->GetRegisteredFileSystems();
+ file_system_provider_service_->GetMountedFileSystems();
ASSERT_EQ(2u, provided_file_systems.size());
file_system_provider_service_->RemoveObserver(&observer);
}
-TEST_F(FileSystemProviderServiceTest, RegisterFileSystem_StressTest) {
+TEST_F(FileSystemProviderServiceTest, MountFileSystem_StressTest) {
LoggingObserver observer;
file_system_provider_service_->AddObserver(&observer);
const size_t kMaxFileSystems = 16;
for (size_t i = 0; i < kMaxFileSystems; ++i) {
- int file_system_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
- ASSERT_LT(0, file_system_id);
+ EXPECT_LT(0, file_system_id);
}
- ASSERT_EQ(kMaxFileSystems, observer.registered.size());
+ ASSERT_EQ(kMaxFileSystems, observer.mounts.size());
// The next file system is out of limit, and registering it should fail.
- int file_system_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
- ASSERT_EQ(0, file_system_id);
- ASSERT_EQ(kMaxFileSystems, observer.registered.size());
+ EXPECT_EQ(0, file_system_id);
+
+ ASSERT_EQ(kMaxFileSystems + 1, observer.mounts.size());
+ EXPECT_EQ(base::File::FILE_ERROR_TOO_MANY_OPENED,
+ observer.mounts[kMaxFileSystems].error());
+ ASSERT_EQ(kMaxFileSystems,
+ file_system_provider_service_->GetMountedFileSystems().size());
std::vector<ProvidedFileSystem> provided_file_systems =
- file_system_provider_service_->GetRegisteredFileSystems();
+ file_system_provider_service_->GetMountedFileSystems();
ASSERT_EQ(kMaxFileSystems, provided_file_systems.size());
file_system_provider_service_->RemoveObserver(&observer);
}
-TEST_F(FileSystemProviderServiceTest, UnregisterFileSystem) {
+TEST_F(FileSystemProviderServiceTest, UnmountFileSystem) {
LoggingObserver observer;
file_system_provider_service_->AddObserver(&observer);
- int file_system_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
- ASSERT_LT(0, file_system_id);
- ASSERT_EQ(1u, observer.registered.size());
+ EXPECT_LT(0, file_system_id);
+ ASSERT_EQ(1u, observer.mounts.size());
- const bool result = file_system_provider_service_->UnregisterFileSystem(
+ const bool result = file_system_provider_service_->UnmountFileSystem(
kExtensionId, file_system_id);
- ASSERT_TRUE(result);
- ASSERT_EQ(1u, observer.unregistered.size());
+ EXPECT_TRUE(result);
+ ASSERT_EQ(1u, observer.unmounts.size());
+ EXPECT_EQ(base::File::FILE_OK, observer.unmounts[0].error());
- EXPECT_EQ(kExtensionId, observer.unregistered[0]->extension_id());
- EXPECT_EQ(1, observer.unregistered[0]->file_system_id());
+ EXPECT_EQ(kExtensionId, observer.unmounts[0].file_system().extension_id());
+ EXPECT_EQ(1, observer.unmounts[0].file_system().file_system_id());
EXPECT_EQ("/provided/mbflcebpggnecokmikipoihdbecnjfoj-1-testing_profile-hash",
- observer.unregistered[0]->mount_path().AsUTF8Unsafe());
- EXPECT_EQ(kFileSystemName, observer.unregistered[0]->file_system_name());
+ observer.unmounts[0].file_system().mount_path().AsUTF8Unsafe());
+ EXPECT_EQ(kFileSystemName,
+ observer.unmounts[0].file_system().file_system_name());
std::vector<ProvidedFileSystem> provided_file_systems =
- file_system_provider_service_->GetRegisteredFileSystems();
+ file_system_provider_service_->GetMountedFileSystems();
ASSERT_EQ(0u, provided_file_systems.size());
file_system_provider_service_->RemoveObserver(&observer);
}
-TEST_F(FileSystemProviderServiceTest, UnregisterFileSystem_WrongExtensionId) {
+TEST_F(FileSystemProviderServiceTest, UnmountFileSystem_WrongExtensionId) {
LoggingObserver observer;
file_system_provider_service_->AddObserver(&observer);
const std::string kWrongExtensionId = "helloworldhelloworldhelloworldhe";
- int file_system_id = file_system_provider_service_->RegisterFileSystem(
+ int file_system_id = file_system_provider_service_->MountFileSystem(
kExtensionId, kFileSystemName);
- ASSERT_LT(0, file_system_id);
- ASSERT_EQ(1u, observer.registered.size());
+ EXPECT_LT(0, file_system_id);
+ ASSERT_EQ(1u, observer.mounts.size());
+ ASSERT_EQ(1u, file_system_provider_service_->GetMountedFileSystems().size());
- const bool result = file_system_provider_service_->UnregisterFileSystem(
+ const bool result = file_system_provider_service_->UnmountFileSystem(
kWrongExtensionId, file_system_id);
- ASSERT_FALSE(result);
- ASSERT_EQ(0u, observer.unregistered.size());
+ EXPECT_FALSE(result);
+ ASSERT_EQ(1u, observer.unmounts.size());
+ EXPECT_EQ(base::File::FILE_ERROR_NOT_FOUND, observer.unmounts[0].error());
+ ASSERT_EQ(1u, file_system_provider_service_->GetMountedFileSystems().size());
std::vector<ProvidedFileSystem> provided_file_systems =
- file_system_provider_service_->GetRegisteredFileSystems();
+ file_system_provider_service_->GetMountedFileSystems();
ASSERT_EQ(1u, provided_file_systems.size());
file_system_provider_service_->RemoveObserver(&observer);
diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi
index 057debe..8f21086 100644
--- a/chrome/chrome_browser_chromeos.gypi
+++ b/chrome/chrome_browser_chromeos.gypi
@@ -372,6 +372,8 @@
'browser/chromeos/file_system_provider/observer.h',
'browser/chromeos/file_system_provider/provided_file_system.cc',
'browser/chromeos/file_system_provider/provided_file_system.h',
+ 'browser/chromeos/file_system_provider/request_manager.cc',
+ 'browser/chromeos/file_system_provider/request_manager.h',
'browser/chromeos/file_system_provider/service.cc',
'browser/chromeos/file_system_provider/service.h',
'browser/chromeos/file_system_provider/service_factory.cc',
diff --git a/chrome/chrome_tests_unit.gypi b/chrome/chrome_tests_unit.gypi
index c73a2b2..b174809 100644
--- a/chrome/chrome_tests_unit.gypi
+++ b/chrome/chrome_tests_unit.gypi
@@ -720,6 +720,7 @@
'browser/chromeos/file_manager/path_util_unittest.cc',
'browser/chromeos/file_manager/url_util_unittest.cc',
'browser/chromeos/file_manager/volume_manager_unittest.cc',
+ 'browser/chromeos/file_system_provider/request_manager_unittest.cc',
'browser/chromeos/file_system_provider/service_unittest.cc',
'browser/chromeos/extensions/device_local_account_management_policy_provider_unittest.cc',
'browser/chromeos/extensions/wallpaper_private_api_unittest.cc',
diff --git a/chrome/common/extensions/api/_api_features.json b/chrome/common/extensions/api/_api_features.json
index 9ed28d2..88a66d1 100644
--- a/chrome/common/extensions/api/_api_features.json
+++ b/chrome/common/extensions/api/_api_features.json
@@ -374,6 +374,11 @@
"dependencies": ["permission:fileSystemProvider"],
"contexts": ["blessed_extension"]
},
+ "fileSystemProviderInternal": {
+ "internal": true,
+ "dependencies": ["fileSystemProvider"],
+ "contexts": ["blessed_extension"]
+ },
"firstRunPrivate": {
"platforms": ["chromeos"],
"dependencies": ["permission:firstRunPrivate"],
diff --git a/chrome/common/extensions/api/api.gyp b/chrome/common/extensions/api/api.gyp
index 63e5295..1ed8619 100644
--- a/chrome/common/extensions/api/api.gyp
+++ b/chrome/common/extensions/api/api.gyp
@@ -69,6 +69,7 @@
'file_browser_private.idl',
'file_system.idl',
'file_system_provider.idl',
+ 'file_system_provider_internal.idl',
'font_settings.json',
'gcm.json',
'hangouts_private.idl',
diff --git a/chrome/common/extensions/api/file_system_provider.idl b/chrome/common/extensions/api/file_system_provider.idl
index 173a1c8..e10cb8c 100644
--- a/chrome/common/extensions/api/file_system_provider.idl
+++ b/chrome/common/extensions/api/file_system_provider.idl
@@ -7,6 +7,28 @@
[platforms=("chromeos"),
implemented_in="chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h"]
namespace fileSystemProvider {
+ // Error codes used by providing extensions in response to requests. For
+ // success, <code>OK</code> should be used.
+ enum ProviderError {
+ OK,
+ FAILED,
+ IN_USE,
+ EXISTS,
+ NOT_FOUND,
+ ACCESS_DENIED,
+ TOO_MANY_OPENED,
+ NO_MEMORY,
+ NO_SPACE,
+ NOT_A_DIRECTORY,
+ INVALID_OPERATION,
+ SECURITY,
+ ABORT,
+ NOT_A_FILE,
+ NOT_EMPTY,
+ INVALID_URL,
+ IO
+ };
+
// Callback to receive the result of mount() function.
// <code>fileSystemID</code> will be a unique ID for the file system just
// mounted. The ID is used to distinguish multiple file systems mounted
@@ -14,6 +36,18 @@ namespace fileSystemProvider {
callback MountCallback = void(long fileSystemId,
[nodoc, instanceOf=DOMError] object error);
+
+ // Callback to receive the result of unmount() function with the <code>
+ // fileSystemId</code> identifier.
+ callback UnmountCallback = void(long fileSystemId,
+ [nodoc, instanceOf=DOMError] object error);
+
+ // Callback to be called by the providing extension in case of a success.
+ callback ProviderSuccessCallback = void();
+
+ // Callback to be called by the providing extension in case of an error.
+ callback ProviderErrorCallback = void(ProviderError error);
+
// Callback to handle an error raised from the browser.
[nocompile] callback ErrorCallback = void([instanceOf=DOMError] object error);
@@ -22,10 +56,33 @@ namespace fileSystemProvider {
// <code>displayName</code> will be shown in the left panel of
// Files.app. <code>displayName</code> can contain any characters
// including '/', but cannot be an empty string. <code>displayName</code>
- // should be descritive but doesn't have to be unique. Duplicate display
+ // should be descriptive but doesn't have to be unique. Duplicate display
// names are uniquified by adding suffix like "(1)" in the Files.app UI.
static void mount(DOMString displayName,
MountCallback successCallback,
[nocompile] ErrorCallback errorCallback);
+
+ // Unmounts a file system with the given <code>fileSystemId</code>. It
+ // should be called after <code>onUnmountRequested</code> is invoked. Also,
+ // the providing extension can decide to perform unmounting if not requested
+ // (eg. in case of lost connection, or a file error). If there is no file
+ // system with the requested id, or unmounting fails, then the
+ // <code>errorCallback</code> will be called.
+ static void unmount(long fileSystemId,
+ UnmountCallback successCallback,
+ [nocompile] ErrorCallback errorCallback);
+ };
+
+ interface Events {
+ // Raised, when the user requests unmounting of the file system with the
+ // <code>fileSystemId</code> identifier in the Files.app UI. In response,
+ // the <code>unmount</code> API method should be called. If unmounting is
+ // not possible (eg. due to pending operation), then <code>errorCallback
+ // </code> should be called, and <code>unmount</code> should not be called.
+ [maxListeners=1] static void onUnmountRequested(
+ long fileSystemId,
+ ProviderSuccessCallback successCallback,
+ ProviderErrorCallback errorCallback);
};
};
+
diff --git a/chrome/common/extensions/api/file_system_provider_internal.idl b/chrome/common/extensions/api/file_system_provider_internal.idl
new file mode 100644
index 0000000..4ada58f
--- /dev/null
+++ b/chrome/common/extensions/api/file_system_provider_internal.idl
@@ -0,0 +1,26 @@
+// Copyright 2014 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.
+
+// Internal, used by fileSystemProvider's custom bindings. These functions are
+// called when events' callbacks are invoked.
+[platforms=("chromeos"),
+ implemented_in="chrome/browser/chromeos/extensions/file_system_provider/file_system_provider_api.h", nodoc]
+namespace fileSystemProviderInternal {
+ interface Functions {
+ // Internal. Success callback of the <code>onUnmountRequested</code>
+ // event. Must be called when unmounting is completed.
+ static void unmountRequestedSuccess(long fileSystemId,
+ long requestId);
+
+ // Internal. Error callback of the <code>onUnmountRequested</code>
+ // event. Must be called if unmounting fails.
+ static void unmountRequestedError(
+ long fileSystemId,
+ long requestId,
+ // TODO(mtomasz): Use FileSystemProvider::ProviderError when namespaces
+ // are fully implemented in IDL.
+ DOMString error);
+ };
+};
+
diff --git a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
index b7e2ea8..b896138 100644
--- a/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
+++ b/chrome/renderer/resources/extensions/file_system_provider_custom_bindings.js
@@ -5,6 +5,9 @@
// Custom binding for the fileSystemProvider API.
var binding = require('binding').Binding.create('fileSystemProvider');
+var fileSystemProviderInternal =
+ require('binding').Binding.create('fileSystemProviderInternal').generate();
+var eventBindings = require('event_bindings');
var fileSystemNatives = requireNative('file_system_natives');
var GetDOMError = fileSystemNatives.GetDOMError;
@@ -44,6 +47,54 @@ binding.registerCustomHook(function(bindingsAPI) {
successCallback(fileSystemId);
}
});
+
+ apiFunctions.setUpdateArgumentsPostValidate(
+ 'unmount',
+ function(fileSystemId, successCallback, errorCallback) {
+ // Piggyback the error callback onto the success callback,
+ // so we can use the error callback later.
+ successCallback.errorCallback_ = errorCallback;
+ return [fileSystemId, successCallback];
+ });
+
+ apiFunctions.setCustomCallback(
+ 'unmount',
+ function(name, request, response) {
+ var domError = null;
+ if (request.callback) {
+ // DOMError is present only if mount() failed.
+ if (response && response[0]) {
+ // Convert a Dictionary to a DOMError.
+ domError = GetDOMError(response[0].name, response[0].message);
+ response.length = 1;
+ }
+
+ var successCallback = request.callback;
+ var errorCallback = request.callback.errorCallback_;
+ delete request.callback;
+
+ if (domError)
+ errorCallback(domError);
+ else
+ successCallback();
+ }
+ });
});
+eventBindings.registerArgumentMassager(
+ 'fileSystemProvider.onUnmountRequested',
+ function(args, dispatch) {
+ var fileSystemId = args[0];
+ var requestId = args[1];
+ var onSuccessCallback = function() {
+ fileSystemProviderInternal.unmountRequestedSuccess(
+ fileSystemId, requestId);
+ };
+ var onErrorCallback = function(error) {
+ fileSystemProviderInternal.unmountRequestedError(
+ fileSystemId, requestId, error);
+ }
+ dispatch([fileSystemId, onSuccessCallback, onErrorCallback]);
+ });
+
exports.binding = binding.generate();
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/mount/test.js b/chrome/test/data/extensions/api_test/file_system_provider/mount/test.js
index b5566d5..00addb8 100644
--- a/chrome/test/data/extensions/api_test/file_system_provider/mount/test.js
+++ b/chrome/test/data/extensions/api_test/file_system_provider/mount/test.js
@@ -3,6 +3,7 @@
// found in the LICENSE file.
chrome.test.runTests([
+ // Tests whether mounting succeeds, when a non-empty name is provided.
function goodDisplayName() {
chrome.fileSystemProvider.mount(
'test file system',
@@ -17,6 +18,7 @@ chrome.test.runTests([
);
},
+ // Verifies that mounting fails, when an empty string is provided as a name.
function emptyDisplayName() {
chrome.fileSystemProvider.mount(
'',
@@ -30,6 +32,9 @@ chrome.test.runTests([
);
},
+ // End to end test. Mounts a volume using fileSystemProvider.mount(), then
+ // checks if the mounted volume is added to VolumeManager, by querying
+ // fileBrowserPrivate.getVolumeMetadataList().
function successfulMount() {
chrome.fileSystemProvider.mount(
'caramel-candy.zip',
@@ -49,10 +54,11 @@ chrome.test.runTests([
});
},
+ // Checks is limit for mounted file systems per profile works correctly.
+ // Tries to create more than allowed number of file systems. All of the mount
+ // requests should succeed, except the last one which should fail with a
+ // security error.
function stressMountTest() {
- // Try to create more than allowed number of file systems. All of the mount
- // requests should succeed, except the last one which should fail with a
- // security error.
var ALREADY_MOUNTED_FILE_SYSTEMS = 2; // By previous tests.
var MAX_FILE_SYSTEMS = 16;
var index = 0;
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/unmount/manifest.json b/chrome/test/data/extensions/api_test/file_system_provider/unmount/manifest.json
new file mode 100644
index 0000000..0988ace
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/unmount/manifest.json
@@ -0,0 +1,13 @@
+{
+ "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQDOuXEIuoK1kAkBe0SKiJn/N9oNn3oUxGa4dwj40MnJqPn+w0aR2vuyocm0R4Drp67aYwtLjOVPF4CICRq6ICP6eU07gGwQxGdZ7HJASXV8hm0tab5I70oJmRLfFJyVAMCeWlFaOGq05v2i6EbifZM0qO5xALKNGQt+yjXi5INM5wIBIw==",
+ "name": "chrome.fileSystemProvider.unmount",
+ "version": "0.1",
+ "manifest_version": 2,
+ "description": "Test for chrome.fileSystemProvider.unmount().",
+ "permissions": ["fileSystemProvider", "fileBrowserPrivate"],
+ "app": {
+ "background": {
+ "scripts": ["test.js"]
+ }
+ }
+}
diff --git a/chrome/test/data/extensions/api_test/file_system_provider/unmount/test.js b/chrome/test/data/extensions/api_test/file_system_provider/unmount/test.js
new file mode 100644
index 0000000..d09dcdb
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/file_system_provider/unmount/test.js
@@ -0,0 +1,123 @@
+// Copyright 2014 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.
+
+var firstFileSystemId;
+var secondFileSystemId;
+
+function setUp(callback) {
+ chrome.fileSystemProvider.mount('chocolate.zip', function(id) {
+ firstFileSystemId = id;
+ if (firstFileSystemId && secondFileSystemId)
+ callback();
+ }, function() {
+ chrome.test.fail();
+ });
+
+ chrome.fileSystemProvider.mount('banana.zip', function(id) {
+ secondFileSystemId = id;
+ if (firstFileSystemId && secondFileSystemId)
+ callback();
+ }, function() {
+ chrome.test.fail();
+ });
+}
+
+function runTests() {
+ chrome.test.runTests([
+ // Tests the fileSystemProvider.unmount(). Verifies if the unmount event
+ // is emitted by VolumeManager.
+ function unmount() {
+ var firstVolumeId =
+ 'provided:' + chrome.runtime.id + '-' + firstFileSystemId + '-user';
+
+ var onMountCompleted = function(event) {
+ chrome.test.assertEq('unmount', event.eventType);
+ chrome.test.assertEq('success', event.status);
+ chrome.test.assertEq(firstVolumeId, event.volumeMetadata.volumeId);
+ chrome.fileBrowserPrivate.onMountCompleted.removeListener(
+ onMountCompleted);
+ chrome.test.succeed();
+ };
+
+ chrome.fileBrowserPrivate.onMountCompleted.addListener(
+ onMountCompleted);
+ chrome.fileSystemProvider.unmount(firstFileSystemId, function() {
+ // Wait for the unmount event.
+ }, function(error) {
+ chrome.test.fail();
+ });
+ },
+
+ // Tests the fileSystemProvider.unmount() with a wrong id. Verifies that
+ // it fails with a correct error code.
+ function unmountWrongId() {
+ chrome.fileSystemProvider.unmount(1337, function() {
+ chrome.test.fail();
+ }, function(error) {
+ chrome.test.assertEq('SecurityError', error.name);
+ chrome.test.succeed();
+ });
+ },
+
+ // Tests if fileBrowserPrivate.removeMount() for provided file systems emits
+ // the onMountRequested() event with correct arguments.
+ function requestUnmountSuccess() {
+ var secondVolumeId =
+ 'provided:' + chrome.runtime.id + '-' + secondFileSystemId + '-user';
+
+ var onUnmountRequested = function(fileSystemId, onSuccess, onError) {
+ chrome.test.assertEq(secondFileSystemId, fileSystemId);
+ onSuccess();
+ // Not calling fileSystemProvider.unmount(), so the onMountCompleted
+ // event will not be raised.
+ chrome.fileSystemProvider.onUnmountRequested.removeListener(
+ onUnmountRequested);
+ chrome.test.succeed();
+ };
+
+ chrome.fileSystemProvider.onUnmountRequested.addListener(
+ onUnmountRequested);
+ chrome.fileBrowserPrivate.removeMount(secondVolumeId);
+ },
+
+ // End to end test with a failure. Invokes fileSystemProvider.removeMount()
+ // on a provided file system, and verifies (1) if the onMountRequested()
+ // event is called with correct aguments, and (2) if calling onError(),
+ // results in an unmount event fired from the VolumeManager instance.
+ function requestUnmountError() {
+ var secondVolumeId =
+ 'provided:' + chrome.runtime.id + '-' + secondFileSystemId + '-user';
+ var unmountRequested = false;
+
+ var onUnmountRequested = function(fileSystemId, onSuccess, onError) {
+ chrome.test.assertEq(false, unmountRequested);
+ chrome.test.assertEq(secondFileSystemId, fileSystemId);
+ onError('IN_USE'); // enum ProviderError.
+ unmountRequested = true;
+ chrome.fileSystemProvider.onUnmountRequested.removeListener(
+ onUnmountRequested);
+ };
+
+ var onMountCompleted = function(event) {
+ chrome.test.assertEq('unmount', event.eventType);
+ chrome.test.assertEq('error_unknown', event.status);
+ chrome.test.assertEq(secondVolumeId, event.volumeMetadata.volumeId);
+ chrome.test.assertTrue(unmountRequested);
+
+ // Remove the handlers and mark the test as succeeded.
+ chrome.fileBrowserPrivate.removeMount(secondVolumeId);
+ chrome.fileBrowserPrivate.onMountCompleted.removeListener(
+ onMountCompleted);
+ chrome.test.succeed();
+ };
+
+ chrome.fileSystemProvider.onUnmountRequested.addListener(
+ onUnmountRequested);
+ chrome.fileBrowserPrivate.onMountCompleted.addListener(onMountCompleted);
+ chrome.fileBrowserPrivate.removeMount(secondVolumeId);
+ }
+ ]);
+};
+
+setUp(runTests);
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index ec2a0ca..63197b2 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -774,6 +774,9 @@ enum HistogramValue {
BLUETOOTH_SET_SOCKET_PAUSED,
BLUETOOTH_GET_SOCKET,
BLUETOOTH_GET_SOCKETS,
+ FILESYSTEMPROVIDER_UNMOUNT,
+ FILESYSTEMPROVIDERINTERNAL_UNMOUNTREQUESTEDSUCCESS,
+ FILESYSTEMPROVIDERINTERNAL_UNMOUNTREQUESTEDERROR,
// Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms/histograms.xml.
ENUM_BOUNDARY
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 50f5327..0b72d65 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -33250,6 +33250,15 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="707" label="BLUETOOTHPRIVATE_ENABLEPAIRING"/>
<int value="708" label="BLUETOOTHPRIVATE_DISABLEPAIRING"/>
<int value="709" label="BLUETOOTHPRIVATE_SETPAIRINGRESPONSE"/>
+ <int value="710" label="NETWORKINGPRIVATE_GETCAPTIVEPORTALSTATUS"/>
+ <int value="711" label="AUTOMATIONINTERNAL_PERFORMACTION"/>
+ <int value="712" label="BLUETOOTH_UPDATE_SOCKET"/>
+ <int value="713" label="BLUETOOTH_SET_SOCKET_PAUSED"/>
+ <int value="714" label="BLUETOOTH_GET_SOCKET"/>
+ <int value="715" label="BLUETOOTH_GET_SOCKETS"/>
+ <int value="716" label="FILESYSTEMPROVIDER_UNMOUNT"/>
+ <int value="717" label="FILESYSTEMPROVIDERINTERNAL_UNMOUNTREQUESTEDSUCCESS"/>
+ <int value="718" label="FILESYSTEMPROVIDERINTERNAL_UNMOUNTREQUESTEDERROR"/>
</enum>
<enum name="ExtensionInstallCause" type="int">