blob: d8c9040dce2f09da39781b05cc94cae1870dbd7a [file] [log] [blame]
// Copyright 2012 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/file_manager/file_tasks.h"
#include <stddef.h>
#include <cstddef>
#include <map>
#include <string>
#include <utility>
#include "apps/launcher.h"
#include "ash/constants/ash_features.h"
#include "ash/webui/file_manager/url_constants.h"
#include "base/containers/contains.h"
#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/metrics/user_metrics.h"
#include "base/metrics/user_metrics_action.h"
#include "base/notreached.h"
#include "base/ranges/algorithm.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "chrome/browser/apps/app_service/app_launch_params.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/metrics/app_platform_metrics.h"
#include "chrome/browser/apps/app_service/metrics/app_service_metrics.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/app_id.h"
#include "chrome/browser/ash/file_manager/app_service_file_tasks.h"
#include "chrome/browser/ash/file_manager/arc_file_tasks.h"
#include "chrome/browser/ash/file_manager/file_browser_handlers.h"
#include "chrome/browser/ash/file_manager/file_tasks_notifier.h"
#include "chrome/browser/ash/file_manager/fileapi_util.h"
#include "chrome/browser/ash/file_manager/filesystem_api_util.h"
#include "chrome/browser/ash/file_manager/open_util.h"
#include "chrome/browser/ash/file_manager/open_with_browser.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_info.h"
#include "chrome/browser/ash/file_system_provider/provided_file_system_interface.h"
#include "chrome/browser/ash/file_system_provider/service.h"
#include "chrome/browser/ash/fileapi/file_system_backend.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/extensions/launch_util.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/webui/ash/cloud_upload/cloud_upload.mojom-shared.h"
#include "chrome/browser/ui/webui/ash/cloud_upload/cloud_upload_dialog.h"
#include "chrome/browser/ui/webui/ash/office_fallback/office_fallback_ui.h"
#include "chrome/browser/ui/webui/extensions/extension_icon_source.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h"
#include "components/drive/drive_api_util.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/services/app_service/public/cpp/app_types.h"
#include "components/services/app_service/public/cpp/app_update.h"
#include "components/services/app_service/public/cpp/file_handler.h"
#include "components/services/app_service/public/cpp/file_handler_info.h"
#include "content/public/browser/network_service_instance.h"
#include "extensions/browser/api/file_handlers/mime_util.h"
#include "extensions/browser/entry_info.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/extension_util.h"
#include "extensions/common/extension_set.h"
#include "net/base/mime_util.h"
#include "pdf/buildflags.h"
#include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h"
#include "third_party/blink/public/common/features.h"
#include "third_party/blink/public/common/mime_util/mime_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/window_open_disposition.h"
#include "ui/chromeos/strings/grit/ui_chromeos_strings.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "url/gurl.h"
using ash::file_manager::kChromeUIFileManagerURL;
using extensions::Extension;
namespace file_manager::file_tasks {
const char kActionIdView[] = "view";
const char kActionIdSend[] = "send";
const char kActionIdSendMultiple[] = "send_multiple";
const char kActionIdQuickOffice[] = "qo_documents";
const char kActionIdWebDriveOfficeWord[] = "open-web-drive-office-word";
const char kActionIdWebDriveOfficeExcel[] = "open-web-drive-office-excel";
const char kActionIdWebDriveOfficePowerPoint[] =
"open-web-drive-office-powerpoint";
const char kActionIdOpenInOffice[] = "open-in-office";
const char kActionIdOpenWeb[] = "OPEN_WEB";
// Searches for the installed extension in order of preference.
std::string GetODFSExtensionId(Profile* profile) {
const ash::file_system_provider::Service* const service =
ash::file_system_provider::Service::Get(profile);
for (const auto& [provider_id, provider] : service->GetProviders()) {
if (provider_id.GetType() !=
ash::file_system_provider::ProviderId::EXTENSION) {
continue;
}
const auto& extension_id = provider_id.GetExtensionId();
// App from official internal build.
if (extension_id == extension_misc::kODFSExtensionId) {
return extension_misc::kODFSExtensionId;
}
// App built manually from internal repo.
if (extension_id == "gcpjnalmmghdoadafjgomdlghfnllceo") {
return "gcpjnalmmghdoadafjgomdlghfnllceo";
}
// App built manually from internal git, used for the early dogfood.
if (extension_id == "ajdgmkbkgifbokednjgbmieaemeighkg") {
return "ajdgmkbkgifbokednjgbmieaemeighkg";
}
}
return {};
}
namespace {
// The values "file" and "app" are confusing, but cannot be changed easily as
// these are used in default task IDs stored in preferences.
const char kFileBrowserHandlerTaskType[] = "file";
const char kFileHandlerTaskType[] = "app";
const char kArcAppTaskType[] = "arc";
const char kBruschettaAppTaskType[] = "bruschetta";
const char kCrostiniAppTaskType[] = "crostini";
const char kPluginVmAppTaskType[] = "pluginvm";
const char kWebAppTaskType[] = "web";
constexpr char kPdfMimeType[] = "application/pdf";
constexpr char kPdfFileExtension[] = ".pdf";
void RecordChangesInDefaultPdfApp(const std::string& new_default_app_id,
const std::set<std::string>& mime_types,
const std::set<std::string>& suffixes) {
bool hasPdfMimeType = base::Contains(mime_types, kPdfMimeType);
bool hasPdfSuffix = base::Contains(suffixes, kPdfFileExtension);
if (!hasPdfMimeType || !hasPdfSuffix) {
return;
}
if (new_default_app_id == web_app::kMediaAppId) {
base::RecordAction(
base::UserMetricsAction("MediaApp.PDF.DefaultApp.SwitchedTo"));
} else {
base::RecordAction(
base::UserMetricsAction("MediaApp.PDF.DefaultApp.SwitchedAway"));
}
}
// Returns True if the `app_id` belongs to Files app either extension or SWA.
inline bool IsFilesAppId(const std::string& app_id) {
return app_id == kFileManagerAppId || app_id == kFileManagerSwaAppId;
}
// The SWA actionId is prefixed with chrome://file-manager/?ACTION_ID, just the
// sub-string compatible with the extension/legacy e.g.: "view-pdf".
std::string ParseFilesAppActionId(const std::string& action_id) {
if (base::StartsWith(action_id, kChromeUIFileManagerURL)) {
std::string result(action_id);
base::ReplaceFirstSubstringAfterOffset(
&result, 0, base::StrCat({kChromeUIFileManagerURL, "?"}), "");
return result;
}
return action_id;
}
// Returns true if path_mime_set contains a Google document.
bool ContainsGoogleDocument(const std::vector<extensions::EntryInfo>& entries) {
for (const auto& it : entries) {
if (drive::util::HasHostedDocumentExtension(it.path)) {
return true;
}
}
return false;
}
// Removes all tasks except tasks handled by file manager.
void KeepOnlyFileManagerInternalTasks(std::vector<FullTaskDescriptor>* tasks) {
std::vector<FullTaskDescriptor> filtered;
for (FullTaskDescriptor& task : *tasks) {
if (IsFilesAppId(task.task_descriptor.app_id)) {
filtered.push_back(std::move(task));
}
}
tasks->swap(filtered);
}
// Removes task |actions| handled by file manager.
void RemoveFileManagerInternalActions(const std::set<std::string>& actions,
std::vector<FullTaskDescriptor>* tasks) {
std::vector<FullTaskDescriptor> filtered;
for (FullTaskDescriptor& task : *tasks) {
const auto& action = task.task_descriptor.action_id;
if (!IsFilesAppId(task.task_descriptor.app_id)) {
filtered.push_back(std::move(task));
} else if (actions.find(ParseFilesAppActionId(action)) == actions.end()) {
filtered.push_back(std::move(task));
}
}
tasks->swap(filtered);
}
// Removes tasks handled by |app_id|".
void RemoveActionsForApp(const std::string& app_id,
std::vector<FullTaskDescriptor>* tasks) {
std::vector<FullTaskDescriptor> filtered;
for (FullTaskDescriptor& task : *tasks) {
if (app_id != task.task_descriptor.app_id) {
filtered.push_back(std::move(task));
}
}
tasks->swap(filtered);
}
// Adjusts |tasks| to reflect the product decision that chrome://media-app
// should behave more like a user-installed app than a fallback handler.
// Specifically, only apps set as the default in user prefs should be preferred
// over chrome://media-app.
void AdjustTasksForMediaApp(const std::vector<extensions::EntryInfo>& entries,
std::vector<FullTaskDescriptor>* tasks) {
const auto task_for_app = [&](const std::string& app_id) {
return base::ranges::find(*tasks, app_id, [](const auto& task) {
return task.task_descriptor.app_id;
});
};
const auto media_app_task = task_for_app(web_app::kMediaAppId);
if (media_app_task == tasks->end()) {
return;
}
// TOOD(crbug/1071289): For a while is_file_extension_match would always be
// false for System Web App manifests, even when specifying extension matches.
// So this line can be removed once the media app manifest is updated with a
// full complement of image file extensions.
media_app_task->is_file_extension_match = true;
// The logic in ChooseAndSetDefaultTask() also requires the following to hold.
// This should only fail if the media app is configured for "*".
// "image/*" does not count as "generic".
DCHECK(!media_app_task->is_generic_file_handler);
// Otherwise, build a new list with Media App at the front.
if (media_app_task == tasks->begin()) {
return;
}
std::vector<FullTaskDescriptor> new_tasks;
new_tasks.push_back(*media_app_task);
for (auto it = tasks->begin(); it != tasks->end(); ++it) {
if (it != media_app_task) {
new_tasks.push_back(std::move(*it));
}
}
std::swap(*tasks, new_tasks);
}
// Returns true if the given task is a handler by built-in apps like the Files
// app itself or QuickOffice etc. They are used as the initial default app.
bool IsFallbackFileHandler(const FullTaskDescriptor& task) {
if ((task.task_descriptor.task_type !=
file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER &&
task.task_descriptor.task_type != file_tasks::TASK_TYPE_FILE_HANDLER &&
task.task_descriptor.task_type != file_tasks::TASK_TYPE_WEB_APP) ||
task.is_generic_file_handler) {
return false;
}
// Note that web_app::kMediaAppId does not appear in the
// list of built-in apps below. Doing so would mean the presence of any other
// handler of image files (e.g. Keep, Photos) would take precedence. But we
// want that only to occur if the user has explicitly set the preference for
// an app other than kMediaAppId to be the default (b/153387960).
constexpr const char* kBuiltInApps[] = {
kFileManagerAppId,
kFileManagerSwaAppId,
kTextEditorAppId,
extension_misc::kQuickOfficeComponentExtensionId,
extension_misc::kQuickOfficeInternalExtensionId,
extension_misc::kQuickOfficeExtensionId};
return base::Contains(kBuiltInApps, task.task_descriptor.app_id);
}
// Gets the profile in which a file task owned by |extension| should be
// launched - for example, it makes sure that a file task is not handled in OTR
// profile for platform apps (outside a guest session).
Profile* GetProfileForExtensionTask(Profile* profile,
const extensions::Extension& extension) {
// In guest profile, all available task handlers are in OTR profile.
if (profile->IsGuestSession()) {
DCHECK(profile->IsOffTheRecord());
return profile;
}
// Outside guest sessions, if the task is handled by a platform app, launch
// the handler in the original profile.
if (extension.is_platform_app()) {
return profile->GetOriginalProfile();
}
return profile;
}
void ExecuteTaskAfterMimeTypesCollected(
Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
FileTaskFinishedCallback done,
extensions::app_file_handler_util::MimeTypeCollector* mime_collector,
std::unique_ptr<std::vector<std::string>> mime_types) {
if (task.task_type == TASK_TYPE_ARC_APP &&
!ash::features::ShouldArcFileTasksUseAppService()) {
apps::RecordAppLaunchMetrics(profile, apps::AppType::kArc, task.app_id,
apps::LaunchSource::kFromFileManager,
apps::LaunchContainer::kLaunchContainerWindow);
ExecuteArcTask(profile, task, file_urls, *mime_types, std::move(done));
} else {
ExecuteAppServiceTask(profile, task, file_urls, *mime_types,
std::move(done));
}
}
void PostProcessFoundTasks(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
FindTasksCallback callback,
std::unique_ptr<ResultingTasks> resulting_tasks) {
AdjustTasksForMediaApp(entries, &resulting_tasks->tasks);
// Google documents can only be handled by internal handlers.
if (ContainsGoogleDocument(entries)) {
KeepOnlyFileManagerInternalTasks(&resulting_tasks->tasks);
}
std::set<std::string> disabled_actions;
#if !BUILDFLAG(ENABLE_PDF)
disabled_actions.emplace("view-pdf");
#endif // !BUILDFLAG(ENABLE_PDF)
if (!ash::cloud_upload::IsEligibleAndEnabledUploadOfficeToCloud(profile)) {
disabled_actions.emplace(kActionIdWebDriveOfficeWord);
disabled_actions.emplace(kActionIdWebDriveOfficeExcel);
disabled_actions.emplace(kActionIdWebDriveOfficePowerPoint);
} else {
// Hide the office PWA File Handler.
RemoveActionsForApp(web_app::kMicrosoft365AppId, &resulting_tasks->tasks);
// Hack around the fact that App Service will only return one task for each
// app. We want both tasks to be available, so add the office task if the
// WebDrive task is available.
// TODO(petermarshall): Find a better way to enable both tasks.
auto it = base::ranges::find_if(
resulting_tasks->tasks, [](const FullTaskDescriptor& task) {
if (!IsFilesAppId(task.task_descriptor.app_id)) {
return false;
}
std::string action_id =
ParseFilesAppActionId(task.task_descriptor.action_id);
return action_id == kActionIdWebDriveOfficeWord ||
action_id == kActionIdWebDriveOfficeExcel ||
action_id == kActionIdWebDriveOfficePowerPoint;
});
if (it != resulting_tasks->tasks.end()) {
FullTaskDescriptor office_task(*it);
office_task.task_descriptor.action_id =
base::StrCat({kChromeUIFileManagerURL, "?", kActionIdOpenInOffice});
resulting_tasks->tasks.push_back(office_task);
}
}
if (!disabled_actions.empty()) {
RemoveFileManagerInternalActions(disabled_actions, &resulting_tasks->tasks);
}
ChooseAndSetDefaultTask(profile, entries, resulting_tasks.get());
std::move(callback).Run(std::move(resulting_tasks));
}
// Returns true if |extension_id| and |action_id| indicate that the file
// currently being handled should be opened with the browser. This function
// is used to handle certain action IDs of the file manager.
bool ShouldBeOpenedWithBrowser(const std::string& extension_id,
const std::string& action_id) {
return IsFilesAppId(extension_id) &&
(action_id == "view-pdf" || action_id == "view-in-browser" ||
action_id == "open-hosted-generic" ||
action_id == "open-hosted-gdoc" ||
action_id == "open-hosted-gsheet" ||
action_id == "open-hosted-gslides");
}
// Opens the files specified by |file_urls| with the browser for |profile|.
// Returns true on success. It's a failure if no files are opened.
bool OpenFilesWithBrowser(Profile* profile,
const std::vector<FileSystemURL>& file_urls,
const std::string& action_id) {
int num_opened = 0;
for (const FileSystemURL& file_url : file_urls) {
if (ash::FileSystemBackend::CanHandleURL(file_url)) {
num_opened +=
util::OpenFileWithBrowser(profile, file_url, action_id) ? 1 : 0;
}
}
return num_opened > 0;
}
bool ExecuteWebDriveOfficeTask(Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent) {
bool offline = drive::util::GetDriveConnectionStatus(profile) !=
drive::util::DRIVE_CONNECTED;
if (offline) {
UMA_HISTOGRAM_ENUMERATION(kDriveErrorMetricName,
OfficeDriveErrors::OFFLINE);
// TODO(petermarshall): Quick Office vs. other default handler.
return GetUserFallbackChoice(
profile, task, file_urls, modal_parent,
ash::office_fallback::FallbackReason::kOffline);
}
drive::DriveIntegrationService* integration_service =
drive::DriveIntegrationServiceFactory::FindForProfile(profile);
if (integration_service && integration_service->IsMounted() &&
integration_service->GetDriveFsInterface()) {
return ash::cloud_upload::CloudOpenTask::Execute(
profile, file_urls, ash::cloud_upload::CloudProvider::kGoogleDrive,
modal_parent);
} else {
UMA_HISTOGRAM_ENUMERATION(kDriveErrorMetricName,
OfficeDriveErrors::DRIVEFS_INTERFACE);
return GetUserFallbackChoice(
profile, task, file_urls, modal_parent,
ash::office_fallback::FallbackReason::kDriveUnavailable);
}
}
using ash::file_system_provider::ProvidedFileSystemInfo;
using ash::file_system_provider::ProviderId;
using ash::file_system_provider::Service;
bool ExecuteOpenInOfficeTask(Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent) {
if (content::GetNetworkConnectionTracker()->IsOffline()) {
return GetUserFallbackChoice(
profile, task, file_urls, modal_parent,
ash::office_fallback::FallbackReason::kOffline);
// TODO(petermarshall): UMAs.
}
return ash::cloud_upload::CloudOpenTask::Execute(
profile, file_urls, ash::cloud_upload::CloudProvider::kOneDrive,
modal_parent);
}
} // namespace
ResultingTasks::ResultingTasks() = default;
ResultingTasks::~ResultingTasks() = default;
void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable* registry) {
registry->RegisterDictionaryPref(prefs::kDefaultHandlersForFileExtensions);
registry->RegisterBooleanPref(
prefs::kOfficeSetupComplete, false,
user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF);
registry->RegisterBooleanPref(prefs::kOfficeFilesAlwaysMoveToDrive, false);
registry->RegisterBooleanPref(prefs::kOfficeFilesAlwaysMoveToOneDrive, false);
registry->RegisterBooleanPref(prefs::kOfficeMoveConfirmationShownForDrive,
false);
registry->RegisterBooleanPref(prefs::kOfficeMoveConfirmationShownForOneDrive,
false);
registry->RegisterBooleanPref(
prefs::kOfficeMoveConfirmationShownForLocalToDrive, false);
registry->RegisterBooleanPref(
prefs::kOfficeMoveConfirmationShownForLocalToOneDrive, false);
registry->RegisterBooleanPref(
prefs::kOfficeMoveConfirmationShownForCloudToDrive, false);
registry->RegisterBooleanPref(
prefs::kOfficeMoveConfirmationShownForCloudToOneDrive, false);
registry->RegisterTimePref(prefs::kOfficeFileMovedToOneDrive, base::Time());
registry->RegisterTimePref(prefs::kOfficeFileMovedToGoogleDrive,
base::Time());
}
// Converts a string to a TaskType. Returns TASK_TYPE_UNKNOWN on error.
TaskType StringToTaskType(const std::string& str) {
if (str == kFileBrowserHandlerTaskType) {
return TASK_TYPE_FILE_BROWSER_HANDLER;
}
if (str == kFileHandlerTaskType) {
return TASK_TYPE_FILE_HANDLER;
}
if (str == kArcAppTaskType) {
return TASK_TYPE_ARC_APP;
}
if (str == kBruschettaAppTaskType) {
return TASK_TYPE_BRUSCHETTA_APP;
}
if (str == kCrostiniAppTaskType) {
return TASK_TYPE_CROSTINI_APP;
}
if (str == kWebAppTaskType) {
return TASK_TYPE_WEB_APP;
}
if (str == kPluginVmAppTaskType) {
return TASK_TYPE_PLUGIN_VM_APP;
}
return TASK_TYPE_UNKNOWN;
}
// Converts a TaskType to a string.
std::string TaskTypeToString(TaskType task_type) {
switch (task_type) {
case TASK_TYPE_FILE_BROWSER_HANDLER:
return kFileBrowserHandlerTaskType;
case TASK_TYPE_FILE_HANDLER:
return kFileHandlerTaskType;
case TASK_TYPE_ARC_APP:
return kArcAppTaskType;
case TASK_TYPE_BRUSCHETTA_APP:
return kBruschettaAppTaskType;
case TASK_TYPE_CROSTINI_APP:
return kCrostiniAppTaskType;
case TASK_TYPE_WEB_APP:
return kWebAppTaskType;
case TASK_TYPE_PLUGIN_VM_APP:
return kPluginVmAppTaskType;
case TASK_TYPE_UNKNOWN:
case DEPRECATED_TASK_TYPE_DRIVE_APP:
case NUM_TASK_TYPE:
break;
}
NOTREACHED();
return "";
}
bool TaskDescriptor::operator<(const TaskDescriptor& other) const {
if (app_id < other.app_id) {
return true;
} else if (app_id > other.app_id) {
return false;
}
// If we're here, it's because app_id == other.app_id.
if (task_type < other.task_type) {
return true;
} else if (task_type > other.task_type) {
return false;
}
// If we're here, it's because task_type == other.task_type.
if (action_id < other.action_id) {
return true;
} else {
return false;
}
}
bool TaskDescriptor::operator==(const TaskDescriptor& other) const {
if (app_id != other.app_id) {
return false;
}
if (task_type != other.task_type) {
return false;
}
if (action_id != other.action_id) {
return false;
}
return true;
}
FullTaskDescriptor::FullTaskDescriptor(const TaskDescriptor& in_task_descriptor,
const std::string& in_task_title,
const GURL& in_icon_url,
bool in_is_default,
bool in_is_generic_file_handler,
bool in_is_file_extension_match,
bool is_dlp_blocked)
: task_descriptor(in_task_descriptor),
task_title(in_task_title),
icon_url(in_icon_url),
is_default(in_is_default),
is_generic_file_handler(in_is_generic_file_handler),
is_file_extension_match(in_is_file_extension_match),
is_dlp_blocked(is_dlp_blocked) {}
FullTaskDescriptor::FullTaskDescriptor(const FullTaskDescriptor& other) =
default;
FullTaskDescriptor& FullTaskDescriptor::operator=(
const FullTaskDescriptor& other) = default;
void UpdateDefaultTask(Profile* profile,
const TaskDescriptor& task_descriptor,
const std::set<std::string>& suffixes,
const std::set<std::string>& mime_types) {
PrefService* pref_service = profile->GetPrefs();
if (!pref_service) {
return;
}
std::string task_id = TaskDescriptorToId(task_descriptor);
if (ash::features::ShouldArcFileTasksUseAppService() &&
task_descriptor.task_type == TASK_TYPE_ARC_APP) {
// Task IDs for Android apps are stored in a legacy format (app id:
// "<package>/<activity>", task id: "view"). For ARC app task descriptors
// (which use app id: "<app service id>", action id: "<activity>"), we
// generate Task IDs in the legacy format.
std::string package;
DCHECK(
apps::AppServiceProxyFactory::IsAppServiceAvailableForProfile(profile));
apps::AppServiceProxy* proxy =
apps::AppServiceProxyFactory::GetForProfile(profile);
if (proxy) {
proxy->AppRegistryCache().ForOneApp(
task_descriptor.app_id, [&package](const apps::AppUpdate& update) {
package = update.PublisherId();
});
}
if (!package.empty()) {
std::string new_app_id = package + "/" + task_descriptor.action_id;
task_id = MakeTaskID(new_app_id, TASK_TYPE_ARC_APP, kActionIdView);
}
}
if (!mime_types.empty()) {
ScopedDictPrefUpdate mime_type_pref(pref_service,
prefs::kDefaultTasksByMimeType);
for (const std::string& mime_type : mime_types) {
mime_type_pref->Set(mime_type, task_id);
}
}
std::set<std::string> lowercase_suffixes;
if (!suffixes.empty()) {
ScopedDictPrefUpdate mime_type_pref(pref_service,
prefs::kDefaultTasksBySuffix);
for (const std::string& suffix : suffixes) {
// Suffixes are case insensitive.
std::string lower_suffix = base::ToLowerASCII(suffix);
lowercase_suffixes.insert(lower_suffix);
mime_type_pref->Set(lower_suffix, task_id);
}
}
RecordChangesInDefaultPdfApp(task_descriptor.app_id, mime_types,
lowercase_suffixes);
}
bool GetDefaultTaskFromPrefs(const PrefService& pref_service,
const std::string& mime_type,
const std::string& suffix,
TaskDescriptor* task_out) {
VLOG(1) << "Looking for default for MIME type: " << mime_type
<< " and suffix: " << suffix;
if (!mime_type.empty()) {
const base::Value::Dict& mime_task_prefs =
pref_service.GetDict(prefs::kDefaultTasksByMimeType);
const std::string* task_id = mime_task_prefs.FindString(mime_type);
if (task_id) {
VLOG(1) << "Found MIME default handler: " << *task_id;
return ParseTaskID(*task_id, task_out);
}
}
const base::Value::Dict& suffix_task_prefs =
pref_service.GetDict(prefs::kDefaultTasksBySuffix);
std::string lower_suffix = base::ToLowerASCII(suffix);
const std::string* task_id = suffix_task_prefs.FindString(lower_suffix);
if (!task_id || task_id->empty()) {
return false;
}
VLOG(1) << "Found suffix default handler: " << *task_id;
return ParseTaskID(*task_id, task_out);
}
std::string MakeTaskID(const std::string& app_id,
TaskType task_type,
const std::string& action_id) {
return base::StringPrintf("%s|%s|%s", app_id.c_str(),
TaskTypeToString(task_type).c_str(),
action_id.c_str());
}
std::string TaskDescriptorToId(const TaskDescriptor& task_descriptor) {
return MakeTaskID(task_descriptor.app_id, task_descriptor.task_type,
task_descriptor.action_id);
}
bool ParseTaskID(const std::string& task_id, TaskDescriptor* task) {
DCHECK(task);
std::vector<std::string> result = base::SplitString(
task_id, "|", base::KEEP_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// Parse a legacy task ID that only contain two parts. The legacy task IDs
// can be stored in preferences.
if (result.size() == 2) {
task->task_type = TASK_TYPE_FILE_BROWSER_HANDLER;
task->app_id = result[0];
task->action_id = result[1];
return true;
}
if (result.size() != 3) {
return false;
}
TaskType task_type = StringToTaskType(result[1]);
if (task_type == TASK_TYPE_UNKNOWN) {
return false;
}
task->app_id = result[0];
task->task_type = task_type;
task->action_id = result[2];
return true;
}
bool ExecuteFileTask(Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent,
FileTaskFinishedCallback done) {
UMA_HISTOGRAM_ENUMERATION("FileBrowser.ViewingTaskType", task.task_type,
NUM_TASK_TYPE);
if (drive::util::GetDriveConnectionStatus(profile) ==
drive::util::DRIVE_DISCONNECTED_NONETWORK) {
UMA_HISTOGRAM_ENUMERATION("FileBrowser.ViewingTaskType.Offline",
task.task_type, NUM_TASK_TYPE);
} else {
UMA_HISTOGRAM_ENUMERATION("FileBrowser.ViewingTaskType.Online",
task.task_type, NUM_TASK_TYPE);
}
// TODO(crbug.com/1005640): Move recording this metric to the App Service when
// file handling is supported there.
apps::RecordAppLaunch(task.app_id, apps::LaunchSource::kFromFileManager);
if (auto* notifier = FileTasksNotifier::GetForProfile(profile)) {
notifier->NotifyFileTasks(file_urls);
}
const std::string parsed_action_id(ParseFilesAppActionId(task.action_id));
if (IsWebDriveOfficeTask(task)) {
const bool started =
ExecuteWebDriveOfficeTask(profile, task, file_urls, modal_parent);
if (done) {
if (started) {
std::move(done).Run(
extensions::api::file_manager_private::TASK_RESULT_OPENED, "");
} else {
std::move(done).Run(
extensions::api::file_manager_private::TASK_RESULT_FAILED, "");
}
}
return true;
}
if (IsOpenInOfficeTask(task)) {
const bool started =
ExecuteOpenInOfficeTask(profile, task, file_urls, modal_parent);
if (done) {
if (started) {
std::move(done).Run(
extensions::api::file_manager_private::TASK_RESULT_OPENED, "");
} else {
std::move(done).Run(
extensions::api::file_manager_private::TASK_RESULT_FAILED, "");
}
}
return true;
}
// Some action IDs of the file manager's file browser handlers require the
// files to be directly opened with the browser. In a multiprofile session
// this will always open on the current desktop, regardless of which profile
// owns the files, so return TASK_RESULT_OPENED.
if (ShouldBeOpenedWithBrowser(task.app_id, parsed_action_id)) {
const bool result =
OpenFilesWithBrowser(profile, file_urls, parsed_action_id);
if (result && done) {
std::move(done).Run(
extensions::api::file_manager_private::TASK_RESULT_OPENED, "");
}
return result;
}
for (const FileSystemURL& file_url : file_urls) {
if (file_manager::util::IsDriveLocalPath(profile, file_url.path()) &&
file_manager::file_tasks::IsOfficeFile(file_url.path())) {
UMA_HISTOGRAM_ENUMERATION(
file_manager::file_tasks::kUseOutsideDriveMetricName,
file_manager::file_tasks::OfficeFilesUseOutsideDriveHook::
OPEN_FROM_FILES_APP);
}
}
// Open Files SWA if the task is for Files app.
if (IsFilesAppId(task.app_id)) {
std::u16string title;
const GURL destination_entry =
file_urls.size() ? file_urls[0].ToGURL() : GURL();
ui::SelectFileDialog::FileTypeInfo file_type_info;
file_type_info.allowed_paths =
ui::SelectFileDialog::FileTypeInfo::ANY_PATH_OR_URL;
GURL files_swa_url =
::file_manager::util::GetFileManagerMainPageUrlWithParams(
ui::SelectFileDialog::SELECT_NONE, title,
/*current_directory_url=*/{},
/*selection_url=*/destination_entry,
/*target_name=*/{}, &file_type_info,
/*file_type_index=*/0,
/*search_query=*/{},
/*show_android_picker_apps=*/false,
/*volume_filter=*/{});
ash::SystemAppLaunchParams params;
params.url = files_swa_url;
ash::LaunchSystemWebAppAsync(profile, ash::SystemWebAppType::FILE_MANAGER,
params);
if (done) {
std::move(done).Run(
extensions::api::file_manager_private::TASK_RESULT_OPENED, "");
}
return true;
}
// Apps from App Service need mime types for launching. Retrieve them first.
if (task.task_type == TASK_TYPE_ARC_APP ||
task.task_type == TASK_TYPE_WEB_APP ||
task.task_type == TASK_TYPE_FILE_HANDLER ||
task.task_type == TASK_TYPE_BRUSCHETTA_APP ||
task.task_type == TASK_TYPE_CROSTINI_APP ||
task.task_type == TASK_TYPE_PLUGIN_VM_APP) {
// TODO(petermarshall): Implement GetProfileForExtensionTask in Lacros if
// necessary, for Chrome Apps.
extensions::app_file_handler_util::MimeTypeCollector* mime_collector =
new extensions::app_file_handler_util::MimeTypeCollector(profile);
mime_collector->CollectForURLs(
file_urls, base::BindOnce(&ExecuteTaskAfterMimeTypesCollected, profile,
task, file_urls, std::move(done),
base::Owned(mime_collector)));
return true;
}
// Execute a file_browser_handler task in an Extension.
if (task.task_type == TASK_TYPE_FILE_BROWSER_HANDLER) {
// Get the extension.
const Extension* extension = extensions::ExtensionRegistry::Get(profile)
->enabled_extensions()
.GetByID(task.app_id);
if (!extension) {
return false;
}
Profile* extension_task_profile =
GetProfileForExtensionTask(profile, *extension);
return file_browser_handlers::ExecuteFileBrowserHandler(
extension_task_profile, extension, task.action_id, file_urls,
std::move(done));
}
NOTREACHED();
return false;
}
void LaunchQuickOffice(Profile* profile,
const std::vector<FileSystemURL>& file_urls) {
UMA_HISTOGRAM_ENUMERATION(kDriveTaskResultMetricName,
OfficeTaskResult::FALLBACK_QUICKOFFICE);
const TaskDescriptor quick_office_task(
extension_misc::kQuickOfficeComponentExtensionId, TASK_TYPE_FILE_HANDLER,
kActionIdQuickOffice);
file_tasks::ExecuteFileTask(
profile, quick_office_task, file_urls, /* modal_parent */ nullptr,
base::BindOnce(
[](extensions::api::file_manager_private::TaskResult result,
std::string error_message) {
if (!error_message.empty()) {
LOG(ERROR) << "Fallback to QuickOffice for opening office file "
"with error message: "
<< error_message << " and result: " << result;
}
}));
return;
}
void OnDialogChoiceReceived(Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent,
const std::string& choice) {
if (choice == ash::office_fallback::kDialogChoiceQuickOffice) {
LaunchQuickOffice(profile, file_urls);
} else if (choice == ash::office_fallback::kDialogChoiceTryAgain) {
if (IsWebDriveOfficeTask(task)) {
ExecuteWebDriveOfficeTask(profile, task, file_urls, modal_parent);
} else if (IsOpenInOfficeTask(task)) {
ExecuteOpenInOfficeTask(profile, task, file_urls, modal_parent);
}
}
}
bool GetUserFallbackChoice(
Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent,
ash::office_fallback::FallbackReason fallback_reason) {
// If QuickOffice is not installed, don't launch dialog.
if (!IsExtensionInstalled(profile,
extension_misc::kQuickOfficeComponentExtensionId)) {
LOG(ERROR) << "Cannot fallback to QuickOffice when it is not installed";
return false;
}
// TODO(b/242685536) Add support for multi-file
// selection so the OfficeFallbackDialog can display multiple file names and
// `OnDialogChoiceReceived()` can open multiple files.
std::vector<storage::FileSystemURL> first_url{file_urls.front()};
ash::office_fallback::DialogChoiceCallback callback = base::BindOnce(
&OnDialogChoiceReceived, profile, task, first_url, modal_parent);
const std::string parsed_action_id = ParseFilesAppActionId(task.action_id);
return ash::office_fallback::OfficeFallbackDialog::Show(
first_url, fallback_reason, parsed_action_id, std::move(callback));
}
void FindExtensionAndAppTasks(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
const std::vector<GURL>& file_urls,
const std::vector<std::string>& dlp_source_urls,
FindTasksCallback callback,
std::unique_ptr<ResultingTasks> resulting_tasks) {
auto* tasks = &resulting_tasks->tasks;
// Web tasks file_handlers (View/Open With), Chrome app file_handlers, and
// extension file_browser_handlers.
FindAppServiceTasks(profile, entries, file_urls, dlp_source_urls, tasks);
// Done. Apply post-filtering and callback.
PostProcessFoundTasks(profile, entries, std::move(callback),
std::move(resulting_tasks));
}
void FindAllTypesOfTasks(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
const std::vector<GURL>& file_urls,
const std::vector<std::string>& dlp_source_urls,
FindTasksCallback callback) {
DCHECK(profile);
auto resulting_tasks = std::make_unique<ResultingTasks>();
if (!ash::features::ShouldArcFileTasksUseAppService()) {
// 1. Find and append ARC handler tasks if ARC file tasks aren't
// provided by App Service.
FindArcTasks(
profile, entries, file_urls, std::move(resulting_tasks),
base::BindOnce(&FindExtensionAndAppTasks, profile, entries, file_urls,
dlp_source_urls, std::move(callback)));
} else {
FindExtensionAndAppTasks(profile, entries, file_urls, dlp_source_urls,
std::move(callback), std::move(resulting_tasks));
}
}
void ChooseAndSetDefaultTask(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
ResultingTasks* resulting_tasks) {
if (ChooseAndSetDefaultTaskFromPolicyPrefs(profile, entries,
resulting_tasks)) {
// If the function returns true, then the default selection has been
// affected by policy. Check that |policy_default_handler_status| is set.
DCHECK(resulting_tasks->policy_default_handler_status);
return;
}
// Otherwise check that |policy_default_handler_status| is not set.
DCHECK(!resulting_tasks->policy_default_handler_status);
// Collect the default tasks from the preferences into a set.
base::flat_set<TaskDescriptor> default_tasks;
for (const extensions::EntryInfo& entry : entries) {
const base::FilePath& file_path = entry.path;
const std::string& mime_type = entry.mime_type;
TaskDescriptor default_task;
if (file_tasks::GetDefaultTaskFromPrefs(*profile->GetPrefs(), mime_type,
file_path.Extension(),
&default_task)) {
default_tasks.insert(default_task);
if (ash::features::ShouldArcFileTasksUseAppService() &&
default_task.task_type == TASK_TYPE_ARC_APP) {
// Default preference Task Descriptors for Android apps are stored in a
// legacy format (app id: "<package>/<activity>", action id: "view"). To
// match against ARC app task descriptors (which use app id: "<app
// service id>", action id: "<activity>"), we translate the default Task
// Descriptors into the new format.
std::vector<std::string> app_id_info =
base::SplitString(default_task.app_id, "/", base::KEEP_WHITESPACE,
base::SPLIT_WANT_NONEMPTY);
if (app_id_info.size() != 2) {
continue;
}
const std::string& package = app_id_info[0];
const std::string& activity = app_id_info[1];
Profile* profile_with_app_service = GetProfileWithAppService(profile);
if (profile_with_app_service) {
// Add possible alternative forms of this task descriptor to our list
// of default tasks.
apps::AppServiceProxyFactory::GetForProfile(profile_with_app_service)
->AppRegistryCache()
.ForEachApp([&default_tasks, package,
activity](const apps::AppUpdate& update) {
if (update.PublisherId() == package) {
TaskDescriptor alternate_default_task(
update.AppId(), TASK_TYPE_ARC_APP, activity);
default_tasks.insert(alternate_default_task);
}
});
}
}
}
}
auto& tasks = resulting_tasks->tasks;
// Go through all the tasks from the beginning and see if there is any
// default task. If found, pick and set it as default and return.
for (FullTaskDescriptor& task : tasks) {
DCHECK(!task.is_default);
if (base::Contains(default_tasks, task.task_descriptor)) {
task.is_default = true;
return;
}
}
// No default task. If the "Open in Docs/Sheets/Slides through Drive" workflow
// is available for Office files, set as default.
for (FullTaskDescriptor& task : tasks) {
if (IsWebDriveOfficeTask(task.task_descriptor)) {
task.is_default = true;
return;
}
}
// Check for an explicit file extension match (without MIME match) in the
// extension manifest and pick that over the fallback handlers below (see
// crbug.com/803930)
for (FullTaskDescriptor& task : tasks) {
if (task.is_file_extension_match && !task.is_generic_file_handler &&
!IsFallbackFileHandler(task)) {
task.is_default = true;
return;
}
}
// Prefer a fallback app over viewing in the browser (crbug.com/1111399).
// Unless it's HTML which should open in the browser (crbug.com/1121396).
for (FullTaskDescriptor& task : tasks) {
if (IsFallbackFileHandler(task) &&
ParseFilesAppActionId(task.task_descriptor.action_id) !=
"view-in-browser") {
const extensions::EntryInfo entry = entries[0];
const base::FilePath& file_path = entry.path;
if (IsHtmlFile(file_path)) {
break;
}
task.is_default = true;
return;
}
}
// No default tasks found. If there is any fallback file browser handler,
// make it as default task, so it's selected by default.
for (FullTaskDescriptor& task : tasks) {
DCHECK(!task.is_default);
if (IsFallbackFileHandler(task)) {
task.is_default = true;
return;
}
}
}
bool IsWebDriveOfficeTask(const TaskDescriptor& task) {
const std::string action_id = ParseFilesAppActionId(task.action_id);
bool is_web_drive_office_action_id =
action_id == kActionIdWebDriveOfficeWord ||
action_id == kActionIdWebDriveOfficeExcel ||
action_id == kActionIdWebDriveOfficePowerPoint;
return IsFilesAppId(task.app_id) && is_web_drive_office_action_id;
}
bool IsOpenInOfficeTask(const TaskDescriptor& task) {
const std::string action_id = ParseFilesAppActionId(task.action_id);
return IsFilesAppId(task.app_id) && action_id == kActionIdOpenInOffice;
}
bool IsExtensionInstalled(Profile* profile, const std::string& extension_id) {
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(profile);
return registry->GetExtensionById(extension_id,
extensions::ExtensionRegistry::ENABLED);
}
bool IsHtmlFile(const base::FilePath& path) {
constexpr const char* kHtmlExtensions[] = {".htm", ".html", ".mhtml",
".xht", ".xhtm", ".xhtml"};
for (const char* extension : kHtmlExtensions) {
if (path.MatchesExtension(extension)) {
return true;
}
}
return false;
}
bool IsOfficeFile(const base::FilePath& path) {
constexpr const char* kOfficeExtensions[] = {".doc", ".docx", ".xls",
".xlsx", ".ppt", ".pptx"};
for (const char* extension : kOfficeExtensions) {
if (path.MatchesExtension(extension)) {
return true;
}
}
return false;
}
namespace {
std::string ToSwaActionId(const std::string& action_id) {
return std::string(ash::file_manager::kChromeUIFileManagerURL) + "?" +
action_id;
}
} // namespace
std::set<std::string> WordGroupExtensions() {
static const base::NoDestructor<std::set<std::string>> extensions(
std::initializer_list<std::string>({".doc", ".docx"}));
return *extensions;
}
void SetWordFileHandler(Profile* profile, const TaskDescriptor& task) {
UpdateDefaultTask(
profile, task, WordGroupExtensions(),
{"application/msword",
"application/"
"vnd.openxmlformats-officedocument.wordprocessingml.document"});
}
void SetWordFileHandlerToFilesSWA(Profile* profile,
const std::string& action_id) {
TaskDescriptor task(kFileManagerSwaAppId, TaskType::TASK_TYPE_WEB_APP,
ToSwaActionId(action_id));
SetWordFileHandler(profile, task);
}
std::set<std::string> ExcelGroupExtensions() {
static const base::NoDestructor<std::set<std::string>> extensions(
std::initializer_list<std::string>({".xls", ".xlsx"}));
return *extensions;
}
void SetExcelFileHandler(Profile* profile, const TaskDescriptor& task) {
UpdateDefaultTask(
profile, task, ExcelGroupExtensions(),
{"application/vnd.ms-excel",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"});
}
void SetExcelFileHandlerToFilesSWA(Profile* profile,
const std::string& action_id) {
TaskDescriptor task(kFileManagerSwaAppId, TaskType::TASK_TYPE_WEB_APP,
ToSwaActionId(action_id));
SetExcelFileHandler(profile, task);
}
std::set<std::string> PowerPointGroupExtensions() {
static const base::NoDestructor<std::set<std::string>> extensions(
std::initializer_list<std::string>({".ppt", ".pptx"}));
return *extensions;
}
void SetPowerPointFileHandler(Profile* profile, const TaskDescriptor& task) {
UpdateDefaultTask(
profile, task, PowerPointGroupExtensions(),
{"application/vnd.ms-powerpoint",
"application/"
"vnd.openxmlformats-officedocument.presentationml.presentation"});
}
void SetPowerPointFileHandlerToFilesSWA(Profile* profile,
const std::string& action_id) {
TaskDescriptor task(kFileManagerSwaAppId, TaskType::TASK_TYPE_WEB_APP,
ToSwaActionId(action_id));
SetPowerPointFileHandler(profile, task);
}
void SetOfficeSetupComplete(Profile* profile, bool complete) {
profile->GetPrefs()->SetBoolean(prefs::kOfficeSetupComplete, complete);
}
bool OfficeSetupComplete(Profile* profile) {
return profile->GetPrefs()->GetBoolean(prefs::kOfficeSetupComplete);
}
void SetAlwaysMoveOfficeFilesToDrive(Profile* profile, bool always_move) {
profile->GetPrefs()->SetBoolean(prefs::kOfficeFilesAlwaysMoveToDrive,
always_move);
}
bool GetAlwaysMoveOfficeFilesToDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(prefs::kOfficeFilesAlwaysMoveToDrive);
}
void SetAlwaysMoveOfficeFilesToOneDrive(Profile* profile, bool always_move) {
profile->GetPrefs()->SetBoolean(prefs::kOfficeFilesAlwaysMoveToOneDrive,
always_move);
}
bool GetAlwaysMoveOfficeFilesToOneDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeFilesAlwaysMoveToOneDrive);
}
void SetOfficeMoveConfirmationShownForDrive(Profile* profile, bool complete) {
profile->GetPrefs()->SetBoolean(prefs::kOfficeMoveConfirmationShownForDrive,
complete);
}
bool GetOfficeMoveConfirmationShownForDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeMoveConfirmationShownForDrive);
}
void SetOfficeMoveConfirmationShownForOneDrive(Profile* profile,
bool complete) {
profile->GetPrefs()->SetBoolean(
prefs::kOfficeMoveConfirmationShownForOneDrive, complete);
}
bool GetOfficeMoveConfirmationShownForOneDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeMoveConfirmationShownForOneDrive);
}
void SetOfficeMoveConfirmationShownForLocalToDrive(Profile* profile,
bool shown) {
profile->GetPrefs()->SetBoolean(
prefs::kOfficeMoveConfirmationShownForLocalToDrive, shown);
}
bool GetOfficeMoveConfirmationShownForLocalToDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeMoveConfirmationShownForLocalToDrive);
}
void SetOfficeMoveConfirmationShownForLocalToOneDrive(Profile* profile,
bool shown) {
profile->GetPrefs()->SetBoolean(
prefs::kOfficeMoveConfirmationShownForLocalToOneDrive, shown);
}
bool GetOfficeMoveConfirmationShownForLocalToOneDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeMoveConfirmationShownForLocalToOneDrive);
}
void SetOfficeMoveConfirmationShownForCloudToDrive(Profile* profile,
bool shown) {
profile->GetPrefs()->SetBoolean(
prefs::kOfficeMoveConfirmationShownForCloudToDrive, shown);
}
bool GetOfficeMoveConfirmationShownForCloudToDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeMoveConfirmationShownForCloudToDrive);
}
void SetOfficeMoveConfirmationShownForCloudToOneDrive(Profile* profile,
bool shown) {
profile->GetPrefs()->SetBoolean(
prefs::kOfficeMoveConfirmationShownForCloudToOneDrive, shown);
}
bool GetOfficeMoveConfirmationShownForCloudToOneDrive(Profile* profile) {
return profile->GetPrefs()->GetBoolean(
prefs::kOfficeMoveConfirmationShownForCloudToOneDrive);
}
void SetOfficeFileMovedToOneDrive(Profile* profile, base::Time moved) {
profile->GetPrefs()->SetTime(prefs::kOfficeFileMovedToOneDrive, moved);
}
void SetOfficeFileMovedToGoogleDrive(Profile* profile, base::Time moved) {
profile->GetPrefs()->SetTime(prefs::kOfficeFileMovedToGoogleDrive, moved);
}
} // namespace file_manager::file_tasks