blob: a16659527891072c9ee0fd20e66c9dffeed90bf0 [file] [log] [blame]
// Copyright 2016 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_ARC_FILEAPI_ARC_DOCUMENTS_PROVIDER_ROOT_H_
#define CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_DOCUMENTS_PROVIDER_ROOT_H_
#include <stdint.h>
#include <map>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/arc/fileapi/arc_file_system_operation_runner.h"
#include "components/arc/mojom/file_system.mojom.h"
#include "storage/browser/fileapi/async_file_util.h"
#include "storage/browser/fileapi/watcher_manager.h"
class GURL;
namespace arc {
// Represents a file system root in Android Documents Provider.
//
// All methods must be called on the UI thread.
// If this object is deleted while there are in-flight operations, callbacks
// for those operations will be never called.
class ArcDocumentsProviderRoot : public ArcFileSystemOperationRunner::Observer {
public:
struct ThinFileInfo {
base::FilePath::StringType name;
std::string document_id;
bool is_directory;
base::Time last_modified;
};
// Extra metadata about write capabilities. All fields are false on read-only
// roots.
struct ExtraFileMetadata {
// True if a document is deletable.
bool supports_delete;
// True if a document can be renamed.
bool supports_rename;
// True if a document is a directory that supports creation of new files
// within it.
bool dir_supports_create;
};
// TODO(crbug.com/755451): Use OnceCallback/RepeatingCallback.
using GetFileInfoCallback = storage::AsyncFileUtil::GetFileInfoCallback;
using StatusCallback = storage::AsyncFileUtil::StatusCallback;
using ReadDirectoryCallback =
base::OnceCallback<void(base::File::Error error,
std::vector<ThinFileInfo> files)>;
using ChangeType = storage::WatcherManager::ChangeType;
using WatcherNotificationCallback =
storage::WatcherManager::NotificationCallback;
using WatcherStatusCallback = storage::WatcherManager::StatusCallback;
using ResolveToContentUrlCallback =
base::OnceCallback<void(const GURL& content_url)>;
using GetMetadataCallback =
base::OnceCallback<void(base::File::Error error,
const ExtraFileMetadata& metadata)>;
ArcDocumentsProviderRoot(ArcFileSystemOperationRunner* runner,
const std::string& authority,
const std::string& root_document_id,
const std::string& root_id,
bool read_only,
const std::vector<std::string>& mime_types);
~ArcDocumentsProviderRoot() override;
// Queries information of a file just like AsyncFileUtil.GetFileInfo().
void GetFileInfo(const base::FilePath& path, GetFileInfoCallback callback);
// Queries a list of files under a directory just like
// AsyncFileUtil.ReadDirectory().
void ReadDirectory(const base::FilePath& path,
ReadDirectoryCallback callback);
// Deletes a file/directory at the given path.
void DeleteFile(const base::FilePath& path, StatusCallback callback);
// Creates a file at the given path.
//
// This reports following error code via |callback|:
// - File::FILE_ERROR_NOT_FOUND if |path|'s parent directory does not exist.
// - File::FILE_ERROR_EXISTS if a file already exists at |path|.
void CreateFile(const base::FilePath& path, StatusCallback callback);
// Creates a directory at the given path.
//
// This reports following error code via |callback|:
// - File::FILE_ERROR_NOT_FOUND if |path|'s parent directory does not exist.
// - File::FILE_ERROR_EXISTS if a file already exists at |path|.
void CreateDirectory(const base::FilePath& path, StatusCallback callback);
// Copies a file from |src_path| to |dest_path| inside this root.
//
// This reports following error code via |callback|:
// - File::FILE_ERROR_NOT_FOUND if |src_path| or the parent directory of
// |dest_path| does not exist.
void CopyFileLocal(const base::FilePath& src_path,
const base::FilePath& dest_path,
StatusCallback callback);
// Moves a file from |src_path| to |dest_path| inside this root.
//
// This reports following error code via |callback|:
// - File::FILE_ERROR_NOT_FOUND if |src_path| or the parent directory of
// |dest_path| does not exist.
void MoveFileLocal(const base::FilePath& src_path,
const base::FilePath& dest_path,
StatusCallback callback);
// Installs a document watcher.
//
// It is not allowed to install multiple watchers at the same file path;
// if attempted, duplicated requests will fail.
//
// Currently, watchers can be installed only on directories, and only
// directory content changes are notified. The result of installing a watcher
// to a non-directory in unspecified.
//
// NOTES ABOUT CORRECTNESS AND CONSISTENCY:
//
// Document watchers are not always correct and they may miss some updates or
// even notify incorrect update events for several reasons, such as:
//
// - Directory moves: Currently a watcher will misbehave if the watched
// directory is moved to another location.
// TODO(fukino): Handle the case when a watched directory is moved or
// renamed.
// - Duplicated file name handling in this class: The same reason as
// directory moves. This may happen even with MediaDocumentsProvider,
// but the chance will not be very high.
// - File system operation races: For example, an watcher can be installed
// to a non-directory in a race condition.
// - Broken DocumentsProviders: For example, we get no notification if a
// document provider does not call setNotificationUri().
//
// However, consistency of installed watchers is guaranteed. That is, after
// a watcher is installed on a file path X, an attempt to uninstall a watcher
// at X will always succeed.
//
// Unfortunately it is too difficult (or maybe theoretically impossible) to
// implement a perfect Android document watcher which never misses document
// updates. So the current implementation gives up correctness, but instead
// focuses on following two goals:
//
// 1. Keep the implementation simple, rather than trying hard to catch
// race conditions or minor cases. Even if we return wrong results, the
// worst consequence is just that users do not see the latest contents
// until they refresh UI.
//
// 2. Keep consistency of installed watchers so that the caller can avoid
// dangling watchers.
void AddWatcher(const base::FilePath& path,
const WatcherNotificationCallback& watcher_callback,
const WatcherStatusCallback& callback);
// Uninstalls a document watcher.
// See the documentation of AddWatcher() above.
void RemoveWatcher(const base::FilePath& path,
const WatcherStatusCallback& callback);
// Resolves a file path into a content:// URL pointing to the file
// on DocumentsProvider. Returns URL that can be passed to
// ArcContentFileSystemFileSystemReader to read the content.
// On errors, an invalid GURL is returned.
void ResolveToContentUrl(const base::FilePath& path,
ResolveToContentUrlCallback callback);
// Get extra metadata of the file at |path|.
// The metadata is about capatility of write operations.
// See ExtraFileMetadata for the supported capabilities.
void GetMetadata(const base::FilePath& path, GetMetadataCallback callback);
// Instructs to make directory caches expire "soon" after callbacks are
// called, that is, when the message loop gets idle.
void SetDirectoryCacheExpireSoonForTesting();
// ArcFileSystemOperationRunner::Observer overrides:
void OnWatchersCleared() override;
private:
struct WatcherData;
struct DirectoryCache;
static const int64_t kInvalidWatcherId;
static const uint64_t kInvalidWatcherRequestId;
static const WatcherData kInvalidWatcherData;
// Mapping from a file name to a Document.
using NameToDocumentMap =
std::map<base::FilePath::StringType, mojom::DocumentPtr>;
using ResolveToDocumentIdCallback =
base::OnceCallback<void(const std::string& document_id)>;
using ReadDirectoryInternalCallback =
base::OnceCallback<void(base::File::Error error,
const NameToDocumentMap& mapping)>;
void GetFileInfoWithParentDocumentId(GetFileInfoCallback callback,
const base::FilePath& basename,
const std::string& parent_document_id);
void GetFileInfoWithNameToDocumentMap(GetFileInfoCallback callback,
const base::FilePath& basename,
base::File::Error error,
const NameToDocumentMap& mapping);
void ReadDirectoryWithDocumentId(ReadDirectoryCallback callback,
const std::string& document_id);
void ReadDirectoryWithNameToDocumentMap(ReadDirectoryCallback callback,
base::File::Error error,
const NameToDocumentMap& mapping);
void DeleteFileWithDocumentId(StatusCallback callback,
const std::string& document_id);
void OnFileDeleted(StatusCallback callback, bool success);
void CreateFileAfterConflictCheck(StatusCallback callback,
const base::FilePath& path,
const std::string& document_id);
void CreateFileWithParentDocumentId(StatusCallback callback,
const base::FilePath& basename,
const std::string& parent_document_id);
void CreateDirectoryAfterConflictCheck(StatusCallback callback,
const base::FilePath& path,
const std::string& document_id);
void CreateDirectoryWithParentDocumentId(
StatusCallback callback,
const base::FilePath& basename,
const std::string& parent_document_id);
void OnFileCreated(StatusCallback callback,
const std::string& parent_document_id,
mojom::DocumentPtr maybe_document);
void RenameFileInternal(const base::FilePath& path,
const std::string& display_name,
StatusCallback callback);
void RenameFileWithDocumentId(StatusCallback callback,
const std::string& display_name,
const std::string& documentId);
void OnFileRenamed(StatusCallback callback, mojom::DocumentPtr document);
void CopyFileWithSourceDocumentId(StatusCallback callback,
const base::FilePath& target_path,
const std::string& source_display_name,
const std::string& source_document_id);
void CopyFileWithTargetParentDocumentId(
StatusCallback callback,
const std::string& source_document_id,
const std::string& target_display_name_to_rename,
const std::string& target_parent_document_id);
void OnFileCopied(StatusCallback callback,
const std::string& target_display_name_to_rename,
mojom::DocumentPtr document);
void MoveFileInternal(const base::FilePath& source_path,
const base::FilePath& target_path,
StatusCallback callback);
void MoveFileWithSourceDocumentId(StatusCallback callback,
const base::FilePath& source_parent_path,
const base::FilePath& target_path,
const std::string& source_display_name,
const std::string& source_document_id);
void MoveFileWithSourceParentDocumentId(
StatusCallback callback,
const std::string& source_document_id,
const base::FilePath& target_path,
const std::string& source_display_name,
const std::string& source_parent_document_id);
void MoveFileWithTargetParentDocumentId(
StatusCallback callback,
const std::string& source_document_id,
const std::string& source_parent_document_id,
const std::string& target_display_name_to_rename,
const std::string& target_parent_document_id);
void OnFileMoved(StatusCallback callback,
const std::string& target_display_name_to_rename,
mojom::DocumentPtr document);
void AddWatcherWithDocumentId(
const base::FilePath& path,
uint64_t watcher_request_id,
const WatcherNotificationCallback& watcher_callback,
const std::string& document_id);
void OnWatcherAdded(const base::FilePath& path,
uint64_t watcher_request_id,
int64_t watcher_id);
void OnWatcherAddedButRemoved(bool success);
void OnWatcherRemoved(const WatcherStatusCallback& callback, bool success);
// Returns true if the specified watcher request has been canceled.
// This function should be called only while the request is in-flight.
bool IsWatcherInflightRequestCanceled(const base::FilePath& path,
uint64_t watcher_request_id) const;
void ResolveToContentUrlWithDocumentId(ResolveToContentUrlCallback callback,
const std::string& document_id);
void GetMetadataWithDocumentId(GetMetadataCallback callback,
const std::string& document_id);
void OnMetadataGotten(GetMetadataCallback callback,
mojom::DocumentPtr document);
// Resolves |path| to a document ID. Failures are indicated by an empty
// document ID.
void ResolveToDocumentId(const base::FilePath& path,
ResolveToDocumentIdCallback callback);
void ResolveToDocumentIdRecursively(
const std::string& document_id,
const std::vector<base::FilePath::StringType>& components,
ResolveToDocumentIdCallback callback);
void ResolveToDocumentIdRecursivelyWithNameToDocumentMap(
const std::vector<base::FilePath::StringType>& components,
ResolveToDocumentIdCallback callback,
base::File::Error error,
const NameToDocumentMap& mapping);
// Enumerates child documents of a directory specified by |document_id|.
// If |force_refresh| is true, the backend is queried even if there is a
// directory cache.
// The result is returned as a NameToDocumentMap. It is valid only within
// the callback and might get deleted immediately after the callback
// returns.
void ReadDirectoryInternal(const std::string& document_id,
bool force_refresh,
ReadDirectoryInternalCallback callback);
void ReadDirectoryInternalWithChildDocuments(
const std::string& document_id,
base::Optional<std::vector<mojom::DocumentPtr>> maybe_children);
// Clears a directory cache.
void ClearDirectoryCache(const std::string& document_id);
// |runner_| outlives this object. ArcDocumentsProviderRootMap, the owner of
// this object, depends on ArcFileSystemOperationRunner in the
// BrowserContextKeyedServiceFactory dependency graph.
ArcFileSystemOperationRunner* const runner_;
const std::string authority_;
const std::string root_document_id_;
const std::string root_id_;
const bool read_only_;
const std::vector<std::string> mime_types_;
bool directory_cache_expire_soon_ = false;
// Cache of directory contents. Keys are document IDs of directories.
std::map<std::string, DirectoryCache> directory_cache_;
// Map from a document ID to callbacks pending for ReadDirectoryInternal()
// calls.
std::map<std::string, std::vector<ReadDirectoryInternalCallback>>
pending_callbacks_map_;
// Map from a file path to a watcher data.
//
// Note that we do not use a document ID as a key here to guarantee that
// a watch installed by AddWatcher() can be always identified in
// RemoveWatcher() with the same file path specified.
// See the documentation of AddWatcher() for more details.
std::map<base::FilePath, WatcherData> path_to_watcher_data_;
uint64_t next_watcher_request_id_ = 1;
base::WeakPtrFactory<ArcDocumentsProviderRoot> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(ArcDocumentsProviderRoot);
};
} // namespace arc
#endif // CHROME_BROWSER_CHROMEOS_ARC_FILEAPI_ARC_DOCUMENTS_PROVIDER_ROOT_H_