blob: c82752af202034a972e2fc58f574ca6573fe523c [file] [log] [blame]
// Copyright 2017 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.
#include "chrome/browser/chromeos/fileapi/recent_disk_source.h"
#include <utility>
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "base/task/post_task.h"
#include "base/time/time.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "storage/browser/fileapi/external_mount_points.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/browser/fileapi/file_system_operation.h"
#include "storage/browser/fileapi/file_system_operation_runner.h"
#include "storage/browser/fileapi/file_system_url.h"
using content::BrowserThread;
namespace chromeos {
namespace {
void OnReadDirectoryOnIOThread(
const storage::FileSystemOperation::ReadDirectoryCallback& callback,
base::File::Error result,
storage::FileSystemOperation::FileEntryList entries,
bool has_more) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(callback, result, std::move(entries), has_more));
}
void ReadDirectoryOnIOThread(
scoped_refptr<storage::FileSystemContext> file_system_context,
const storage::FileSystemURL& url,
const storage::FileSystemOperation::ReadDirectoryCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
file_system_context->operation_runner()->ReadDirectory(
url, base::BindRepeating(&OnReadDirectoryOnIOThread, callback));
}
void OnGetMetadataOnIOThread(
storage::FileSystemOperation::GetMetadataCallback callback,
base::File::Error result,
const base::File::Info& info) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
base::BindOnce(std::move(callback), result, info));
}
void GetMetadataOnIOThread(
scoped_refptr<storage::FileSystemContext> file_system_context,
const storage::FileSystemURL& url,
int fields,
storage::FileSystemOperation::GetMetadataCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
file_system_context->operation_runner()->GetMetadata(
url, fields,
base::BindOnce(&OnGetMetadataOnIOThread, std::move(callback)));
}
} // namespace
RecentDiskSource::RecentDiskSource(std::string mount_point_name,
bool ignore_dotfiles,
int max_depth,
std::string uma_histogram_name)
: mount_point_name_(std::move(mount_point_name)),
ignore_dotfiles_(ignore_dotfiles),
max_depth_(max_depth),
uma_histogram_name_(std::move(uma_histogram_name)),
weak_ptr_factory_(this) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
RecentDiskSource::~RecentDiskSource() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void RecentDiskSource::GetRecentFiles(Params params) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!params_.has_value());
DCHECK(build_start_time_.is_null());
DCHECK_EQ(0, inflight_readdirs_);
DCHECK_EQ(0, inflight_stats_);
DCHECK(recent_files_.empty());
// Return immediately if mount point does not exist.
storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();
base::FilePath path;
if (!mount_points->GetRegisteredPath(mount_point_name_, &path)) {
std::move(params.callback()).Run({});
return;
}
params_.emplace(std::move(params));
DCHECK(params_.has_value());
build_start_time_ = base::TimeTicks::Now();
ScanDirectory(base::FilePath(), 1);
}
void RecentDiskSource::ScanDirectory(const base::FilePath& path, int depth) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(params_.has_value());
storage::FileSystemURL url = BuildDiskURL(path);
++inflight_readdirs_;
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(
&ReadDirectoryOnIOThread,
base::WrapRefCounted(params_.value().file_system_context()), url,
base::BindRepeating(&RecentDiskSource::OnReadDirectory,
weak_ptr_factory_.GetWeakPtr(), path, depth)));
}
void RecentDiskSource::OnReadDirectory(
const base::FilePath& path,
const int depth,
base::File::Error result,
storage::FileSystemOperation::FileEntryList entries,
bool has_more) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(params_.has_value());
for (const auto& entry : entries) {
// Ignore directories and files that start with dot.
if (ignore_dotfiles_ &&
base::StartsWith(entry.name.value(), ".",
base::CompareCase::INSENSITIVE_ASCII)) {
continue;
}
base::FilePath subpath = path.Append(entry.name);
if (entry.type == filesystem::mojom::FsFileType::DIRECTORY) {
if (max_depth_ > 0 && depth >= max_depth_) {
continue;
}
ScanDirectory(subpath, depth + 1);
} else {
storage::FileSystemURL url = BuildDiskURL(subpath);
++inflight_stats_;
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(
&GetMetadataOnIOThread,
base::WrapRefCounted(params_.value().file_system_context()), url,
storage::FileSystemOperation::GET_METADATA_FIELD_LAST_MODIFIED,
base::BindOnce(&RecentDiskSource::OnGetMetadata,
weak_ptr_factory_.GetWeakPtr(), url)));
}
}
if (has_more)
return;
--inflight_readdirs_;
OnReadOrStatFinished();
}
void RecentDiskSource::OnGetMetadata(const storage::FileSystemURL& url,
base::File::Error result,
const base::File::Info& info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(params_.has_value());
if (result == base::File::FILE_OK &&
info.last_modified >= params_.value().cutoff_time()) {
recent_files_.emplace(RecentFile(url, info.last_modified));
while (recent_files_.size() > params_.value().max_files())
recent_files_.pop();
}
--inflight_stats_;
OnReadOrStatFinished();
}
void RecentDiskSource::OnReadOrStatFinished() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (inflight_readdirs_ > 0 || inflight_stats_ > 0)
return;
// All reads/scans completed.
std::vector<RecentFile> files;
while (!recent_files_.empty()) {
files.emplace_back(recent_files_.top());
recent_files_.pop();
}
DCHECK(!build_start_time_.is_null());
UmaHistogramTimes(uma_histogram_name_,
base::TimeTicks::Now() - build_start_time_);
build_start_time_ = base::TimeTicks();
Params params = std::move(params_.value());
params_.reset();
DCHECK(!params_.has_value());
DCHECK(build_start_time_.is_null());
DCHECK_EQ(0, inflight_readdirs_);
DCHECK_EQ(0, inflight_stats_);
DCHECK(recent_files_.empty());
std::move(params.callback()).Run(std::move(files));
}
storage::FileSystemURL RecentDiskSource::BuildDiskURL(
const base::FilePath& path) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(params_.has_value());
storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();
return mount_points->CreateExternalFileSystemURL(params_.value().origin(),
mount_point_name_, path);
}
} // namespace chromeos