chromium/chromeos/crosapi/mojom/file_system_provider.mojom

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

module crosapi.mojom;

import "mojo/public/mojom/base/file_path.mojom";
import "mojo/public/mojom/base/values.mojom";
import "ui/gfx/image/mojom/image.mojom";

// A file system is uniquely identified by an opaque string (consisting of at
// least the extension id + profile unique identifier) and a file_system_id.
[Stable]
struct FileSystemId {
  // At a minimum, identifies the extension. May also need to identify profiles
  // in the future.
  string provider@0;

  // A unique id (for a particular |provider|) provided by the extension.
  string id@1;
};

// The primary data source for the file system.
[Stable, Extensible]
enum FileSystemSource {
  [Default] kFile,
  kNetwork,
  kDevice
};

// Provides immutable metadata about a file system.
[Stable]
struct FileSystemMetadata {
  // The identifier of the file system.
  FileSystemId file_system_id@0;

  // A human-readable name for the file system.
  string display_name@1;

  // Whether the file system supports operations which may change contents of
  // the file system (such as creating, deleting or writing to files).
  bool writable@2;

  // The maximum number of files that can be opened at once. If 0, then not
  // limited.
  uint32 opened_files_limit@3;

  // Whether the file system supports the tag field for observing directories.
  bool supports_notify@4;
};

// Files can be opened either for reading or writing.
[Stable, Extensible]
enum OpenFileMode {
  [Default] kRead,
  kWrite,
};

[Stable]
struct OpenedFile {
  // A request ID to be be used by consecutive read/write and close requests.
  int64 open_request_id@0;

  // The path of the opened file. While it seems like it would make sense to use
  // a base::Path, both the extension API and the ash FileSystemProvider API
  // pass strings.
  string file_path@1;

  // Whether the file was opened for reading or writing.
  OpenFileMode mode@2;
};

// Represents a watcher.
[Stable]
struct FSPWatcher {
  // The path of the entry being observed.
  mojo_base.mojom.FilePath entry_path@0;

  // Whether watching should include all child entries recursively. It can be
  // true for directories only.
  bool recursive@1;

  // Tag used by the last notification for the watcher.
  string last_tag@2;
};

[Stable]
struct FileSystemInfo {
  // Fixed metadata about the file system.
  FileSystemMetadata metadata@0;

  // List of currently opened files.
  array<OpenedFile> opened_files@1;

  // List of watchers.
  array<FSPWatcher> watchers@2;
};

[Stable, Extensible]
enum FSPChangeType {
  [Default] kChanged,
  kDeleted
};

[Stable]
struct CloudFileInfo {
  string? version_tag@0;
};

[Stable]
struct FSPChange {
  // Next MinVersion: 2
  FSPChangeType type@0;
  mojo_base.mojom.FilePath path@1;
  [MinVersion=1] CloudFileInfo? cloud_file_info@2;
};

[Stable, Extensible]
enum FSPOperationResponse {
  // Next MinVersion: 2
  [Default] kUnknown,
  kUnmountSuccess,
  kGetEntryMetadataSuccess,
  kGetActionsSuccess,
  kReadDirectorySuccess,
  kReadFileSuccess,
  kGenericSuccess,
  kGenericFailure,
  [MinVersion=1] kOpenFileSuccess,
};

[Stable, Extensible]
enum FSPForwardResult {
  [Default] kUnknown,
  kSuccess,
  kInternalError,
  kNoListener,
};

// Implemented by Lacros.
[Stable, Uuid="8cbcf151-c189-4fe9-90ff-892c7f5637a9"]
interface FileSystemProvider {
  // Allows Ash to forward requests to Lacros extension providers.
  // We use mojo_base.mojom.Value as the type because:
  //   * The extension API is stable.
  //   * Both the supplier and consumer of this information uses a type
  //   interchangeable with mojo_base.mojom.Value. While we could add a
  //   translation layer to a strongly typed mojom struct, this adds a
  //   overhead and the potential for errors with no benefit.
  // Similarly, histogram_value is an int32 instead of the native enum
  // HistogramValue because the enum is stable, has hundreds of entries, and
  // cannot be type-mapped due to build-time dependency inversions.
  // Provider will typically be an extension_id but is intentionally left
  // abstract to support multi-profile in the future.
  // |delivery_failure| is set to true if the operation could not be forwarded
  // to the extension for any reason.
  [MinVersion=1]
  DeprecatedDeprecatedForwardOperation@0(
      string provider, int32 histogram_value, string event_name,
      array<mojo_base.mojom.Value> args);
  [MinVersion=2]
  DeprecatedForwardOperation@1(
      string provider, int32 histogram_value, string event_name,
      array<mojo_base.mojom.Value> args) => (bool delivery_failure);
  [MinVersion=3]
  ForwardOperation@2(
      string provider, int32 histogram_value, string event_name,
      mojo_base.mojom.ListValue args) => (bool delivery_failure);
  // Same as ForwardOperation, but exposes request metadata uniquely identifying
  // a request. A request is uniquely identified by a tuple of:
  // - |provider|, |file_system_id|, |request_id|: for requests addressed to a
  //   specific file system instance.
  // - |provider|, |request_id|: for requests addressed to a provider in general
  //   (e.g. a request to mount a new filesystem). In this case,
  //   |file_system_id| will be null.
  [MinVersion=4]
  ForwardRequest@3(
      string provider, string? file_system_id, int64 request_id,
      int32 histogram_value, string event_name,
      mojo_base.mojom.ListValue args) => (FSPForwardResult result);
  // Signal to Lacros that Ash cancelled a request uniquely identified by
  // given parameters.
  [MinVersion=4]
  CancelRequest@4(
      string provider, string? file_system_id, int64 request_id);
};

// Implemented by Ash. Extensions in Lacros can register themselves as file
// system providers. As the underlying system in Ash already supports
// multiplexing, we refrain from introducing a second layer of multiplexing at
// the crosapi layer. Lacros registers itself as a FileSystemProvider at
// startup. Extensions will call the "mount" method to make themselves known to
// Ash. At that point, Ash will send operations via FileSystemProvider, which
// Lacros will fulfill by calling methods in FileSystemProviderService. This
// creates a state machine with state spread between Ash and Lacros. This is
// dispreferred but unfortunately impossible to avoid given the design of the
// fileSystemProvider extension API.
[Stable, Uuid="82db93f7-e0a5-4a9c-a09f-db92f9e45348"]
interface FileSystemProviderService {
  // File system providers use this interface to receive operation requests.
  RegisterFileSystemProvider@0(pending_remote<FileSystemProvider> provider);

  // Makes a filesystem provided by an extension visible to the operating system
  // and other consumers.
  Mount@1(FileSystemMetadata metadata, bool persistent) => (string error);

  // Makes a filesystem provided by an extension unavailable.
  Unmount@2(FileSystemId file_system_id) => (string error);

  // Returns all file systems mounted by the extension.
  GetAll@3(string provider) => (array<FileSystemInfo> infos);

  // Returns information about a file system with the passed file_system_id.
  Get@4(FileSystemId file_system_id) => (FileSystemInfo? info);

  // Called by the extension to notify watchers of changes.
  // |file_system_id| is the identifier of the file system related to this
  // change.
  // |watcher| gives information about the path being observed, including the
  // tag.
  // |type| provides information about the observed entry.
  // |changes| provides information about the observed directory.
  // Note: for backwards compatibility with the existing extension API, both
  // |type| and |changes| are necessary even though they appear mutually
  // exclusive.
  Notify@5(FileSystemId file_system_id, FSPWatcher watcher,
      FSPChangeType type, array<FSPChange> changes) => (string error);

  // The following function represents responses from the Extension after
  // handling an operation. We use mojo_base.mojom.Value as the type because:
  //   * The extension API is stable.
  //   * Both the supplier and consumer of this information uses a type
  //   interchangeable with mojo_base.mojom.Value. While we could add a
  //   translation layer to a strongly typed mojom struct, this adds a
  //   overhead and the potential for errors with no benefit.
  // |request_id| corresponds to ids passed in via methods on interface
  // FileSystemProvider.
  [MinVersion=1]
  DeprecatedOperationFinished@6(FSPOperationResponse response,
      FileSystemId file_system_id, int64 request_id,
      array<mojo_base.mojom.Value> args) => (string error);
  [MinVersion=3]
  OperationFinished@9(FSPOperationResponse response,
      FileSystemId file_system_id, int64 request_id,
      mojo_base.mojom.ListValue args) => (string error);
  // Handles return the success callback from the `OpenFile` method after the
  // optional metadata dictionary support was added.
  // The underlying functionality behaves similarly to the `OperationFinished`
  // method above, however, having it as an interface method enables validating
  // whether the remote version supports the callback or not.
  [MinVersion=6]
  OpenFileFinishedSuccessfully@12(FileSystemId file_system_id,
      int64 request_id,
      mojo_base.mojom.ListValue args) => (string error);

  // The following function represents responses from the Extension after
  // handling a mount request. As opposed to `OperationFinished`, these
  // requests are handled per-provider instead of per provided file system
  // (no `FileSystemId).
  // |request_id| corresponds to ids passed in via methods on interface
  // FileSystemProvider.
  [MinVersion=4]
  MountFinished@10(string extension_id, int64 request_id,
      mojo_base.mojom.ListValue args) => (string error);

  // Called by Lacros when a filesystem providing extension is loaded.
  [MinVersion=2]
  ExtensionLoadedDeprecated@7(
      bool configurable, bool watchable, bool multiple_mounts,
      FileSystemSource source, string extension_name, string extension_id);

  // Called by Lacros when a filesystem providing extension is loaded. This
  // version passes the extension's icons to use in the file browser.
  [MinVersion=5]
  ExtensionLoaded@11(
      bool configurable, bool watchable, bool multiple_mounts,
      FileSystemSource source, string extension_name, string extension_id,
      gfx.mojom.ImageSkia? icon16x16, gfx.mojom.ImageSkia? icon32x32);

  // Called by Lacros when a filesystem providing extension is unloaded.
  [MinVersion=2]
  ExtensionUnloaded@8(string id, bool due_to_shutdown);
};