// 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.
#include "chrome/browser/ash/fileapi/recent_drive_source.h"
#include <iterator>
#include <utility>
#include <vector>
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/fileapi/recent_file.h"
#include "chromeos/ash/components/drivefs/drivefs_util.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "recent_drive_source.h"
#include "storage/browser/file_system/file_system_operation.h"
#include "storage/browser/file_system/file_system_operation_runner.h"
#include "storage/browser/file_system/file_system_url.h"
#include "storage/common/file_system/file_system_types.h"
#include "ui/file_manager/file_types_data.h"
#include "url/origin.h"
using content::BrowserThread;
namespace ash {
const char RecentDriveSource::kLoadHistogramName[] =
"FileBrowser.Recent.LoadDrive";
const char kAudioMimeType[] = "audio";
const char kImageMimeType[] = "image";
const char kVideoMimeType[] = "video";
RecentDriveSource::CallContext::CallContext(GetRecentFilesCallback callback)
: callback(std::move(callback)), build_start_time(base::TimeTicks::Now()) {}
RecentDriveSource::CallContext::CallContext(CallContext&& context)
: callback(std::move(context.callback)),
build_start_time(context.build_start_time),
files(std::move(context.files)),
search_query(std::move(context.search_query)) {}
RecentDriveSource::CallContext::~CallContext() = default;
RecentDriveSource::RecentDriveSource(Profile* profile)
: RecentSource(extensions::api::file_manager_private::VolumeType::kDrive),
profile_(profile) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
RecentDriveSource::~RecentDriveSource() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
std::vector<std::string> RecentDriveSource::CreateTypeFilters(
RecentSource::FileType file_type) {
std::vector<std::string> type_filters;
switch (file_type) {
case FileType::kAudio:
type_filters.push_back(kAudioMimeType);
break;
case FileType::kImage:
type_filters.push_back(kImageMimeType);
break;
case FileType::kVideo:
type_filters.push_back(kVideoMimeType);
break;
case FileType::kDocument: {
type_filters.insert(type_filters.end(),
file_types_data::kDocumentMIMETypes.begin(),
file_types_data::kDocumentMIMETypes.end());
break;
}
default:
// Leave the filters empty.
break;
}
return type_filters;
}
void RecentDriveSource::GetRecentFiles(const Params& params,
GetRecentFilesCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto context = std::make_unique<CallContext>(std::move(callback));
CallContext* context_ptr = context.get();
context_map_.AddWithID(std::move(context), params.call_id());
auto* integration_service =
drive::util::GetIntegrationServiceByProfile(profile_);
if (!integration_service) {
// |integration_service| is nullptr if Drive is disabled.
OnComplete(params.call_id());
return;
}
auto query_params = drivefs::mojom::QueryParameters::New();
query_params->page_size = params.max_files();
query_params->query_source =
drivefs::mojom::QueryParameters::QuerySource::kLocalOnly;
query_params->sort_field =
drivefs::mojom::QueryParameters::SortField::kLastViewedByMe;
query_params->sort_direction =
drivefs::mojom::QueryParameters::SortDirection::kDescending;
std::vector<std::string> type_filters =
RecentDriveSource::CreateTypeFilters(params.file_type());
query_params->viewed_time = params.cutoff_time();
query_params->title = params.query();
query_params->viewed_time_operator =
drivefs::mojom::QueryParameters::DateComparisonOperator::kGreaterThan;
if (type_filters.size() == 1) {
query_params->mime_type = type_filters.front();
} else if (type_filters.size() > 1) {
query_params->mime_types = std::move(type_filters);
}
integration_service->GetDriveFsInterface()->StartSearchQuery(
context_ptr->search_query.BindNewPipeAndPassReceiver(),
std::move(query_params));
context_ptr->search_query->GetNextPage(
base::BindOnce(&RecentDriveSource::GotSearchResults,
weak_ptr_factory_.GetWeakPtr(), params));
}
std::vector<RecentFile> RecentDriveSource::Stop(const int32_t call_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CallContext* context = context_map_.Lookup(call_id);
if (context == nullptr) {
return {};
}
std::vector<RecentFile> files = std::move(context->files);
context_map_.Remove(call_id);
return files;
}
void RecentDriveSource::OnComplete(const int32_t call_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CallContext* context = context_map_.Lookup(call_id);
if (context == nullptr) {
return;
}
DCHECK(!context->build_start_time.is_null());
UMA_HISTOGRAM_TIMES(kLoadHistogramName,
base::TimeTicks::Now() - context->build_start_time);
std::move(context->callback).Run(std::move(context->files));
context_map_.Remove(call_id);
}
void RecentDriveSource::GotSearchResults(
const Params& params,
drive::FileError error,
std::optional<std::vector<drivefs::mojom::QueryItemPtr>> results) {
CallContext* context = context_map_.Lookup(params.call_id());
if (context == nullptr) {
return;
}
auto* integration_service =
drive::util::GetIntegrationServiceByProfile(profile_);
if (!results || !integration_service) {
OnComplete(params.call_id());
return;
}
context->files.reserve(results->size());
for (auto& result : *results) {
if (!drivefs::IsAFile(result->metadata->type)) {
continue;
}
base::FilePath path = integration_service->GetMountPointPath().BaseName();
if (!base::FilePath("/").AppendRelativePath(result->path, &path)) {
path = path.Append(result->path);
}
context->files.emplace_back(
params.file_system_context()->CreateCrackedFileSystemURL(
blink::StorageKey::CreateFirstParty(
url::Origin::Create(params.origin())),
storage::kFileSystemTypeExternal, path),
// Do not use "modification_time" field here because that will cause
// files modified by others recently (e.g. Shared with me) being
// treated as recent files.
result->metadata->last_viewed_by_me_time);
}
OnComplete(params.call_id());
}
} // namespace ash