| // 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_tasks.h" |
| |
| #include <stddef.h> |
| |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "ash/webui/system_apps/public/system_web_app_type.h" |
| #include "base/command_line.h" |
| #include "base/functional/bind.h" |
| #include "chrome/browser/ash/drive/file_system_util.h" |
| #include "chrome/browser/ash/file_manager/app_service_file_tasks.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/filesystem_api_util.h" |
| #include "chrome/browser/ash/fileapi/file_system_backend.h" |
| #include "chrome/browser/extensions/chrome_extension_function_details.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/ash/system_web_apps/system_web_app_ui_utils.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/common/extensions/api/file_manager_private.h" |
| #include "chrome/common/extensions/api/file_manager_private_internal.h" |
| #include "extensions/browser/api/file_handlers/directory_util.h" |
| #include "extensions/browser/api/file_handlers/mime_util.h" |
| #include "extensions/browser/entry_info.h" |
| #include "storage/browser/file_system/file_system_context.h" |
| #include "storage/browser/file_system/file_system_url.h" |
| |
| namespace extensions { |
| namespace { |
| |
| // Error messages. |
| constexpr char kInvalidTaskType[] = "Invalid task type: "; |
| constexpr char kInvalidFileUrl[] = "Invalid file URL"; |
| |
| // Make a set of unique filename suffixes out of the list of file URLs. |
| std::set<std::string> GetUniqueSuffixes( |
| const std::vector<std::string>& file_urls, |
| const storage::FileSystemContext* context) { |
| std::set<std::string> suffixes; |
| for (const auto& file_url : file_urls) { |
| const storage::FileSystemURL url = |
| context->CrackURLInFirstPartyContext(GURL{file_url}); |
| if (!url.is_valid() || url.path().empty()) { |
| return {}; |
| } |
| // We'll skip empty suffixes. |
| if (!url.path().Extension().empty()) { |
| suffixes.insert(url.path().Extension()); |
| } |
| } |
| return suffixes; |
| } |
| |
| // Make a set of unique MIME types out of the list of MIME types. |
| std::set<std::string> GetUniqueMimeTypes( |
| const std::vector<std::string>& mime_type_list) { |
| std::set<std::string> mime_types; |
| for (const auto& mime_type : mime_type_list) { |
| // We'll skip empty MIME types and existing MIME types. |
| if (!mime_type.empty()) { |
| mime_types.insert(mime_type); |
| } |
| } |
| return mime_types; |
| } |
| |
| namespace api_fmp = extensions::api::file_manager_private; |
| namespace api_fmp_internal = extensions::api::file_manager_private_internal; |
| |
| std::optional<api_fmp::PolicyDefaultHandlerStatus> |
| RemapPolicyDefaultHandlerStatus( |
| const std::optional<file_manager::file_tasks::PolicyDefaultHandlerStatus>& |
| status) { |
| if (!status) { |
| return {}; |
| } |
| |
| switch (*status) { |
| case file_manager::file_tasks::PolicyDefaultHandlerStatus:: |
| kDefaultHandlerAssignedByPolicy: |
| return api_fmp::PolicyDefaultHandlerStatus:: |
| kDefaultHandlerAssignedByPolicy; |
| case file_manager::file_tasks::PolicyDefaultHandlerStatus:: |
| kIncorrectAssignment: |
| return api_fmp::PolicyDefaultHandlerStatus::kIncorrectAssignment; |
| } |
| } |
| |
| } // namespace |
| |
| FileManagerPrivateInternalExecuteTaskFunction:: |
| FileManagerPrivateInternalExecuteTaskFunction() = default; |
| |
| ExtensionFunction::ResponseAction |
| FileManagerPrivateInternalExecuteTaskFunction::Run() { |
| using api_fmp_internal::ExecuteTask::Params; |
| using api_fmp_internal::ExecuteTask::Results::Create; |
| const std::optional<Params> params = Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| file_manager::file_tasks::TaskType task_type = |
| file_manager::file_tasks::StringToTaskType(params->descriptor.task_type); |
| if (task_type == file_manager::file_tasks::TASK_TYPE_UNKNOWN) { |
| return RespondNow(Error(kInvalidTaskType + params->descriptor.task_type)); |
| } |
| file_manager::file_tasks::TaskDescriptor task( |
| params->descriptor.app_id, task_type, params->descriptor.action_id); |
| |
| if (params->urls.empty()) { |
| return RespondNow(ArgumentList(Create(api_fmp::TaskResult::kEmpty))); |
| } |
| |
| Profile* const profile = Profile::FromBrowserContext(browser_context()); |
| const scoped_refptr<storage::FileSystemContext> file_system_context = |
| file_manager::util::GetFileSystemContextForRenderFrameHost( |
| profile, render_frame_host()); |
| |
| std::vector<storage::FileSystemURL> urls; |
| for (const auto& url_param : params->urls) { |
| const storage::FileSystemURL url = |
| file_system_context->CrackURLInFirstPartyContext(GURL{url_param}); |
| if (!ash::FileSystemBackend::CanHandleURL(url)) { |
| return RespondNow(Error(kInvalidFileUrl)); |
| } |
| urls.push_back(url); |
| } |
| |
| const bool result = file_manager::file_tasks::ExecuteFileTask( |
| profile, task, urls, |
| base::BindOnce( |
| &FileManagerPrivateInternalExecuteTaskFunction::OnTaskExecuted, |
| this)); |
| if (!result) { |
| return RespondNow(Error("ExecuteFileTask failed")); |
| } |
| return RespondLater(); |
| } |
| |
| void FileManagerPrivateInternalExecuteTaskFunction::OnTaskExecuted( |
| api_fmp::TaskResult result, |
| std::string failure_reason) { |
| auto result_list = api_fmp_internal::ExecuteTask::Results::Create(result); |
| if (result == api_fmp::TaskResult::kFailed) { |
| Respond(Error("Task result failed: " + failure_reason)); |
| } else { |
| Respond(ArgumentList(std::move(result_list))); |
| } |
| } |
| |
| FileManagerPrivateInternalGetFileTasksFunction:: |
| FileManagerPrivateInternalGetFileTasksFunction() = default; |
| |
| FileManagerPrivateInternalGetFileTasksFunction:: |
| ~FileManagerPrivateInternalGetFileTasksFunction() = default; |
| |
| ExtensionFunction::ResponseAction |
| FileManagerPrivateInternalGetFileTasksFunction::Run() { |
| using api_fmp_internal::GetFileTasks::Params; |
| const std::optional<Params> params = Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| if (params->urls.empty()) { |
| return RespondNow(Error("No URLs provided")); |
| } |
| |
| if (params->dlp_source_urls.size() != params->urls.size()) { |
| return RespondNow(Error("Mismatching URLs and DLP source URLs provided")); |
| } |
| |
| Profile* const profile = Profile::FromBrowserContext(browser_context()); |
| const scoped_refptr<storage::FileSystemContext> file_system_context = |
| file_manager::util::GetFileSystemContextForRenderFrameHost( |
| profile, render_frame_host()); |
| |
| // Collect all the URLs, convert them to GURLs, and crack all the urls into |
| // file paths. |
| for (const auto& url_param : params->urls) { |
| const GURL url{url_param}; |
| storage::FileSystemURL file_system_url( |
| file_system_context->CrackURLInFirstPartyContext(url)); |
| if (!ash::FileSystemBackend::CanHandleURL(file_system_url)) { |
| continue; |
| } |
| urls_.push_back(url); |
| local_paths_.push_back(file_system_url.path()); |
| } |
| |
| dlp_source_urls_ = std::move(params->dlp_source_urls); |
| |
| mime_type_collector_ = |
| std::make_unique<app_file_handler_util::MimeTypeCollector>(profile); |
| mime_type_collector_->CollectForLocalPaths( |
| local_paths_, |
| base::BindOnce( |
| &FileManagerPrivateInternalGetFileTasksFunction::OnMimeTypesCollected, |
| this)); |
| |
| return RespondLater(); |
| } |
| |
| void FileManagerPrivateInternalGetFileTasksFunction::OnMimeTypesCollected( |
| std::unique_ptr<std::vector<std::string>> mime_types) { |
| is_directory_collector_ = |
| std::make_unique<app_file_handler_util::IsDirectoryCollector>( |
| Profile::FromBrowserContext(browser_context())); |
| is_directory_collector_->CollectForEntriesPaths( |
| local_paths_, |
| base::BindOnce(&FileManagerPrivateInternalGetFileTasksFunction:: |
| OnAreDirectoriesAndMimeTypesCollected, |
| this, std::move(mime_types))); |
| } |
| |
| void FileManagerPrivateInternalGetFileTasksFunction:: |
| OnAreDirectoriesAndMimeTypesCollected( |
| std::unique_ptr<std::vector<std::string>> mime_types, |
| std::unique_ptr<std::set<base::FilePath>> directory_paths) { |
| std::vector<EntryInfo> entries; |
| for (size_t i = 0; i < local_paths_.size(); ++i) { |
| entries.emplace_back( |
| local_paths_[i], (*mime_types)[i], |
| directory_paths->find(local_paths_[i]) != directory_paths->end()); |
| } |
| |
| file_manager::file_tasks::FindAllTypesOfTasks( |
| Profile::FromBrowserContext(browser_context()), entries, urls_, |
| dlp_source_urls_, |
| base::BindOnce( |
| &FileManagerPrivateInternalGetFileTasksFunction::OnFileTasksListed, |
| this)); |
| } |
| |
| void FileManagerPrivateInternalGetFileTasksFunction::OnFileTasksListed( |
| std::unique_ptr<file_manager::file_tasks::ResultingTasks> resulting_tasks) { |
| // Convert the tasks into JSON compatible objects. |
| std::vector<api_fmp::FileTask> results; |
| for (const auto& task : resulting_tasks->tasks) { |
| api_fmp::FileTask converted; |
| converted.descriptor.app_id = task.task_descriptor.app_id; |
| converted.descriptor.task_type = |
| TaskTypeToString(task.task_descriptor.task_type); |
| converted.descriptor.action_id = task.task_descriptor.action_id; |
| if (!task.icon_url.is_empty()) { |
| converted.icon_url = task.icon_url.spec(); |
| } |
| converted.title = task.task_title; |
| converted.is_default = task.is_default; |
| converted.is_generic_file_handler = task.is_generic_file_handler; |
| converted.is_dlp_blocked = task.is_dlp_blocked; |
| results.push_back(std::move(converted)); |
| } |
| |
| api_fmp::ResultingTasks api_resulting_tasks; |
| |
| api_resulting_tasks.tasks = std::move(results); |
| if (auto status = RemapPolicyDefaultHandlerStatus( |
| resulting_tasks->policy_default_handler_status)) { |
| api_resulting_tasks.policy_default_handler_status = *status; |
| } |
| |
| Respond(ArgumentList(api_fmp_internal::GetFileTasks::Results::Create( |
| std::move(api_resulting_tasks)))); |
| } |
| |
| ExtensionFunction::ResponseAction |
| FileManagerPrivateInternalSetDefaultTaskFunction::Run() { |
| using api_fmp_internal::SetDefaultTask::Params; |
| const std::optional<Params> params = Params::Create(args()); |
| EXTENSION_FUNCTION_VALIDATE(params); |
| |
| Profile* profile = Profile::FromBrowserContext(browser_context()); |
| const scoped_refptr<storage::FileSystemContext> file_system_context = |
| file_manager::util::GetFileSystemContextForRenderFrameHost( |
| profile, render_frame_host()); |
| |
| const std::set<std::string> suffixes = |
| GetUniqueSuffixes(params->urls, file_system_context.get()); |
| const std::set<std::string> mime_types = |
| GetUniqueMimeTypes(params->mime_types); |
| |
| // If there weren't any mime_types, and all the suffixes were blank, |
| // then we "succeed", but don't actually associate with anything. |
| // Otherwise, any time we set the default on a file with no extension |
| // on the local drive, we'd fail. |
| // TODO(gspencer): Fix file manager so that it never tries to set default in |
| // cases where extensionless local files are part of the selection. |
| if (suffixes.empty() && mime_types.empty()) { |
| return RespondNow(WithArguments(true)); |
| } |
| |
| file_manager::file_tasks::TaskType task_type = |
| file_manager::file_tasks::StringToTaskType(params->descriptor.task_type); |
| if (task_type == file_manager::file_tasks::TASK_TYPE_UNKNOWN) { |
| return RespondNow(Error(kInvalidTaskType + params->descriptor.task_type)); |
| } |
| file_manager::file_tasks::TaskDescriptor descriptor( |
| params->descriptor.app_id, task_type, params->descriptor.action_id); |
| |
| file_manager::file_tasks::UpdateDefaultTask(profile, descriptor, suffixes, |
| mime_types); |
| return RespondNow(NoArguments()); |
| } |
| |
| } // namespace extensions |