chromium/chrome/browser/ash/fileapi/recent_arc_media_source.h

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

#ifndef CHROME_BROWSER_ASH_FILEAPI_RECENT_ARC_MEDIA_SOURCE_H_
#define CHROME_BROWSER_ASH_FILEAPI_RECENT_ARC_MEDIA_SOURCE_H_

#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <vector>

#include "ash/components/arc/mojom/file_system.mojom.h"
#include "base/containers/id_map.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root.h"
#include "chrome/browser/ash/fileapi/recent_source.h"
#include "chrome/browser/profiles/profile.h"

class Profile;

namespace ash {

class RecentFile;

// RecentSource implementation for ARC media view. This class is not designed to
// be used by itself. Instead, it is instantiated and used by the RecentModel to
// retrieve recent files from ARC media view.
//
// All member functions must be called on the UI thread.
class RecentArcMediaSource : public RecentSource {
 public:
  // Creates a recent file sources that scans Arc media. The `profile` is used
  // to arc::ArcFileSystemOperationRunner and arc::ArcDocumentsProviderRootMap
  // for retrieving recent files and scanning ARC directories, respectively. The
  // `root_id` must be one of the know ARC root IDs, denoting Documents, Videos,
  // Images or Audio roots.
  RecentArcMediaSource(Profile* profile, const std::string& root_id);

  RecentArcMediaSource(const RecentArcMediaSource&) = delete;
  RecentArcMediaSource& operator=(const RecentArcMediaSource&) = delete;

  ~RecentArcMediaSource() override;

  // Overrides the base class method to launch searches for recent file in the
  // root identified by the `root_id` parameter given at the construction time.
  void GetRecentFiles(const Params& params,
                      GetRecentFilesCallback callback) override;

  // Overrides the base class Stop method to return partial results collected
  // before the timeout call. This method must be called on the UI thread.
  std::vector<RecentFile> Stop(const int32_t call_id) override;

  // Causes laggy performance for this source. This is to be only used in tests.
  void SetLagForTesting(const base::TimeDelta& lag);

  // The name of the metric under which recent file access statistics for ARC
  // are recorded.
  static const char kLoadHistogramName[];

 private:
  // Call context stores information specific to a single GetRecentFiles call.
  // If multiple calls are issued each will have its own context.
  struct CallContext {
    CallContext(const Params& params, GetRecentFilesCallback callback);
    // Move constructor needed as callback cannot be copied.
    CallContext(CallContext&& context);
    ~CallContext();

    // The parameters of the GetRecentFiles call.
    const Params params;

    // The callback to be called once all files are gathered. We do not know
    // ahead of time when this may be the case, due to nested directories.
    // Thus this class behaves similarly to a Barrier class, except that the
    // number of times the barrier has to be called varies.
    GetRecentFilesCallback callback;

    // Time when this call started.
    base::TimeTicks build_start_time;

    // Number of in-flight ReadDirectory() calls by ScanDirectory().
    int num_inflight_readdirs = 0;

    // Maps a document ID to a RecentFile. In OnGotRecentDocuments(), this map
    // is initialized with document IDs returned by GetRecentDocuments(), and
    // its values are filled as we scan the tree in ScanDirectory().
    // In case of multiple files with the same document ID found, the file with
    // lexicographically smallest URL is kept. A nullopt value means the
    // corresponding file is not (yet) found.
    std::map<std::string, std::optional<RecentFile>> document_id_to_file;
  };

  // Returns whether or not this scanner supports files with the given type.
  bool MatchesFileType(FileType file_type) const;

  // Extra method that allows us to insert an optional lag between the runner
  // being done and the OnGotRecentDocuments being called.
  void OnRunnerDone(
      const int32_t call_id,
      std::optional<std::vector<arc::mojom::DocumentPtr>> maybe_documents);

  // The method called once recent document pointers have been retrieved. This
  // may take place immediately after the runner was done, or with a small lag
  // that helps testing the interaction with the Stop method.
  void OnGotRecentDocuments(
      const int32_t call_id,
      std::optional<std::vector<arc::mojom::DocumentPtr>> maybe_documents);

  // Starts scanning of the directory with the given path.
  void ScanDirectory(const int32_t call_id, const base::FilePath& path);

  // The method called once a scan of directory is completed.
  void OnDirectoryRead(
      const int32_t call_id,
      const base::FilePath& path,
      base::File::Error result,
      std::vector<arc::ArcDocumentsProviderRoot::ThinFileInfo> files);

  // Invoked once traversing of the directory hirerachy is finished.
  void OnComplete(const int32_t call_id);

  // Creates a complete FileSystemURL for the given `path`, with the
  // help of `relative_mount_path_`.
  storage::FileSystemURL BuildDocumentsProviderUrl(
      const Params& params,
      const base::FilePath& path) const;

  bool WillArcFileSystemOperationsRunImmediately();

  // A map from the call ID to the call context.
  base::IDMap<std::unique_ptr<CallContext>> context_map_;

  // The profile for which this recent source was created.
  const raw_ptr<Profile> profile_;

  // The ARC root, such as Documents, Images, Media or Audio.
  const std::string root_id_;

  // The path at which the given ARC system is mounted.
  const base::FilePath relative_mount_path_;

  // The artificial lag introduced to this root for test purposes.
  base::TimeDelta lag_;

  // Timer; only allocated if the lag is positive.
  std::unique_ptr<base::OneShotTimer> timer_;

  base::WeakPtrFactory<RecentArcMediaSource> weak_ptr_factory_{this};
};

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_FILEAPI_RECENT_ARC_MEDIA_SOURCE_H_