chromium/chrome/browser/ash/fileapi/recent_drive_source.cc

// 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