chromium/chrome/browser/ash/fileapi/recent_disk_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_DISK_SOURCE_H_
#define CHROME_BROWSER_ASH_FILEAPI_RECENT_DISK_SOURCE_H_

#include <memory>
#include <string>
#include <vector>

#include "base/containers/id_map.h"
#include "base/files/file.h"
#include "base/files/file_path.h"
#include "base/gtest_prod_util.h"
#include "base/memory/weak_ptr.h"
#include "base/time/time.h"
#include "chrome/browser/ash/fileapi/file_accumulator.h"
#include "chrome/browser/ash/fileapi/recent_file.h"
#include "chrome/browser/ash/fileapi/recent_model.h"
#include "chrome/browser/ash/fileapi/recent_source.h"
#include "storage/browser/file_system/file_system_operation.h"

namespace ash {

// RecentSource implementation for local disks.
// Used for Downloads and fuse-based Crostini.
//
// All member functions must be called on the UI thread.
class RecentDiskSource : public RecentSource {
 public:
  // Create a RecentDiskSource for the volume registered to `mount_point_name`.
  // Does nothing if no volume is registered at `mount_point_name`.
  // If `ignore_dotfiles` is true, recents will ignore directories and files
  // starting with a dot. Set `max_depth` to zero for unlimited depth.
  RecentDiskSource(
      extensions::api::file_manager_private::VolumeType volume_type,
      std::string mount_point_name,
      bool ignore_dotfiles,
      int max_depth,
      std::string uma_histogram_name);

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

  ~RecentDiskSource() override;

  // RecentSource overrides:
  void GetRecentFiles(const Params& params,
                      GetRecentFilesCallback callback) override;

  // Stops the recent files search. Returns any partial results already
  // collected.
  std::vector<RecentFile> Stop(const int32_t call_id) override;

  // Helper function that determines a match between file type inferred from the
  // path and the desired file_type.
  static bool MatchesFileType(const base::FilePath& path,
                              RecentSource::FileType file_type);

 private:
  FRIEND_TEST_ALL_PREFIXES(RecentDiskSourceTest, GetRecentFiles_UmaStats);

  static const char kLoadHistogramName[];

  void ScanDirectory(const int32_t call_id,
                     const base::FilePath& path,
                     int depth);
  void OnReadDirectory(const int32_t call_id,
                       const base::FilePath& path,
                       int depth,
                       base::File::Error result,
                       storage::FileSystemOperation::FileEntryList entries,
                       bool has_more);
  void OnGotMetadata(const int32_t call_id,
                     const storage::FileSystemURL& url,
                     base::File::Error result,
                     const base::File::Info& info);
  void OnReadOrStatFinished(int32_t call_id);

  storage::FileSystemURL BuildDiskURL(const Params& params,
                                      const base::FilePath& path) const;

  const std::string mount_point_name_;
  const bool ignore_dotfiles_;
  const int max_depth_;
  const std::string uma_histogram_name_;

  // CallContext gather information for a single GetRecentFiles call. As
  // GetRecentFiles call can take time, and some data is collected on IO thread,
  // we cannot guarantee that two calls will not overlap. To solve this each
  // call receives a unique call_id and its context is stored in the map. As the
  // map is only accessed on the UI thread we do not need to use additional
  // locks to guarantee its consistency.
  struct CallContext {
    CallContext(const Params& params, GetRecentFilesCallback callback);
    // Move constructor; necessary as callback is a move-only type.
    CallContext(CallContext&& context);

    ~CallContext();

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

    // The callback called when the files and their metadata is ready.
    GetRecentFilesCallback callback;
    // Time when the build started.
    base::TimeTicks build_start_time;
    // Number of ReadDirectory() calls in flight.
    int inflight_readdirs = 0;
    // Number of GetMetadata() calls in flight.
    int inflight_stats = 0;
    // Most recently modified files.
    FileAccumulator accumulator;
  };

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

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

}  // namespace ash

#endif  // CHROME_BROWSER_ASH_FILEAPI_RECENT_DISK_SOURCE_H_