blob: 4d017498d791b181b8342adb27718de908806df6 [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.
//
// This file provides utility functions for "file tasks".
//
// WHAT ARE FILE TASKS?
//
// File tasks are actions that can be performed over the currently selected
// files from the Files app. A task can be one of:
//
// 1) A Chrome Extension or App, registered via "file_handlers" or
// "file_browser_handlers" in manifest.json (ex. Text.app). This information
// comes from FileBrowserHandler::GetHandlers()
//
// See also: https://developer.chrome.com/extensions/manifest.html#file_handlers
// https://developer.chrome.com/extensions/fileBrowserHandler.html
//
// 2) Built-in handlers provided by the Files app. The Files app provides lots
// of file_browser_handlers, such as "play", "mount-archive". These built-in
// handlers are often handled specially inside the Files app. This
// information also comes from FileBrowserHandler::GetHandlers().
//
// See also: ui/file_manager/file_manager/manifest.json
//
// For example, if the user selects a JPEG file, the Files app will receive file
// tasks represented as a JSON object via
// chrome.fileManagerPrivate.getFileTasks() API, which look like:
//
// [
// {
// "iconUrl":
// "chrome://extension-icon/hhaomjibdihmijegdhdafkllkbggdgoj/16/1",
// "isDefault": true,
// "descriptor": {
// appId: "hhaomjibdihmijegdhdafkllkbggdgoj",
// taskType: "file",
// actionId: "gallery"
// }
// "title": "__MSG_OPEN_ACTION__"
// }
// ]
//
// The file task is a built-in handler from the Files app.
//
// WHAT ARE TASK IDS?
//
// "TaskId" is a string of the format "appId|taskType|actionId". We used to
// store these three fields together in a string so we could easily store this
// data in user preferences. We are removing taskId wherever possible in favour
// of the TaskDescriptor struct, which contains the same information but in a
// more typical struct format. TaskId will remain in some parts of the code
// where we need to serialize TaskDescriptors, like for UMA.
//
// What are the three types of information encoded here?
//
// The "TaskId" format encoding is as follows:
//
// <app-id>|<task-type>|<action-id>
//
// <app-id> is a Chrome Extension/App ID.
//
// <task-type> is either of
// - "file" - File browser handler - app/extension declaring
// "file_browser_handlers" in manifest.
// - "app" - File handler - app declaring "file_handlers" in manifest.json.
// - "arc" - ARC App
// - "crostini" - Crostini App
//
// <action-id> is an ID string used for identifying actions provided from a
// single Chrome Extension/App. In other words, a single Chrome/Extension can
// provide multiple file handlers hence each of them needs to have a unique
// action ID. For Crostini apps, <action-id> is always "open-with".
//
// HOW ARE TASKS EXECUTED?
//
// chrome.fileManagerPrivate.executeTask() is used to open a file with a handler
// (Chrome Extension/App), and to open files directly in the browser without any
// handler, e.g. PDF.
//
// Files app also has "internal tasks" which we can split into three categories:
// 1. Tasks that open in the browser. The JS-side calls executeTask(), and we
// spawn a new browser tab here on the C++ side. e.g. "view-in-browser",
// "view-pdf" and "open-hosted-*".
// 2. Tasks that are handled internally by Files app JS. e.g. "mount-archive",
// "install-linux-package" and "import-crostini-image".
// 3. Tasks where the browser process opens Files app to a folder or file, e.g.
// "open" and "select", through file_manager::util::OpenItem().
//
// See also: ui/file_manager/file_manager/foreground/js/file_tasks.js
//
#ifndef CHROME_BROWSER_ASH_FILE_MANAGER_FILE_TASKS_H_
#define CHROME_BROWSER_ASH_FILE_MANAGER_FILE_TASKS_H_
#include <memory>
#include <set>
#include <string>
#include <vector>
#include "base/functional/callback_forward.h"
#include "chrome/browser/ui/webui/ash/office_fallback/office_fallback_dialog.h"
#include "chrome/common/extensions/api/file_manager_private.h"
#include "url/gurl.h"
using storage::FileSystemURL;
class PrefService;
class Profile;
namespace extensions {
struct EntryInfo;
}
namespace storage {
class FileSystemURL;
}
namespace user_prefs {
class PrefRegistrySyncable;
}
namespace file_manager::file_tasks {
extern const char kActionIdView[];
extern const char kActionIdSend[];
extern const char kActionIdSendMultiple[];
extern const char kActionIdWebDriveOfficeWord[];
extern const char kActionIdWebDriveOfficeExcel[];
extern const char kActionIdWebDriveOfficePowerPoint[];
extern const char kActionIdOpenInOffice[];
extern const char kActionIdOpenWeb[];
// Checks which extension is installed and return the latest one installed or ""
// if none is installed
std::string GetODFSExtensionId(Profile* profile);
// Task types as explained in the comment above. Search for <task-type>.
enum TaskType {
TASK_TYPE_UNKNOWN = 0, // Used only for handling errors.
TASK_TYPE_FILE_BROWSER_HANDLER,
TASK_TYPE_FILE_HANDLER,
DEPRECATED_TASK_TYPE_DRIVE_APP,
TASK_TYPE_ARC_APP,
TASK_TYPE_CROSTINI_APP,
TASK_TYPE_WEB_APP,
TASK_TYPE_PLUGIN_VM_APP,
TASK_TYPE_BRUSCHETTA_APP,
// The enum values must be kept in sync with FileManagerTaskType in
// tools/metrics/histograms/enums.xml. Since enums for histograms are
// append-only (for keeping the number consistent across versions), new values
// for this enum also has to be always appended at the end (i.e., here).
NUM_TASK_TYPE,
};
TaskType StringToTaskType(const std::string& str);
std::string TaskTypeToString(TaskType task_type);
constexpr char kDriveErrorMetricName[] = "FileBrowser.OfficeFiles.Errors.Drive";
constexpr char kDriveTaskResultMetricName[] =
"FileBrowser.OfficeFiles.TaskResult.Drive";
// List of UMA enum value for Web Drive Office task results. The enum values
// must be kept in sync with OfficeTaskResult in
// tools/metrics/histograms/enums.xml.
enum class OfficeTaskResult {
FALLBACK_QUICKOFFICE = 0,
FALLBACK_OTHER = 1,
OPENED = 2,
MOVED = 3,
CANCELLED = 4,
FAILED = 5,
kMaxValue = FAILED,
};
// List of UMA enum values for Office File Handler task results for Drive. The
// enum values must be kept in sync with OfficeDriveErrors in
// tools/metrics/histograms/enums.xml.
enum class OfficeDriveErrors {
OFFLINE = 0,
DRIVEFS_INTERFACE = 1,
TIMEOUT = 2,
NO_METADATA = 3,
INVALID_ALTERNATE_URL = 4,
DRIVE_ALTERNATE_URL = 5,
UNEXPECTED_ALTERNATE_URL = 6,
kMaxValue = UNEXPECTED_ALTERNATE_URL,
};
// UMA metric name that tracks the result of using a MS Office file outside
// of Drive.
constexpr char kUseOutsideDriveMetricName[] =
"FileBrowser.OfficeFiles.UseOutsideDrive";
// List of UMA enum values for file system operations that let a user use a
// MS Office file outside of Drive. The enum values must be kept in sync with
// OfficeFilesUseOutsideDriveHook in tools/metrics/histograms/enums.xml.
enum class OfficeFilesUseOutsideDriveHook {
FILE_PICKER_SELECTION = 0,
COPY = 1,
MOVE = 2,
ZIP = 3,
OPEN_FROM_FILES_APP = 4,
kMaxValue = OPEN_FROM_FILES_APP,
};
// Describes a task.
// See the comment above for <app-id>, <task-type>, and <action-id>.
struct TaskDescriptor {
TaskDescriptor(const std::string& in_app_id,
TaskType in_task_type,
const std::string& in_action_id)
: app_id(in_app_id), task_type(in_task_type), action_id(in_action_id) {
// For web apps, the action_id must be a full valid URL if it exists.
DCHECK(task_type != TASK_TYPE_WEB_APP || action_id.empty() ||
GURL(action_id).is_valid());
}
TaskDescriptor() = default;
bool operator<(const TaskDescriptor& other) const;
bool operator==(const TaskDescriptor& other) const;
std::string app_id;
TaskType task_type;
std::string action_id;
};
// Describes a task with extra information such as icon URL.
struct FullTaskDescriptor {
FullTaskDescriptor(const TaskDescriptor& task_descriptor,
const std::string& task_title,
const GURL& icon_url,
bool is_default,
bool is_generic_file_handler,
bool is_file_extension_match,
bool is_dlp_blocked = false);
FullTaskDescriptor(const FullTaskDescriptor& other);
FullTaskDescriptor& operator=(const FullTaskDescriptor& other);
// Unique ID for the task.
TaskDescriptor task_descriptor;
// The user-visible title/name of the app/extension/thing to be launched.
std::string task_title;
// The icon URL for the task (ex. app icon)
GURL icon_url;
// The default task is stored in user preferences and will be used when the
// user doesn't explicitly pick another e.g. double click.
bool is_default;
// True if this task is from generic file handler. Generic file handler is a
// file handler which handles any type of files (e.g. extensions: ["*"],
// types: ["*/*"]). Partial wild card (e.g. types: ["image/*"]) is not
// generic file handler.
bool is_generic_file_handler;
// True if this task is from a file extension only. e.g. an extension/app
// that declares no MIME types in its manifest, but matches with the
// file_handlers "extensions" instead.
bool is_file_extension_match;
// True if this task is blocked by Data Leak Prevention (DLP).
bool is_dlp_blocked;
};
// Describes how admin policy affects the default task in a ResultingTasks.
enum class PolicyDefaultHandlerStatus {
// Indicates that the default task was selected according to the policy
// settings.
kDefaultHandlerAssignedByPolicy,
// Indicates that no default task was set due to some assignment conflicts.
// Possible reasons are:
// * The user is trying to open multiple files which have different policy
// default handlers;
// * The admin-specified handler was not found in the list of tasks.
kIncorrectAssignment
};
// Represents a set of tasks capable of handling file entries.
struct ResultingTasks {
ResultingTasks();
~ResultingTasks();
std::vector<FullTaskDescriptor> tasks;
absl::optional<PolicyDefaultHandlerStatus> policy_default_handler_status;
};
// Registers profile prefs related to file_manager.
void RegisterProfilePrefs(user_prefs::PrefRegistrySyncable*);
// Update the default file handler for the given sets of suffixes and MIME
// types.
void UpdateDefaultTask(Profile* profile,
const TaskDescriptor& task_descriptor,
const std::set<std::string>& suffixes,
const std::set<std::string>& mime_types);
// Returns the default task for the given |mime_type|/|suffix| combination in
// |task_out|. If it finds a MIME type match, then it prefers that over a suffix
// match. If a default can't be found, then it returns false.
bool GetDefaultTaskFromPrefs(const PrefService& pref_service,
const std::string& mime_type,
const std::string& suffix,
TaskDescriptor* task_out);
// Generates task id for the task specified by |app_id|, |task_type| and
// |action_id|.
//
// |app_id| is the Chrome Extension/App ID.
// |action_id| is a free-form string ID for the action.
std::string MakeTaskID(const std::string& app_id,
TaskType task_type,
const std::string& action_id);
// Converts |task_descriptor| to a task ID.
std::string TaskDescriptorToId(const TaskDescriptor& task_descriptor);
// Parses the task ID and extracts app ID, task type, and action ID into
// |task|. On failure, returns false, and the contents of |task| are
// undefined.
//
// See also the comment at the beginning of the file for details for how
// "task_id" looks like.
bool ParseTaskID(const std::string& task_id, TaskDescriptor* task);
// The callback is used for ExecuteFileTask().
typedef base::OnceCallback<void(
extensions::api::file_manager_private::TaskResult result,
std::string error_message)>
FileTaskFinishedCallback;
// Executes file handler task for each element of |file_urls|.
// Returns |false| if the execution cannot be initiated. Otherwise returns
// |true| and then eventually calls |done| when all the files have been handled.
// |done| can be a null callback.
//
// Parameters:
// profile - The profile used for making this function call.
// task - See the comment at TaskDescriptor struct.
// file_urls - URLs of the target files.
// modal_parent - Certain tasks like the Office setup flow can create WebUIs,
// which will be made modal to this parent, if not null.
// done - The callback which will be called on completion.
// The callback won't be called if the function returns
// false.
bool ExecuteFileTask(Profile* profile,
const TaskDescriptor& task,
const std::vector<storage::FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent,
FileTaskFinishedCallback done);
// Executes QuickOffice file handler for each element of |file_urls|.
void LaunchQuickOffice(Profile* profile,
const std::vector<storage::FileSystemURL>& file_urls);
// Executes appropriate task to open the selected `file_urls`.
// If user's `choice` is `kDialogChoiceQuickOffice`, launch QuickOffice.
// If user's `choice` is `kDialogChoiceTryAgain`, execute the `task`.
// If user's `choice` is `kDialogChoiceCancel`, do nothing.
void OnDialogChoiceReceived(Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent,
const std::string& choice);
// Shows a new dialog for users to choose what to do next. Returns True
// if a new dialog has been effectively created.
bool GetUserFallbackChoice(Profile* profile,
const TaskDescriptor& task,
const std::vector<FileSystemURL>& file_urls,
gfx::NativeWindow modal_parent,
ash::office_fallback::FallbackReason failure_reason);
// Callback function type for FindAllTypesOfTasks.
typedef base::OnceCallback<void(
std::unique_ptr<ResultingTasks> resulting_tasks)>
FindTasksCallback;
// Finds all types (file handlers, file browser handlers) of
// tasks.
//
// If |entries| contains a Google document, only the internal tasks of the
// Files app (i.e., tasks having the app ID of the Files app) are listed.
// This is to avoid listing normal file handler and file browser handler tasks,
// which can handle only normal files. If passed, |dlp_source_urls| should have
// the same length as |entries| and each element should represent the URL from
// which the corresponding entry was downloaded from, and are used to check DLP
// restrictions on the |entries|.
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);
// Chooses the default task in |resulting_tasks| and sets it as default, if the
// default task is found (i.e. the default task may not exist in
// |resulting_tasks|). No tasks should be set as default before calling this
// function.
void ChooseAndSetDefaultTask(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
ResultingTasks* resulting_tasks);
bool IsWebDriveOfficeTask(const TaskDescriptor& task);
bool IsOpenInOfficeTask(const TaskDescriptor& task);
bool IsExtensionInstalled(Profile* profile, const std::string& extension_id);
// Returns whether |path| is an HTML file according to its extension.
bool IsHtmlFile(const base::FilePath& path);
// Returns whether |path| is a MS Office file according to its extension.
bool IsOfficeFile(const base::FilePath& path);
// Returns the group of extensions we consider to be 'Word', 'Excel' or
// 'PowerPoint' files for the purpose of setting preferences. The extensions
// contain the '.' character at the start.
std::set<std::string> WordGroupExtensions();
std::set<std::string> ExcelGroupExtensions();
std::set<std::string> PowerPointGroupExtensions();
// The same as above but MIME types.
std::set<std::string> WordGroupMimeTypes();
std::set<std::string> ExcelGroupMimeTypes();
std::set<std::string> PowerPointGroupMimeTypes();
// Updates the default task for each of the office file types.
void SetWordFileHandler(Profile* profile, const TaskDescriptor& task);
void SetExcelFileHandler(Profile* profile, const TaskDescriptor& task);
void SetPowerPointFileHandler(Profile* profile, const TaskDescriptor& task);
// Whether we have an explicit user preference stored for the file handler for
// this extension. |extension| should contain the leading '.'.
bool HasExplicitDefaultFileHandler(Profile* profile,
const std::string& extension);
// TODO(petermarshall): Move these to a new file office_file_tasks.cc/h
// Updates the default task for each of the office file types to a Files
// SWA with |action_id|. |action_id| must be a valid action registered with the
// Files app SWA.
void SetWordFileHandlerToFilesSWA(Profile* profile,
const std::string& action_id);
void SetExcelFileHandlerToFilesSWA(Profile* profile,
const std::string& action_id);
void SetPowerPointFileHandlerToFilesSWA(Profile* profile,
const std::string& action_id);
// TODO(petermarshall): Move these to a new file office_file_tasks.cc/h
// Sets the user preference storing whether we should always move office files
// to Google Drive without first asking the user.
void SetAlwaysMoveOfficeFilesToDrive(Profile* profile, bool complete = true);
// Whether we should always move office files to Google Drive without first
// asking the user.
bool GetAlwaysMoveOfficeFilesToDrive(Profile* profile);
// Sets the user preference storing whether we should always move office files
// to OneDrive without first asking the user.
void SetAlwaysMoveOfficeFilesToOneDrive(Profile* profile, bool complete = true);
// Whether we should always move office files to OneDrive without first asking
// the user.
bool GetAlwaysMoveOfficeFilesToOneDrive(Profile* profile);
// Sets the user preference storing whether the move confirmation dialog has
// been shown before for moving files to Drive.
void SetOfficeMoveConfirmationShownForDrive(Profile* profile, bool complete);
// Whether the move confirmation dialog has been shown before for moving files
// to Drive.
bool GetOfficeMoveConfirmationShownForDrive(Profile* profile);
// Sets the user preference storing whether the move confirmation dialog has
// been shown before for moving files to OneDrive.
void SetOfficeMoveConfirmationShownForOneDrive(Profile* profile, bool complete);
// Whether the move confirmation dialog has been shown before for moving files
// to OneDrive.
bool GetOfficeMoveConfirmationShownForOneDrive(Profile* profile);
// Sets the user preference storing whether the move confirmation dialog has
// been shown before for uploading files from a local source to Drive.
void SetOfficeMoveConfirmationShownForLocalToDrive(Profile* profile,
bool shown);
// Whether the move confirmation dialog has been shown before for uploading
// files from a local source to Drive.
bool GetOfficeMoveConfirmationShownForLocalToDrive(Profile* profile);
// Sets the user preference storing whether the move confirmation dialog has
// been shown before for uploading files from a local source to OneDrive.
void SetOfficeMoveConfirmationShownForLocalToOneDrive(Profile* profile,
bool shown);
// Whether the move confirmation dialog has been shown before for uploading
// files from a local source to OneDrive.
bool GetOfficeMoveConfirmationShownForLocalToOneDrive(Profile* profile);
// Sets the user preference storing whether the move confirmation dialog has
// been shown before for uploading files from a cloud source to Drive.
void SetOfficeMoveConfirmationShownForCloudToDrive(Profile* profile,
bool shown);
// Whether the move confirmation dialog has been shown before for uploading
// files from a cloud source to Drive.
bool GetOfficeMoveConfirmationShownForCloudToDrive(Profile* profile);
// Sets the user preference storing whether the move confirmation dialog has
// been shown before for uploading files from a cloud source to OneDrive.
void SetOfficeMoveConfirmationShownForCloudToOneDrive(Profile* profile,
bool shown);
// Whether the move confirmation dialog has been shown before for uploading
// files from a cloud source to OneDrive.
bool GetOfficeMoveConfirmationShownForCloudToOneDrive(Profile* profile);
// Sets the preference `office.file_moved_one_drive`.
void SetOfficeFileMovedToOneDrive(Profile* profile, base::Time moved);
// Sets the preference `office.file_moved_google_drive`.
void SetOfficeFileMovedToGoogleDrive(Profile* profile, base::Time moved);
} // namespace file_manager::file_tasks
#endif // CHROME_BROWSER_ASH_FILE_MANAGER_FILE_TASKS_H_