diff options
author | erg <erg@chromium.org> | 2015-07-15 11:30:52 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-15 18:31:34 +0000 |
commit | e3ec7c45bd64ef826fd3940c8a2302f5b55edc45 (patch) | |
tree | ccf427506bb9dcc2140c0129e3913101ee1a40bc /components | |
parent | fe391dde31e7dbe0644953bea9061e7779a25f6c (diff) | |
download | chromium_src-e3ec7c45bd64ef826fd3940c8a2302f5b55edc45.zip chromium_src-e3ec7c45bd64ef826fd3940c8a2302f5b55edc45.tar.gz chromium_src-e3ec7c45bd64ef826fd3940c8a2302f5b55edc45.tar.bz2 |
mandoline filesystem: Reland "Save cookie data to the mojo:filesystem."
In addition to saving the cookies to the file system service (per the
previous patch), we also modify the filesystem service to take a
client. When the shell connection to the file system service is broken,
instead of quitting immediately, we broadcast a shutdown notification to
all of our clients, and wait for them to close the directory objects.
This solves the timing problems during shutdown.
BUG=493311
TBR=shess@chromium.org
R=jam@chromium.org,erikwright@chromium.org
First Review URL: https://codereview.chromium.org/1179413010
Review URL: https://codereview.chromium.org/1231493002
Cr-Commit-Position: refs/heads/master@{#338888}
Diffstat (limited to 'components')
-rw-r--r-- | components/filesystem/directory_impl.h | 4 | ||||
-rw-r--r-- | components/filesystem/file_system_app.cc | 75 | ||||
-rw-r--r-- | components/filesystem/file_system_app.h | 37 | ||||
-rw-r--r-- | components/filesystem/file_system_impl.cc | 120 | ||||
-rw-r--r-- | components/filesystem/file_system_impl.h | 27 | ||||
-rw-r--r-- | components/filesystem/files_test_base.cc | 11 | ||||
-rw-r--r-- | components/filesystem/files_test_base.h | 8 | ||||
-rw-r--r-- | components/filesystem/public/interfaces/file_system.mojom | 15 |
8 files changed, 282 insertions, 15 deletions
diff --git a/components/filesystem/directory_impl.h b/components/filesystem/directory_impl.h index ed19d00..b016b83 100644 --- a/components/filesystem/directory_impl.h +++ b/components/filesystem/directory_impl.h @@ -28,6 +28,10 @@ class DirectoryImpl : public Directory { scoped_ptr<base::ScopedTempDir> temp_dir); ~DirectoryImpl() override; + void set_connection_error_handler(const mojo::Closure& error_handler) { + binding_.set_connection_error_handler(error_handler); + } + // |Directory| implementation: void Read(const ReadCallback& callback) override; void OpenFile(const mojo::String& path, diff --git a/components/filesystem/file_system_app.cc b/components/filesystem/file_system_app.cc index d69df2e..c1e3e16 100644 --- a/components/filesystem/file_system_app.cc +++ b/components/filesystem/file_system_app.cc @@ -4,24 +4,95 @@ #include "components/filesystem/file_system_app.h" +#include "base/bind.h" +#include "base/logging.h" #include "mojo/application/public/cpp/application_connection.h" +#include "mojo/application/public/cpp/application_impl.h" namespace filesystem { -FileSystemApp::FileSystemApp() {} +FileSystemApp::FileSystemApp() : app_(nullptr), in_shutdown_(false) {} FileSystemApp::~FileSystemApp() {} +void FileSystemApp::Initialize(mojo::ApplicationImpl* app) { + app_ = app; +} + bool FileSystemApp::ConfigureIncomingConnection( mojo::ApplicationConnection* connection) { connection->AddService<FileSystem>(this); return true; } +void FileSystemApp::RegisterDirectoryToClient(DirectoryImpl* directory, + FileSystemClientPtr client) { + directory->set_connection_error_handler( + base::Bind(&FileSystemApp::OnDirectoryConnectionError, + base::Unretained(this), + directory)); + client_mapping_.emplace_back(directory, client.Pass()); +} + +bool FileSystemApp::OnShellConnectionError() { + if (client_mapping_.empty()) { + // If we have no current connections, we can shutdown immediately. + return true; + } + + in_shutdown_ = true; + + // We have live connections. Send a notification to each one indicating that + // they should shutdown. + for (std::vector<Client>::iterator it = client_mapping_.begin(); + it != client_mapping_.end(); ++it) { + it->fs_client_->OnFileSystemShutdown(); + } + + return false; +} + // |InterfaceFactory<Files>| implementation: void FileSystemApp::Create(mojo::ApplicationConnection* connection, mojo::InterfaceRequest<FileSystem> request) { - new FileSystemImpl(connection, request.Pass()); + new FileSystemImpl(this, connection, request.Pass()); +} + +void FileSystemApp::OnDirectoryConnectionError(DirectoryImpl* directory) { + for (std::vector<Client>::iterator it = client_mapping_.begin(); + it != client_mapping_.end(); ++it) { + if (it->directory_ == directory) { + client_mapping_.erase(it); + + if (in_shutdown_ && client_mapping_.empty()) { + // We just cleared the last directory after our shell connection went + // away. Time to shut ourselves down. + app_->QuitNow(); + } + + return; + } + } +} + +FileSystemApp::Client::Client(DirectoryImpl* directory, + FileSystemClientPtr fs_client) + : directory_(directory), + fs_client_(fs_client.Pass()) { +} + +FileSystemApp::Client::Client(Client&& rhs) + : directory_(rhs.directory_), + fs_client_(rhs.fs_client_.Pass()) { +} + +FileSystemApp::Client::~Client() {} + +FileSystemApp::Client& FileSystemApp::Client::operator=( + FileSystemApp::Client&& rhs) { + directory_ = rhs.directory_; + fs_client_ = rhs.fs_client_.Pass(); + return *this; } } // namespace filesystem diff --git a/components/filesystem/file_system_app.h b/components/filesystem/file_system_app.h index c636c54..56e33af 100644 --- a/components/filesystem/file_system_app.h +++ b/components/filesystem/file_system_app.h @@ -6,11 +6,16 @@ #define COMPONENTS_FILESYSTEM_FILE_SYSTEM_APP_H_ #include "base/macros.h" +#include "components/filesystem/directory_impl.h" #include "components/filesystem/file_system_impl.h" #include "components/filesystem/public/interfaces/file_system.mojom.h" #include "mojo/application/public/cpp/application_delegate.h" #include "mojo/application/public/cpp/interface_factory.h" +namespace mojo { +class ApplicationImpl; +} + namespace filesystem { class FileSystemApp : public mojo::ApplicationDelegate, @@ -19,15 +24,47 @@ class FileSystemApp : public mojo::ApplicationDelegate, FileSystemApp(); ~FileSystemApp() override; + // Called by individual FileSystem objects to register lifetime events. + void RegisterDirectoryToClient(DirectoryImpl* directory, + FileSystemClientPtr client); + private: + // We set the DirectoryImpl's error handler to this function. We do this so + // that we can QuitNow() once the last DirectoryImpl has closed itself. + void OnDirectoryConnectionError(DirectoryImpl* directory); + // |ApplicationDelegate| override: + void Initialize(mojo::ApplicationImpl* app) override; bool ConfigureIncomingConnection( mojo::ApplicationConnection* connection) override; + bool OnShellConnectionError() override; // |InterfaceFactory<Files>| implementation: void Create(mojo::ApplicationConnection* connection, mojo::InterfaceRequest<FileSystem> request) override; + // Use a vector to work around map not letting us use FileSystemClientPtr as + // a value in a std::map. The move constructors are to allow us to deal with + // FileSystemClientPtr inside a vector. + struct Client { + Client(DirectoryImpl* directory, FileSystemClientPtr fs_client); + Client(Client&& rhs); + ~Client(); + + Client& operator=(Client&& rhs); + + DirectoryImpl* directory_; + FileSystemClientPtr fs_client_; + }; + std::vector<Client> client_mapping_; + + mojo::ApplicationImpl* app_; + + // Set to true when our shell connection is closed. On connection error, we + // then broadcast a notification to all FileSystemClients that they should + // shut down. Once the final one does, then we QuitNow(). + bool in_shutdown_; + DISALLOW_COPY_AND_ASSIGN(FileSystemApp); }; diff --git a/components/filesystem/file_system_impl.cc b/components/filesystem/file_system_impl.cc index cfd4cac..7dfe5df 100644 --- a/components/filesystem/file_system_impl.cc +++ b/components/filesystem/file_system_impl.cc @@ -4,19 +4,48 @@ #include "components/filesystem/file_system_impl.h" +#include "base/command_line.h" #include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/files/scoped_file.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "components/filesystem/directory_impl.h" +#include "components/filesystem/file_system_app.h" #include "mojo/application/public/cpp/application_connection.h" +#include "url/gurl.h" + +#if defined(OS_WIN) +#include "base/base_paths_win.h" +#include "base/path_service.h" +#include "base/strings/utf_string_conversions.h" +#elif defined(OS_ANDROID) +#include "base/base_paths_android.h" +#include "base/path_service.h" +#elif defined(OS_LINUX) +#include "base/environment.h" +#include "base/nix/xdg_util.h" +#elif defined(OS_MACOSX) +#include "base/base_paths_mac.h" +#include "base/path_service.h" +#endif namespace filesystem { -FileSystemImpl::FileSystemImpl(mojo::ApplicationConnection* connection, +namespace { + +const char kEscapeChar = ','; + +const char kUserDataDir[] = "user-data-dir"; + +} // namespace filesystem + +FileSystemImpl::FileSystemImpl(FileSystemApp* app, + mojo::ApplicationConnection* connection, mojo::InterfaceRequest<FileSystem> request) - : remote_application_url_(connection->GetRemoteApplicationURL()), + : app_(app), + remote_application_url_(connection->GetRemoteApplicationURL()), binding_(this, request.Pass()) { } @@ -25,6 +54,7 @@ FileSystemImpl::~FileSystemImpl() { void FileSystemImpl::OpenFileSystem(const mojo::String& file_system, mojo::InterfaceRequest<Directory> directory, + FileSystemClientPtr client, const OpenFileSystemCallback& callback) { // Set only if the |DirectoryImpl| will own a temporary directory. scoped_ptr<base::ScopedTempDir> temp_dir; @@ -34,16 +64,96 @@ void FileSystemImpl::OpenFileSystem(const mojo::String& file_system, CHECK(temp_dir->CreateUniqueTempDir()); path = temp_dir->path(); } else if (file_system.get() == std::string("origin")) { - // TODO(erg): We should serve a persistent directory based on the - // subdirectory |remote_application_url_| of a profile directory. + base::FilePath base_profile_dir = GetSystemProfileDir(); + + // Sanitize the url for disk access. + // + // TODO(erg): While it's currently impossible, we need to deal with http:// + // URLs that have a path. (Or make the decision that these file systems are + // path bound, not origin bound.) + std::string sanitized_origin; + BuildSanitizedOrigin(remote_application_url_, &sanitized_origin); + +#if defined(OS_WIN) + path = base_profile_dir.Append(base::UTF8ToWide(sanitized_origin)); +#else + path = base_profile_dir.Append(sanitized_origin); +#endif + if (!base::PathExists(path)) + base::CreateDirectory(path); } if (!path.empty()) { - new DirectoryImpl(directory.Pass(), path, temp_dir.Pass()); + DirectoryImpl* dir_impl = + new DirectoryImpl(directory.Pass(), path, temp_dir.Pass()); + app_->RegisterDirectoryToClient(dir_impl, client.Pass()); callback.Run(FILE_ERROR_OK); } else { callback.Run(FILE_ERROR_FAILED); } } +base::FilePath FileSystemImpl::GetSystemProfileDir() const { + base::FilePath path; + + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(kUserDataDir)) { + path = command_line->GetSwitchValuePath(kUserDataDir); + } else { +#if defined(OS_WIN) + CHECK(PathService::Get(base::DIR_LOCAL_APP_DATA, &path)); + path = path.Append(FILE_PATH_LITERAL("mandoline")); +#elif defined(OS_LINUX) + scoped_ptr<base::Environment> env(base::Environment::Create()); + base::FilePath config_dir( + base::nix::GetXDGDirectory(env.get(), + base::nix::kXdgConfigHomeEnvVar, + base::nix::kDotConfigDir)); + path = config_dir.Append("mandoline"); +#elif defined(OS_MACOSX) + CHECK(PathService::Get(base::DIR_APP_DATA, &path)); + path = path.Append("Mandoline Shell"); +#elif defined(OS_ANDROID) + CHECK(PathService::Get(base::DIR_ANDROID_APP_DATA, &path)); + path = path.Append(FILE_PATH_LITERAL("mandoline")); +#else + NOTIMPLEMENTED(); +#endif + } + + if (!base::PathExists(path)) + base::CreateDirectory(path); + + return path; +} + +void FileSystemImpl::BuildSanitizedOrigin( + const std::string& origin, + std::string* sanitized_origin) { + // We take the origin string, and encode it in a way safe for filesystem + // access. This is vaguely based on //net/tools/dump_cache/ + // url_to_filename_encoder.h; that file strips out schemes, and does weird + // things with subdirectories. We do follow the basic algorithm used there, + // including using ',' as our escape character. + for (size_t i = 0; i < origin.length(); ++i) { + unsigned char ch = origin[i]; + char encoded[3]; + int encoded_len; + if ((ch == '_') || (ch == '.') || (ch == '=') || (ch == '+') || + (ch == '-') || (('0' <= ch) && (ch <= '9')) || + (('A' <= ch) && (ch <= 'Z')) || (('a' <= ch) && (ch <= 'z'))) { + encoded[0] = ch; + encoded_len = 1; + } else { + encoded[0] = kEscapeChar; + encoded[1] = ch / 16; + encoded[1] += (encoded[1] >= 10) ? 'A' - 10 : '0'; + encoded[2] = ch % 16; + encoded[2] += (encoded[2] >= 10) ? 'A' - 10 : '0'; + encoded_len = 3; + } + sanitized_origin->append(encoded, encoded_len); + } +} + } // namespace filesystem diff --git a/components/filesystem/file_system_impl.h b/components/filesystem/file_system_impl.h index 90ea77d..2cda44d 100644 --- a/components/filesystem/file_system_impl.h +++ b/components/filesystem/file_system_impl.h @@ -10,27 +10,46 @@ #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/strong_binding.h" +namespace base { +class FilePath; +} + namespace mojo { class ApplicationConnection; } namespace filesystem { +class FileSystemApp; class FileSystemImpl : public FileSystem { public: - FileSystemImpl(mojo::ApplicationConnection* connection, + FileSystemImpl(FileSystemApp* app, + mojo::ApplicationConnection* connection, mojo::InterfaceRequest<FileSystem> request); ~FileSystemImpl() override; // |Files| implementation: - // We provide a "private" temporary file system as the default. In Debug - // builds, we also provide access to a common file system named "debug" - // (stored under ~/MojoDebug). + + // Current valid values for |file_system| are "temp" for a temporary + // filesystem and "origin" for a persistent filesystem bound to the origin of + // the URL of the caller. void OpenFileSystem(const mojo::String& file_system, mojo::InterfaceRequest<Directory> directory, + FileSystemClientPtr client, const OpenFileSystemCallback& callback) override; private: + // Gets the system specific toplevel profile directory. + base::FilePath GetSystemProfileDir() const; + + // Takes the origin string from |remote_application_url_|. + std::string GetOriginFromRemoteApplicationURL() const; + + // Sanitizes |origin| so it is an acceptable filesystem name. + void BuildSanitizedOrigin(const std::string& origin, + std::string* sanitized_origin); + + FileSystemApp* app_; const std::string remote_application_url_; mojo::StrongBinding<FileSystem> binding_; diff --git a/components/filesystem/files_test_base.cc b/components/filesystem/files_test_base.cc index 47f7d61..bf7a3d0 100644 --- a/components/filesystem/files_test_base.cc +++ b/components/filesystem/files_test_base.cc @@ -11,7 +11,7 @@ namespace filesystem { -FilesTestBase::FilesTestBase() { +FilesTestBase::FilesTestBase() : binding_(this) { } FilesTestBase::~FilesTestBase() { @@ -25,9 +25,16 @@ void FilesTestBase::SetUp() { application_impl()->ConnectToService(request.Pass(), &files_); } +void FilesTestBase::OnFileSystemShutdown() { +} + void FilesTestBase::GetTemporaryRoot(DirectoryPtr* directory) { + filesystem::FileSystemClientPtr client; + binding_.Bind(GetProxy(&client)); + FileError error = FILE_ERROR_FAILED; - files()->OpenFileSystem("temp", GetProxy(directory), mojo::Capture(&error)); + files()->OpenFileSystem("temp", GetProxy(directory), client.Pass(), + mojo::Capture(&error)); ASSERT_TRUE(files().WaitForIncomingResponse()); ASSERT_EQ(FILE_ERROR_OK, error); } diff --git a/components/filesystem/files_test_base.h b/components/filesystem/files_test_base.h index bc9a9a3..5388a9e 100644 --- a/components/filesystem/files_test_base.h +++ b/components/filesystem/files_test_base.h @@ -11,13 +11,18 @@ namespace filesystem { -class FilesTestBase : public mojo::test::ApplicationTestBase { +class FilesTestBase : public mojo::test::ApplicationTestBase, + public filesystem::FileSystemClient { public: FilesTestBase(); ~FilesTestBase() override; + // Overridden from mojo::test::ApplicationTestBase: void SetUp() override; + // Overridden from FileSystemClient: + void OnFileSystemShutdown() override; + protected: // Note: This has an out parameter rather than returning the |DirectoryPtr|, // since |ASSERT_...()| doesn't work with return values. @@ -26,6 +31,7 @@ class FilesTestBase : public mojo::test::ApplicationTestBase { FileSystemPtr& files() { return files_; } private: + mojo::Binding<filesystem::FileSystemClient> binding_; FileSystemPtr files_; DISALLOW_COPY_AND_ASSIGN(FilesTestBase); diff --git a/components/filesystem/public/interfaces/file_system.mojom b/components/filesystem/public/interfaces/file_system.mojom index 2631216..7278563 100644 --- a/components/filesystem/public/interfaces/file_system.mojom +++ b/components/filesystem/public/interfaces/file_system.mojom @@ -7,8 +7,21 @@ module filesystem; import "components/filesystem/public/interfaces/directory.mojom"; import "components/filesystem/public/interfaces/types.mojom"; +// Callback interface for FileSystem. When we call OpenFileSystem, we supply a +// client to receive and handle the shutdown signal. Just because the shell has +// closed the application connection to the FileSystem doesn't mean that we +// should immediately kill all connections to our clients. We notify them that +// we are shutting down so that they can flush any data and cleanly shutdown. +// +// Actual connection lifetime is controlled by the lifetime of the |directory| +// object. +interface FileSystemClient { + OnFileSystemShutdown(); +}; + interface FileSystem { // Opens the root directory for the file system with the given name; null // yields the default file system, if any. - OpenFileSystem(string? file_system, Directory& directory) => (FileError error); + OpenFileSystem(string? file_system, Directory& directory, + FileSystemClient client) => (FileError error); }; |