// Copyright 2013 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/extensions/file_manager/private_api_drive.h"
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string_view>
#include <utility>
#include "ash/constants/ash_features.h"
#include "base/base64.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/i18n/string_search.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/task/thread_pool.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root.h"
#include "chrome/browser/ash/arc/fileapi/arc_documents_provider_root_map.h"
#include "chrome/browser/ash/drive/drive_integration_service.h"
#include "chrome/browser/ash/drive/file_system_util.h"
#include "chrome/browser/ash/extensions/file_manager/event_router.h"
#include "chrome/browser/ash/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/ash/extensions/file_manager/private_api_util.h"
#include "chrome/browser/ash/file_manager/file_tasks.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/path_util.h"
#include "chrome/browser/ash/file_manager/url_util.h"
#include "chrome/browser/ash/file_system_provider/mount_path_util.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/ash/fileapi/external_file_url_util.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/ash/fileapi/recent_drive_source.h"
#include "chrome/browser/ash/fusebox/fusebox_server.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/drivefs/drivefs_native_message_host.h"
#include "chrome/browser/chromeos/drivefs/drivefs_native_message_host_origins.h"
#include "chrome/browser/net/system_network_context_manager.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/webui/ash/manage_mirrorsync/manage_mirrorsync_dialog.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "chrome/common/extensions/api/file_manager_private_internal.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chromeos/ash/components/drivefs/drivefs_pinning_manager.h"
#include "chromeos/ash/components/drivefs/drivefs_util.h"
#include "chromeos/ash/components/drivefs/mojom/drivefs.mojom.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/drive/chromeos/search_metadata.h"
#include "components/drive/drive_pref_names.h"
#include "components/drive/event_logger.h"
#include "components/drive/file_errors.h"
#include "components/prefs/pref_service.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/network_service_instance.h"
#include "content/public/browser/storage_partition.h"
#include "extensions/browser/extension_registry.h"
#include "google_apis/common/auth_service.h"
#include "google_apis/drive/drive_api_url_generator.h"
#include "google_apis/gaia/gaia_constants.h"
#include "mojo/public/cpp/bindings/callback_helpers.h"
#include "services/network/public/cpp/network_connection_tracker.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "storage/common/file_system/file_system_info.h"
#include "storage/common/file_system/file_system_util.h"
#include "url/gurl.h"
namespace extensions {
namespace {
using ash::file_system_provider::EntryMetadata;
using ash::file_system_provider::ProvidedFileSystemInterface;
using ash::file_system_provider::util::FileSystemURLParser;
using content::BrowserThread;
using drive::DriveIntegrationService;
using drive::util::GetIntegrationServiceByProfile;
using drivefs::pinning::PinningManager;
using extensions::api::file_manager_private::EntryProperties;
using extensions::api::file_manager_private::EntryPropertyName;
using file_manager::util::EntryDefinition;
using file_manager::util::EntryDefinitionCallback;
using file_manager::util::EntryDefinitionList;
using file_manager::util::EntryDefinitionListCallback;
using file_manager::util::FileDefinition;
using file_manager::util::FileDefinitionList;
using google_apis::DriveApiUrlGenerator;
constexpr char kAvailableOfflinePropertyName[] = "availableOffline";
// Thresholds for logging slow operations.
constexpr base::TimeDelta kDriveSlowOperationThreshold = base::Seconds(5);
constexpr base::TimeDelta kDriveVerySlowOperationThreshold = base::Minutes(1);
class SingleEntryPropertiesGetterForFileSystemProvider {
public:
typedef base::OnceCallback<void(std::unique_ptr<EntryProperties> properties,
base::File::Error error)>
ResultCallback;
// Creates an instance and starts the process.
static void Start(const storage::FileSystemURL file_system_url,
const std::set<EntryPropertyName>& names,
ResultCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
SingleEntryPropertiesGetterForFileSystemProvider* instance =
new SingleEntryPropertiesGetterForFileSystemProvider(
file_system_url, names, std::move(callback));
instance->StartProcess();
// The instance will be destroyed by itself.
}
virtual ~SingleEntryPropertiesGetterForFileSystemProvider() = default;
private:
SingleEntryPropertiesGetterForFileSystemProvider(
const storage::FileSystemURL& file_system_url,
const std::set<EntryPropertyName>& names,
ResultCallback callback)
: callback_(std::move(callback)),
file_system_url_(file_system_url),
names_(names),
properties_(new EntryProperties) {
DCHECK(!callback_.is_null());
}
void StartProcess() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
FileSystemURLParser parser(file_system_url_);
if (!parser.Parse()) {
CompleteGetEntryProperties(base::File::FILE_ERROR_NOT_FOUND);
return;
}
ProvidedFileSystemInterface::MetadataFieldMask field_mask =
ProvidedFileSystemInterface::METADATA_FIELD_NONE;
if (names_.find(api::file_manager_private::EntryPropertyName::kSize) !=
names_.end()) {
field_mask |= ProvidedFileSystemInterface::METADATA_FIELD_SIZE;
}
if (names_.find(
api::file_manager_private::EntryPropertyName::kModificationTime) !=
names_.end()) {
field_mask |=
ProvidedFileSystemInterface::METADATA_FIELD_MODIFICATION_TIME;
}
if (names_.find(
api::file_manager_private::EntryPropertyName::kContentMimeType) !=
names_.end()) {
field_mask |= ProvidedFileSystemInterface::METADATA_FIELD_MIME_TYPE;
}
if (names_.find(
api::file_manager_private::EntryPropertyName::kThumbnailUrl) !=
names_.end()) {
field_mask |= ProvidedFileSystemInterface::METADATA_FIELD_THUMBNAIL;
}
if (!field_mask) {
OnGetMetadataCompleted(nullptr, base::File::FILE_OK);
return;
}
parser.file_system()->GetMetadata(
parser.file_path(), field_mask,
base::BindOnce(&SingleEntryPropertiesGetterForFileSystemProvider::
OnGetMetadataCompleted,
weak_ptr_factory_.GetWeakPtr()));
}
void OnGetMetadataCompleted(std::unique_ptr<EntryMetadata> metadata,
base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (result != base::File::FILE_OK) {
CompleteGetEntryProperties(result);
return;
}
if (names_.find(api::file_manager_private::EntryPropertyName::kSize) !=
names_.end()) {
properties_->size = *metadata->size;
}
if (names_.find(
api::file_manager_private::EntryPropertyName::kModificationTime) !=
names_.end()) {
properties_->modification_time =
metadata->modification_time->InMillisecondsFSinceUnixEpoch();
}
if (names_.find(
api::file_manager_private::EntryPropertyName::kContentMimeType) !=
names_.end() &&
metadata->mime_type.get()) {
properties_->content_mime_type = *metadata->mime_type;
}
if (names_.find(
api::file_manager_private::EntryPropertyName::kThumbnailUrl) !=
names_.end() &&
metadata->thumbnail.get()) {
properties_->thumbnail_url = *metadata->thumbnail;
}
CompleteGetEntryProperties(base::File::FILE_OK);
}
void CompleteGetEntryProperties(base::File::Error result) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!callback_.is_null());
std::move(callback_).Run(std::move(properties_), result);
content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
}
// Given parameters.
ResultCallback callback_;
const storage::FileSystemURL file_system_url_;
const std::set<EntryPropertyName> names_;
// Values used in the process.
std::unique_ptr<EntryProperties> properties_;
base::WeakPtrFactory<SingleEntryPropertiesGetterForFileSystemProvider>
weak_ptr_factory_{this};
}; // class SingleEntryPropertiesGetterForFileSystemProvider
class SingleEntryPropertiesGetterForDocumentsProvider {
public:
typedef base::OnceCallback<void(std::unique_ptr<EntryProperties> properties,
base::File::Error error)>
ResultCallback;
// Creates an instance and starts the process.
static void Start(const storage::FileSystemURL file_system_url,
Profile* const profile,
ResultCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
SingleEntryPropertiesGetterForDocumentsProvider* instance =
new SingleEntryPropertiesGetterForDocumentsProvider(
file_system_url, profile, std::move(callback));
instance->StartProcess();
// The instance will be destroyed by itself.
}
private:
SingleEntryPropertiesGetterForDocumentsProvider(
const storage::FileSystemURL& file_system_url,
Profile* const profile,
ResultCallback callback)
: callback_(std::move(callback)),
file_system_url_(ResolveFuseBoxFSURL(profile, file_system_url)),
profile_(profile),
properties_(new EntryProperties) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(!callback_.is_null());
}
void StartProcess() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto* root_map =
arc::ArcDocumentsProviderRootMap::GetForBrowserContext(profile_);
if (!root_map) {
CompleteGetEntryProperties(base::File::FILE_ERROR_NOT_FOUND);
}
base::FilePath path;
auto* root = root_map->ParseAndLookup(file_system_url_, &path);
if (!root) {
CompleteGetEntryProperties(base::File::FILE_ERROR_NOT_FOUND);
return;
}
root->GetExtraFileMetadata(
path, base::BindOnce(&SingleEntryPropertiesGetterForDocumentsProvider::
OnGetExtraFileMetadata,
weak_ptr_factory_.GetWeakPtr()));
}
static storage::FileSystemURL ResolveFuseBoxFSURL(
Profile* profile,
const storage::FileSystemURL& file_system_url) {
if (!base::StartsWith(file_system_url.filesystem_id(),
file_manager::util::kFuseBoxMountNamePrefix)) {
return file_system_url;
}
fusebox::Server* fusebox_server = fusebox::Server::GetInstance();
if (!fusebox_server) {
return storage::FileSystemURL();
}
return fusebox_server->ResolveFilename(profile,
file_system_url.path().value());
}
void OnGetExtraFileMetadata(
base::File::Error error,
const arc::ArcDocumentsProviderRoot::ExtraFileMetadata& metadata) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error != base::File::FILE_OK) {
CompleteGetEntryProperties(error);
return;
}
properties_->can_delete = metadata.supports_delete;
properties_->can_rename = metadata.supports_rename;
properties_->can_add_children = metadata.dir_supports_create;
if (!metadata.last_modified.is_null()) {
properties_->modification_time =
metadata.last_modified.InMillisecondsFSinceUnixEpochIgnoringNull();
}
properties_->size = metadata.size;
CompleteGetEntryProperties(base::File::FILE_OK);
}
void CompleteGetEntryProperties(base::File::Error error) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(callback_);
std::move(callback_).Run(std::move(properties_), error);
content::GetUIThreadTaskRunner({})->DeleteSoon(FROM_HERE, this);
}
// Given parameters.
ResultCallback callback_;
const storage::FileSystemURL file_system_url_;
const raw_ptr<Profile> profile_;
// Values used in the process.
std::unique_ptr<EntryProperties> properties_;
base::WeakPtrFactory<SingleEntryPropertiesGetterForDocumentsProvider>
weak_ptr_factory_{this};
}; // class SingleEntryPropertiesGetterForDocumentsProvider
void OnSearchDriveFs(
scoped_refptr<ExtensionFunction> function,
bool filter_dirs,
base::OnceCallback<void(std::optional<base::Value::List>)> callback,
drive::FileError error,
std::optional<std::vector<drivefs::mojom::QueryItemPtr>> items) {
Profile* const profile =
Profile::FromBrowserContext(function->browser_context());
DriveIntegrationService* const service =
GetIntegrationServiceByProfile(profile);
if (!service) {
LOG(ERROR) << "No drive service";
std::move(callback).Run(std::nullopt);
return;
}
if (error != drive::FILE_ERROR_OK || !items.has_value()) {
LOG_IF(ERROR, error != drive::FILE_ERROR_OK)
<< "Drive search failed: " << drive::FileErrorToString(error);
std::move(callback).Run(std::nullopt);
return;
}
GURL url;
file_manager::util::ConvertAbsoluteFilePathToFileSystemUrl(
profile, service->GetMountPointPath(), function->source_url(), &url);
const auto fs_root = base::StrCat({url.spec(), "/"});
const auto fs_name = service->GetMountPointPath().BaseName().value();
const base::FilePath root("/");
base::Value::List result;
for (const auto& item : *items) {
base::FilePath path;
if (!root.AppendRelativePath(item->path, &path)) {
path = item->path;
}
base::Value::Dict entry;
entry.Set("fileSystemName", fs_name);
entry.Set("fileSystemRoot", fs_root);
entry.Set("fileFullPath", item->path.AsUTF8Unsafe());
bool is_dir = drivefs::IsADirectory(item->metadata->type);
entry.Set("fileIsDirectory", is_dir);
entry.Set(kAvailableOfflinePropertyName, item->metadata->available_offline);
if (!filter_dirs || !is_dir) {
result.Append(std::move(entry));
}
}
std::move(callback).Run(std::move(result));
}
drivefs::mojom::QueryParameters::QuerySource SearchDriveFs(
scoped_refptr<ExtensionFunction> function,
drivefs::mojom::QueryParametersPtr query,
bool filter_dirs,
base::OnceCallback<void(std::optional<base::Value::List>)> callback) {
DriveIntegrationService* const service = GetIntegrationServiceByProfile(
Profile::FromBrowserContext(function->browser_context()));
auto on_response = base::BindOnce(&OnSearchDriveFs, std::move(function),
filter_dirs, std::move(callback));
return service->GetDriveFsHost()->PerformSearch(std::move(query),
std::move(on_response));
}
void UmaEmitSearchOutcome(
bool success,
bool remote,
FileManagerPrivateSearchDriveMetadataFunction::SearchType type,
const base::TimeTicks& time_started) {
const char* infix = nullptr;
switch (type) {
case FileManagerPrivateSearchDriveMetadataFunction::SearchType::kText:
infix = "TextSearchTime";
break;
case FileManagerPrivateSearchDriveMetadataFunction::SearchType::
kSharedWithMe:
infix = "SharedSearchTime";
break;
case FileManagerPrivateSearchDriveMetadataFunction::SearchType::kOffline:
infix = "OfflineSearchTime";
break;
}
if (remote) {
base::UmaHistogramMediumTimes(
base::StrCat({"DriveCommon.RemoteSearch.", infix,
success ? ".SuccessTime" : ".FailTime"}),
base::TimeTicks::Now() - time_started);
} else {
base::UmaHistogramMediumTimes(
base::StrCat({"DriveCommon.LocalSearch.", infix,
success ? ".SuccessTime" : ".FailTime"}),
base::TimeTicks::Now() - time_started);
}
}
} // namespace
FileManagerPrivateInternalGetEntryPropertiesFunction::
FileManagerPrivateInternalGetEntryPropertiesFunction()
: processed_count_(0) {
SetWarningThresholds(kDriveSlowOperationThreshold,
kDriveVerySlowOperationThreshold);
}
FileManagerPrivateInternalGetEntryPropertiesFunction::
~FileManagerPrivateInternalGetEntryPropertiesFunction() = default;
ExtensionFunction::ResponseAction
FileManagerPrivateInternalGetEntryPropertiesFunction::Run() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
using api::file_manager_private_internal::GetEntryProperties::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* const profile = Profile::FromBrowserContext(browser_context());
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
profile, render_frame_host());
properties_list_.resize(params->urls.size());
const std::set<EntryPropertyName> names_as_set(params->names.begin(),
params->names.end());
for (size_t i = 0; i < params->urls.size(); i++) {
const GURL url = GURL(params->urls[i]);
const storage::FileSystemURL file_system_url =
file_system_context->CrackURLInFirstPartyContext(url);
storage::FileSystemType file_system_type = file_system_url.type();
if (file_system_type == storage::kFileSystemTypeFuseBox) {
std::string_view path(file_system_url.path().value());
if (base::StartsWith(path, file_manager::util::kFuseBoxMediaSlashPath)) {
path.remove_prefix(strlen(file_manager::util::kFuseBoxMediaSlashPath));
if (base::StartsWith(path,
file_manager::util::kFuseBoxSubdirPrefixADP)) {
file_system_type = storage::kFileSystemTypeArcDocumentsProvider;
} else if (base::StartsWith(
path, file_manager::util::kFuseBoxSubdirPrefixFSP)) {
file_system_type = storage::kFileSystemTypeProvided;
}
}
}
auto callback =
base::BindOnce(&FileManagerPrivateInternalGetEntryPropertiesFunction::
CompleteGetEntryProperties,
this, i, file_system_url);
switch (file_system_type) {
case storage::kFileSystemTypeProvided:
SingleEntryPropertiesGetterForFileSystemProvider::Start(
file_system_url, names_as_set, std::move(callback));
break;
case storage::kFileSystemTypeDriveFs:
file_manager::util::SingleEntryPropertiesGetterForDriveFs::Start(
file_system_url, profile, std::move(callback));
break;
case storage::kFileSystemTypeArcDocumentsProvider:
SingleEntryPropertiesGetterForDocumentsProvider::Start(
file_system_url, profile, std::move(callback));
break;
default:
// TODO(yawano) Change this to support other voluems (e.g. local) ,and
// integrate fileManagerPrivate.getMimeType to this method.
LOG(ERROR) << "Not supported file system type.";
CompleteGetEntryProperties(i, file_system_url,
base::WrapUnique(new EntryProperties),
base::File::FILE_ERROR_INVALID_OPERATION);
}
}
return RespondLater();
}
void FileManagerPrivateInternalGetEntryPropertiesFunction::
CompleteGetEntryProperties(size_t index,
const storage::FileSystemURL& url,
std::unique_ptr<EntryProperties> properties,
base::File::Error error) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(0 <= processed_count_ && processed_count_ < properties_list_.size());
if (error == base::File::FILE_OK) {
properties->external_file_url =
ash::FileSystemURLToExternalFileURL(url).spec();
}
properties_list_[index] = std::move(*properties);
processed_count_++;
if (processed_count_ < properties_list_.size()) {
return;
}
Respond(
ArgumentList(extensions::api::file_manager_private_internal::
GetEntryProperties::Results::Create(properties_list_)));
}
FileManagerPrivateInternalPinDriveFileFunction::
FileManagerPrivateInternalPinDriveFileFunction() {
SetWarningThresholds(kDriveSlowOperationThreshold,
kDriveVerySlowOperationThreshold);
}
ExtensionFunction::ResponseAction
FileManagerPrivateInternalPinDriveFileFunction::Run() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
using extensions::api::file_manager_private_internal::PinDriveFile::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
scoped_refptr<storage::FileSystemContext> file_system_context =
file_manager::util::GetFileSystemContextForRenderFrameHost(
Profile::FromBrowserContext(browser_context()), render_frame_host());
const GURL url = GURL(params->url);
const storage::FileSystemURL file_system_url =
file_system_context->CrackURLInFirstPartyContext(url);
switch (file_system_url.type()) {
case storage::kFileSystemTypeDriveFs:
return RunAsyncForDriveFs(file_system_url, params->pin);
default:
return RespondNow(Error("Invalid file system type"));
}
}
ExtensionFunction::ResponseAction
FileManagerPrivateInternalPinDriveFileFunction::RunAsyncForDriveFs(
const storage::FileSystemURL& file_system_url,
bool pin) {
DriveIntegrationService* const service =
drive::DriveIntegrationServiceFactory::FindForProfile(
Profile::FromBrowserContext(browser_context()));
base::FilePath path;
if (!service ||
!service->GetRelativeDrivePath(file_system_url.path(), &path)) {
return RespondNow(Error("Drive is disabled"));
}
drivefs::mojom::DriveFs* const drivefs = service->GetDriveFsInterface();
if (!drivefs) {
return RespondNow(Error("Drive is disabled"));
}
drivefs->SetPinned(
path, pin,
mojo::WrapCallbackWithDefaultInvokeIfNotRun(
base::BindOnce(
&FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet,
this),
drive::FILE_ERROR_SERVICE_UNAVAILABLE));
return RespondLater();
}
void FileManagerPrivateInternalPinDriveFileFunction::OnPinStateSet(
drive::FileError error) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (error == drive::FILE_ERROR_OK) {
Respond(NoArguments());
} else {
Respond(Error(drive::FileErrorToString(error)));
}
}
FileManagerPrivateSearchDriveFunction::FileManagerPrivateSearchDriveFunction() {
SetWarningThresholds(kDriveSlowOperationThreshold,
kDriveVerySlowOperationThreshold);
}
ExtensionFunction::ResponseAction FileManagerPrivateSearchDriveFunction::Run() {
using extensions::api::file_manager_private::SearchDrive::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
if (!GetIntegrationServiceByProfile(
Profile::FromBrowserContext(browser_context()))) {
// |integration_service| is NULL if Drive is disabled or not mounted.
return RespondNow(Error("Drive is disabled"));
}
operation_start_ = base::TimeTicks::Now();
is_offline_ = content::GetNetworkConnectionTracker()->IsOffline();
auto query = drivefs::mojom::QueryParameters::New();
query->text_content = params->search_params.query;
if (params->search_params.modified_timestamp.has_value()) {
query->modified_time = base::Time::FromMillisecondsSinceUnixEpoch(
*params->search_params.modified_timestamp);
query->modified_time_operator =
drivefs::mojom::QueryParameters::DateComparisonOperator::kGreaterThan;
}
ash::RecentSource::FileType file_type;
if (!file_manager::util::ToRecentSourceFileType(
params->search_params.category, &file_type)) {
return RespondNow(Error("Unable to convert file category"));
}
auto type_filters = ash::RecentDriveSource::CreateTypeFilters(file_type);
if (type_filters.size() == 1) {
query->mime_type = type_filters.front();
} else if (type_filters.size() > 1) {
query->mime_types = std::move(type_filters);
}
is_offline_ =
SearchDriveFs(
this, std::move(query), false,
base::BindOnce(
&FileManagerPrivateSearchDriveFunction::OnSearchDriveFs, this)) ==
drivefs::mojom::QueryParameters::QuerySource::kLocalOnly;
return RespondLater();
}
void FileManagerPrivateSearchDriveFunction::OnSearchDriveFs(
std::optional<base::Value::List> results) {
using api::file_manager_private::SearchDriveResponse;
if (!results) {
UmaEmitSearchOutcome(
false, !is_offline_,
FileManagerPrivateSearchDriveMetadataFunction::SearchType::kText,
operation_start_);
Respond(Error("No search results"));
return;
}
SearchDriveResponse response;
// Search queries are capped at 100 of items anyway and pagination is
// never actually used, so no need to fill this.
response.next_feed = "";
response.entries.reserve(results.value().size());
for (const auto& e : results.value()) {
auto entry = SearchDriveResponse::EntriesType::FromValue(e);
if (!entry) {
LOG(ERROR) << "Failed to convert entry: " << e.DebugString();
continue;
}
response.entries.push_back(std::move(entry.value()));
}
UmaEmitSearchOutcome(
true, !is_offline_,
FileManagerPrivateSearchDriveMetadataFunction::SearchType::kText,
operation_start_);
Respond(WithArguments(response.ToValue()));
}
FileManagerPrivateSearchDriveMetadataFunction::
FileManagerPrivateSearchDriveMetadataFunction() {
SetWarningThresholds(kDriveSlowOperationThreshold,
kDriveVerySlowOperationThreshold);
}
ExtensionFunction::ResponseAction
FileManagerPrivateSearchDriveMetadataFunction::Run() {
using api::file_manager_private::SearchDriveMetadata::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
Profile* const profile = Profile::FromBrowserContext(browser_context());
if (drive::EventLogger* logger = file_manager::util::GetLogger(profile)) {
logger->Log(
logging::LOGGING_INFO, "%s[%s] called. (types: '%s', maxResults: '%d')",
name(), request_uuid().AsLowercaseString().c_str(),
api::file_manager_private::ToString(params->search_params.types),
params->search_params.max_results);
}
set_log_on_completion(true);
if (!GetIntegrationServiceByProfile(profile)) {
// |integration_service| is NULL if Drive is disabled or not mounted.
return RespondNow(Error("Drive not available"));
}
operation_start_ = base::TimeTicks::Now();
is_offline_ = true; // Legacy search is assumed offline always.
auto query = drivefs::mojom::QueryParameters::New();
query->sort_field = drivefs::mojom::QueryParameters::SortField::kLastModified;
query->sort_direction =
drivefs::mojom::QueryParameters::SortDirection::kDescending;
if (!params->search_params.query.empty()) {
query->title = params->search_params.query;
query->query_source =
drivefs::mojom::QueryParameters::QuerySource::kLocalOnly;
}
if (params->search_params.modified_timestamp.has_value()) {
query->modified_time = base::Time::FromMillisecondsSinceUnixEpoch(
*params->search_params.modified_timestamp);
query->modified_time_operator =
drivefs::mojom::QueryParameters::DateComparisonOperator::kGreaterThan;
}
ash::RecentSource::FileType file_type;
if (!file_manager::util::ToRecentSourceFileType(
params->search_params.category, &file_type)) {
return RespondNow(Error("Unable to convert file category"));
}
auto type_filters = ash::RecentDriveSource::CreateTypeFilters(file_type);
if (type_filters.size() == 1) {
query->mime_type = type_filters.front();
} else if (type_filters.size() > 1) {
query->mime_types = std::move(type_filters);
}
query->page_size = params->search_params.max_results;
bool filter_dirs = false;
switch (params->search_params.types) {
case api::file_manager_private::SearchType::kExcludeDirectories:
filter_dirs = true;
search_type_ = SearchType::kText;
break;
case api::file_manager_private::SearchType::kSharedWithMe:
query->shared_with_me = true;
search_type_ = SearchType::kSharedWithMe;
break;
case api::file_manager_private::SearchType::kOffline:
query->available_offline = true;
query->query_source =
drivefs::mojom::QueryParameters::QuerySource::kLocalOnly;
search_type_ = SearchType::kOffline;
break;
case api::file_manager_private::SearchType::kAll:
search_type_ = SearchType::kText;
break;
default:
return RespondNow(Error("Invalid search type"));
}
is_offline_ =
SearchDriveFs(
this, std::move(query), filter_dirs,
base::BindOnce(
&FileManagerPrivateSearchDriveMetadataFunction::OnSearchDriveFs,
this, params->search_params.query)) ==
drivefs::mojom::QueryParameters::QuerySource::kLocalOnly;
return RespondLater();
}
void FileManagerPrivateSearchDriveMetadataFunction::OnSearchDriveFs(
const std::string& query_text,
std::optional<base::Value::List> results) {
if (!results) {
UmaEmitSearchOutcome(false, !is_offline_, search_type_, operation_start_);
Respond(Error("No search results"));
return;
}
std::vector<std::u16string> keywords =
base::SplitString(base::UTF8ToUTF16(query_text),
std::u16string_view(base::kWhitespaceUTF16),
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
std::vector<std::unique_ptr<
base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents>>
queries;
queries.reserve(keywords.size());
for (const auto& keyword : keywords) {
queries.push_back(
std::make_unique<
base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents>(
keyword));
}
base::Value::List results_list;
for (auto& item : *results) {
base::Value::Dict& entry = item.GetDict();
base::Value::Dict dict;
std::string highlight;
std::string* value = entry.FindString("fileFullPath");
if (value) {
highlight = *value;
base::FilePath path(highlight);
if (!drive::internal::FindAndHighlight(path.BaseName().value(), queries,
&highlight)) {
highlight = path.BaseName().value();
}
}
if (auto availableOffline = entry.FindBool(kAvailableOfflinePropertyName)) {
dict.Set(kAvailableOfflinePropertyName, *availableOffline);
entry.Remove(kAvailableOfflinePropertyName);
}
dict.Set("entry", std::move(entry));
dict.Set("highlightedBaseName", highlight);
results_list.Append(std::move(dict));
}
UmaEmitSearchOutcome(true, !is_offline_, search_type_, operation_start_);
Respond(WithArguments(std::move(results_list)));
}
ExtensionFunction::ResponseAction
FileManagerPrivateGetDriveConnectionStateFunction::Run() {
api::file_manager_private::DriveConnectionState result;
using enum drive::util::ConnectionStatus;
switch (drive::util::GetDriveConnectionStatus(
Profile::FromBrowserContext(browser_context()))) {
case kNoService:
result.type =
api::file_manager_private::DriveConnectionStateType::kOffline;
result.reason = api::file_manager_private::DriveOfflineReason::kNoService;
break;
case kNoNetwork:
result.type =
api::file_manager_private::DriveConnectionStateType::kOffline;
result.reason = api::file_manager_private::DriveOfflineReason::kNoNetwork;
break;
case kNotReady:
result.type =
api::file_manager_private::DriveConnectionStateType::kOffline;
result.reason = api::file_manager_private::DriveOfflineReason::kNotReady;
break;
case kMetered:
result.type =
api::file_manager_private::DriveConnectionStateType::kMetered;
break;
case kConnected:
result.type =
api::file_manager_private::DriveConnectionStateType::kOnline;
break;
}
return RespondNow(ArgumentList(
api::file_manager_private::GetDriveConnectionState::Results::Create(
result)));
}
ExtensionFunction::ResponseAction
FileManagerPrivateNotifyDriveDialogResultFunction::Run() {
using api::file_manager_private::NotifyDriveDialogResult::Params;
const std::optional<Params> params = Params::Create(args());
EXTENSION_FUNCTION_VALIDATE(params);
file_manager::EventRouter* const event_router =
file_manager::EventRouterFactory::GetForProfile(
Profile::FromBrowserContext(browser_context()));
if (event_router) {
drivefs::mojom::DialogResult result;
switch (params->result) {
case api::file_manager_private::DriveDialogResult::kNone:
case api::file_manager_private::DriveDialogResult::kNotDisplayed:
result = drivefs::mojom::DialogResult::kNotDisplayed;
break;
case api::file_manager_private::DriveDialogResult::kAccept:
result = drivefs::mojom::DialogResult::kAccept;
break;
case api::file_manager_private::DriveDialogResult::kReject:
result = drivefs::mojom::DialogResult::kReject;
break;
case api::file_manager_private::DriveDialogResult::kDismiss:
result = drivefs::mojom::DialogResult::kDismiss;
break;
}
event_router->OnDriveDialogResult(result);
} else {
return RespondNow(Error("Could not find event router"));
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
FileManagerPrivatePollDriveHostedFilePinStatesFunction::Run() {
if (DriveIntegrationService* const service = GetIntegrationServiceByProfile(
Profile::FromBrowserContext(browser_context()))) {
service->PollHostedFilePinStates();
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
FileManagerPrivateGetBulkPinProgressFunction::Run() {
DriveIntegrationService* const service = GetIntegrationServiceByProfile(
Profile::FromBrowserContext(browser_context()));
if (!service) {
return RespondNow(Error("Drive not available"));
}
PinningManager* const p = service->GetPinningManager();
if (!p) {
return RespondNow(Error("Pin Manager not available"));
}
return RespondNow(ArgumentList(
api::file_manager_private::GetBulkPinProgress::Results::Create(
file_manager::util::BulkPinProgressToJs(p->GetProgress()))));
}
ExtensionFunction::ResponseAction
FileManagerPrivateOpenManageSyncSettingsFunction::Run() {
if (ash::features::IsDriveFsMirroringEnabled()) {
ash::ManageMirrorSyncDialog::Show(
Profile::FromBrowserContext(browser_context()));
}
return RespondNow(NoArguments());
}
ExtensionFunction::ResponseAction
FileManagerPrivateCalculateBulkPinRequiredSpaceFunction::Run() {
Profile* const profile = Profile::FromBrowserContext(browser_context());
DriveIntegrationService* const service =
GetIntegrationServiceByProfile(profile);
if (!service) {
return RespondNow(Error("Drive not available"));
}
PinningManager* const p = service->GetPinningManager();
if (!p) {
return RespondNow(Error("Pin Manager not available"));
}
if (!p->CalculateRequiredSpace()) {
return RespondNow(Error("Pin Manager is already pinning"));
}
return RespondNow(NoArguments());
}
} // namespace extensions