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