// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_CONTEXT_H_
#define WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_CONTEXT_H_

#include <map>
#include <string>
#include <vector>

#include "base/callback.h"
#include "base/files/file.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/sequenced_task_runner_helpers.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/open_file_system_mode.h"
#include "webkit/browser/fileapi/plugin_private_file_system_backend.h"
#include "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
#include "webkit/browser/fileapi/task_runner_bound_observer_list.h"
#include "webkit/browser/webkit_storage_browser_export.h"
#include "webkit/common/fileapi/file_system_types.h"

namespace base {
class FilePath;
class SequencedTaskRunner;
class SingleThreadTaskRunner;
}

namespace chrome {
class NativeMediaFileUtilTest;
}

namespace quota {
class QuotaManagerProxy;
class SpecialStoragePolicy;
}

namespace net {
class URLRequest;
}

namespace webkit_blob {
class BlobURLRequestJobTest;
class FileStreamReader;
}

namespace fileapi {

class AsyncFileUtil;
class CopyOrMoveFileValidatorFactory;
class ExternalFileSystemBackend;
class ExternalMountPoints;
class FileStreamWriter;
class FileSystemBackend;
class FileSystemFileUtil;
class FileSystemOperation;
class FileSystemOperationRunner;
class FileSystemOptions;
class FileSystemQuotaUtil;
class FileSystemURL;
class IsolatedFileSystemBackend;
class MountPoints;
class QuotaReservation;
class SandboxFileSystemBackend;

struct DefaultContextDeleter;
struct FileSystemInfo;

// An auto mount handler will attempt to mount the file system requested in
// |url_request|. If the URL is for this auto mount handler, it returns true
// and calls |callback| when the attempt is complete. If the auto mounter
// does not recognize the URL, it returns false and does not call |callback|.
// Called on the IO thread.
typedef base::Callback<bool(
    const net::URLRequest* url_request,
    const FileSystemURL& filesystem_url,
    const std::string& storage_domain,
    const base::Callback<void(base::File::Error result)>& callback)>
        URLRequestAutoMountHandler;

// This class keeps and provides a file system context for FileSystem API.
// An instance of this class is created and owned by profile.
class WEBKIT_STORAGE_BROWSER_EXPORT FileSystemContext
    : public base::RefCountedThreadSafe<FileSystemContext,
                                        DefaultContextDeleter> {
 public:
  // Returns file permission policy we should apply for the given |type|.
  // The return value must be bitwise-or'd of FilePermissionPolicy.
  //
  // Note: if a part of a filesystem is returned via 'Isolated' mount point,
  // its per-filesystem permission overrides the underlying filesystem's
  // permission policy.
  static int GetPermissionPolicy(FileSystemType type);

  // file_task_runner is used as default TaskRunner.
  // Unless a FileSystemBackend is overridden in CreateFileSystemOperation,
  // it is used for all file operations and file related meta operations.
  // The code assumes that file_task_runner->RunsTasksOnCurrentThread()
  // returns false if the current task is not running on the thread that allows
  // blocking file operations (like SequencedWorkerPool implementation does).
  //
  // |external_mount_points| contains non-system external mount points available
  // in the context. If not NULL, it will be used during URL cracking.
  // |external_mount_points| may be NULL only on platforms different from
  // ChromeOS (i.e. platforms that don't use external_mount_point_provider).
  //
  // |additional_backends| are added to the internal backend map
  // to serve filesystem requests for non-regular types.
  // If none is given, this context only handles HTML5 Sandbox FileSystem
  // and Drag-and-drop Isolated FileSystem requests.
  //
  // |auto_mount_handlers| are used to resolve calls to
  // AttemptAutoMountForURLRequest. Only external filesystems are auto mounted
  // when a filesystem: URL request is made.
  FileSystemContext(
      base::SingleThreadTaskRunner* io_task_runner,
      base::SequencedTaskRunner* file_task_runner,
      ExternalMountPoints* external_mount_points,
      quota::SpecialStoragePolicy* special_storage_policy,
      quota::QuotaManagerProxy* quota_manager_proxy,
      ScopedVector<FileSystemBackend> additional_backends,
      const std::vector<URLRequestAutoMountHandler>& auto_mount_handlers,
      const base::FilePath& partition_path,
      const FileSystemOptions& options);

  bool DeleteDataForOriginOnFileTaskRunner(const GURL& origin_url);

  // Creates a new QuotaReservation for the given |origin_url| and |type|.
  // Returns NULL if |type| does not support quota or reservation fails.
  // This should be run on |default_file_task_runner_| and the returned value
  // should be destroyed on the runner.
  scoped_refptr<QuotaReservation> CreateQuotaReservationOnFileTaskRunner(
      const GURL& origin_url,
      FileSystemType type);

  quota::QuotaManagerProxy* quota_manager_proxy() const {
    return quota_manager_proxy_.get();
  }

  // Discards inflight operations in the operation runner.
  void Shutdown();

  // Returns a quota util for a given filesystem type.  This may
  // return NULL if the type does not support the usage tracking or
  // it is not a quota-managed storage.
  FileSystemQuotaUtil* GetQuotaUtil(FileSystemType type) const;

  // Returns the appropriate AsyncFileUtil instance for the given |type|.
  AsyncFileUtil* GetAsyncFileUtil(FileSystemType type) const;

  // Returns the appropriate CopyOrMoveFileValidatorFactory for the given
  // |type|.  If |error_code| is File::FILE_OK and the result is NULL,
  // then no validator is required.
  CopyOrMoveFileValidatorFactory* GetCopyOrMoveFileValidatorFactory(
      FileSystemType type, base::File::Error* error_code) const;

  // Returns the file system backend instance for the given |type|.
  // This may return NULL if it is given an invalid or unsupported filesystem
  // type.
  FileSystemBackend* GetFileSystemBackend(
      FileSystemType type) const;

  // Returns true for sandboxed filesystems. Currently this does
  // the same as GetQuotaUtil(type) != NULL. (In an assumption that
  // all sandboxed filesystems must cooperate with QuotaManager so that
  // they can get deleted)
  bool IsSandboxFileSystem(FileSystemType type) const;

  // Returns observers for the given filesystem type.
  const UpdateObserverList* GetUpdateObservers(FileSystemType type) const;
  const AccessObserverList* GetAccessObservers(FileSystemType type) const;

  // Returns all registered filesystem types.
  void GetFileSystemTypes(std::vector<FileSystemType>* types) const;

  // Returns a FileSystemBackend instance for external filesystem
  // type, which is used only by chromeos for now.  This is equivalent to
  // calling GetFileSystemBackend(kFileSystemTypeExternal).
  ExternalFileSystemBackend* external_backend() const;

  // Used for OpenFileSystem.
  typedef base::Callback<void(const GURL& root,
                              const std::string& name,
                              base::File::Error result)>
      OpenFileSystemCallback;

  // Used for ResolveURL.
  enum ResolvedEntryType {
    RESOLVED_ENTRY_FILE,
    RESOLVED_ENTRY_DIRECTORY,
    RESOLVED_ENTRY_NOT_FOUND,
  };
  typedef base::Callback<void(base::File::Error result,
                              const FileSystemInfo& info,
                              const base::FilePath& file_path,
                              ResolvedEntryType type)> ResolveURLCallback;

  // Used for DeleteFileSystem and OpenPluginPrivateFileSystem.
  typedef base::Callback<void(base::File::Error result)> StatusCallback;

  // Opens the filesystem for the given |origin_url| and |type|, and dispatches
  // |callback| on completion.
  // If |create| is true this may actually set up a filesystem instance
  // (e.g. by creating the root directory or initializing the database
  // entry etc).
  void OpenFileSystem(
      const GURL& origin_url,
      FileSystemType type,
      OpenFileSystemMode mode,
      const OpenFileSystemCallback& callback);

  // Opens the filesystem for the given |url| as read-only, if the filesystem
  // backend referred by the URL allows opening by resolveURL. Otherwise it
  // fails with FILE_ERROR_SECURITY. The entry pointed by the URL can be
  // absent; in that case RESOLVED_ENTRY_NOT_FOUND type is returned to the
  // callback for indicating the absence. Can be called from any thread with
  // a message loop. |callback| is invoked on the caller thread.
  void ResolveURL(
      const FileSystemURL& url,
      const ResolveURLCallback& callback);

  // Attempts to mount the filesystem needed to satisfy |url_request| made
  // from |storage_domain|. If an appropriate file system is not found,
  // callback will return an error.
  void AttemptAutoMountForURLRequest(const net::URLRequest* url_request,
                                     const std::string& storage_domain,
                                     const StatusCallback& callback);

  // Deletes the filesystem for the given |origin_url| and |type|. This should
  // be called on the IO thread.
  void DeleteFileSystem(
      const GURL& origin_url,
      FileSystemType type,
      const StatusCallback& callback);

  // Creates new FileStreamReader instance to read a file pointed by the given
  // filesystem URL |url| starting from |offset|. |expected_modification_time|
  // specifies the expected last modification if the value is non-null, the
  // reader will check the underlying file's actual modification time to see if
  // the file has been modified, and if it does any succeeding read operations
  // should fail with ERR_UPLOAD_FILE_CHANGED error.
  // This method internally cracks the |url|, get an appropriate
  // FileSystemBackend for the URL and call the backend's CreateFileReader.
  // The resolved FileSystemBackend could perform further specialization
  // depending on the filesystem type pointed by the |url|.
  scoped_ptr<webkit_blob::FileStreamReader> CreateFileStreamReader(
      const FileSystemURL& url,
      int64 offset,
      const base::Time& expected_modification_time);

  // Creates new FileStreamWriter instance to write into a file pointed by
  // |url| from |offset|.
  scoped_ptr<FileStreamWriter> CreateFileStreamWriter(
      const FileSystemURL& url,
      int64 offset);

  // Creates a new FileSystemOperationRunner.
  scoped_ptr<FileSystemOperationRunner> CreateFileSystemOperationRunner();

  base::SequencedTaskRunner* default_file_task_runner() {
    return default_file_task_runner_.get();
  }

  FileSystemOperationRunner* operation_runner() {
    return operation_runner_.get();
  }

  const base::FilePath& partition_path() const { return partition_path_; }

  // Same as |CrackFileSystemURL|, but cracks FileSystemURL created from |url|.
  FileSystemURL CrackURL(const GURL& url) const;
  // Same as |CrackFileSystemURL|, but cracks FileSystemURL created from method
  // arguments.
  FileSystemURL CreateCrackedFileSystemURL(const GURL& origin,
                                           FileSystemType type,
                                           const base::FilePath& path) const;

#if defined(OS_CHROMEOS)
  // Used only on ChromeOS for now.
  void EnableTemporaryFileSystemInIncognito();
#endif

  SandboxFileSystemBackendDelegate* sandbox_delegate() {
    return sandbox_delegate_.get();
  }

  // Returns true if the requested url is ok to be served.
  // (E.g. this returns false if the context is created for incognito mode)
  bool CanServeURLRequest(const FileSystemURL& url) const;

  // Returns true if a file in the file system should be flushed for each write
  // completion.
  bool ShouldFlushOnWriteCompletion(FileSystemType type) const;

  // This must be used to open 'plugin private' filesystem.
  // See "plugin_private_file_system_backend.h" for more details.
  void OpenPluginPrivateFileSystem(
      const GURL& origin_url,
      FileSystemType type,
      const std::string& filesystem_id,
      const std::string& plugin_id,
      OpenFileSystemMode mode,
      const StatusCallback& callback);

 private:
  typedef std::map<FileSystemType, FileSystemBackend*>
      FileSystemBackendMap;

  // For CreateFileSystemOperation.
  friend class FileSystemOperationRunner;

  // For sandbox_backend().
  friend class content::SandboxFileSystemTestHelper;

  // For plugin_private_backend().
  friend class content::PluginPrivateFileSystemBackendTest;

  // Deleters.
  friend struct DefaultContextDeleter;
  friend class base::DeleteHelper<FileSystemContext>;
  friend class base::RefCountedThreadSafe<FileSystemContext,
                                          DefaultContextDeleter>;
  ~FileSystemContext();

  void DeleteOnCorrectThread() const;

  // Creates a new FileSystemOperation instance by getting an appropriate
  // FileSystemBackend for |url| and calling the backend's corresponding
  // CreateFileSystemOperation method.
  // The resolved FileSystemBackend could perform further specialization
  // depending on the filesystem type pointed by the |url|.
  //
  // Called by FileSystemOperationRunner.
  FileSystemOperation* CreateFileSystemOperation(
      const FileSystemURL& url,
      base::File::Error* error_code);

  // For non-cracked isolated and external mount points, returns a FileSystemURL
  // created by cracking |url|. The url is cracked using MountPoints registered
  // as |url_crackers_|. If the url cannot be cracked, returns invalid
  // FileSystemURL.
  //
  // If the original url does not point to an isolated or external filesystem,
  // returns the original url, without attempting to crack it.
  FileSystemURL CrackFileSystemURL(const FileSystemURL& url) const;

  // For initial backend_map construction. This must be called only from
  // the constructor.
  void RegisterBackend(FileSystemBackend* backend);

  void DidOpenFileSystemForResolveURL(
      const FileSystemURL& url,
      const ResolveURLCallback& callback,
      const GURL& filesystem_root,
      const std::string& filesystem_name,
      base::File::Error error);

  // Returns a FileSystemBackend, used only by test code.
  SandboxFileSystemBackend* sandbox_backend() const {
    return sandbox_backend_.get();
  }

  // Used only by test code.
  PluginPrivateFileSystemBackend* plugin_private_backend() const {
    return plugin_private_backend_.get();
  }

  scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
  scoped_refptr<base::SequencedTaskRunner> default_file_task_runner_;

  scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_;

  scoped_ptr<SandboxFileSystemBackendDelegate> sandbox_delegate_;

  // Regular file system backends.
  scoped_ptr<SandboxFileSystemBackend> sandbox_backend_;
  scoped_ptr<IsolatedFileSystemBackend> isolated_backend_;

  // Additional file system backends.
  scoped_ptr<PluginPrivateFileSystemBackend> plugin_private_backend_;
  ScopedVector<FileSystemBackend> additional_backends_;

  std::vector<URLRequestAutoMountHandler> auto_mount_handlers_;

  // Registered file system backends.
  // The map must be constructed in the constructor since it can be accessed
  // on multiple threads.
  // This map itself doesn't retain each backend's ownership; ownerships
  // of the backends are held by additional_backends_ or other scoped_ptr
  // backend fields.
  FileSystemBackendMap backend_map_;

  // External mount points visible in the file system context (excluding system
  // external mount points).
  scoped_refptr<ExternalMountPoints> external_mount_points_;

  // MountPoints used to crack FileSystemURLs. The MountPoints are ordered
  // in order they should try to crack a FileSystemURL.
  std::vector<MountPoints*> url_crackers_;

  // The base path of the storage partition for this context.
  const base::FilePath partition_path_;

  bool is_incognito_;

  scoped_ptr<FileSystemOperationRunner> operation_runner_;

  DISALLOW_IMPLICIT_CONSTRUCTORS(FileSystemContext);
};

struct DefaultContextDeleter {
  static void Destruct(const FileSystemContext* context) {
    context->DeleteOnCorrectThread();
  }
};

}  // namespace fileapi

#endif  // WEBKIT_BROWSER_FILEAPI_FILE_SYSTEM_CONTEXT_H_
