1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/file_manager/open_util.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/chromeos/drive/file_system_util.h"
12 #include "chrome/browser/chromeos/file_manager/app_id.h"
13 #include "chrome/browser/chromeos/file_manager/file_tasks.h"
14 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
15 #include "chrome/browser/chromeos/file_manager/path_util.h"
16 #include "chrome/browser/chromeos/file_manager/url_util.h"
17 #include "chrome/browser/extensions/api/file_handlers/mime_util.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/user_metrics.h"
20 #include "storage/browser/fileapi/file_system_backend.h"
21 #include "storage/browser/fileapi/file_system_context.h"
22 #include "storage/browser/fileapi/file_system_operation_runner.h"
23 #include "storage/browser/fileapi/file_system_url.h"
25 using content::BrowserThread
;
26 using storage::FileSystemURL
;
28 namespace file_manager
{
32 bool shell_operations_allowed
= true;
34 // Executes the |task| for the file specified by |url|.
35 void ExecuteFileTaskForUrl(Profile
* profile
,
36 const file_tasks::TaskDescriptor
& task
,
38 if (!shell_operations_allowed
)
40 storage::FileSystemContext
* file_system_context
=
41 GetFileSystemContextForExtensionId(profile
, kFileManagerAppId
);
43 file_tasks::ExecuteFileTask(
45 GetFileManagerMainPageUrl(), // Executing the task on behalf of Files.app.
47 std::vector
<FileSystemURL
>(1, file_system_context
->CrackURL(url
)),
48 file_tasks::FileTaskFinishedCallback());
51 // Opens the file manager for the specified |url|. Used to implement
52 // internal handlers of special action IDs:
54 // "open" - Open the file manager for the given folder.
55 // "select" - Open the file manager for the given file. The folder containing
56 // the file will be opened with the file selected.
57 void OpenFileManagerWithInternalActionId(Profile
* profile
,
59 const std::string
& action_id
) {
60 DCHECK(action_id
== "open" || action_id
== "select");
61 if (!shell_operations_allowed
)
63 content::RecordAction(base::UserMetricsAction("ShowFileBrowserFullTab"));
65 file_tasks::TaskDescriptor
task(kFileManagerAppId
,
66 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER
,
68 ExecuteFileTaskForUrl(profile
, task
, url
);
71 // Opens the file with fetched MIME type and calls the callback.
72 void OpenFileWithMimeType(Profile
* profile
,
73 const base::FilePath
& path
,
75 const platform_util::OpenOperationCallback
& callback
,
76 const std::string
& mime_type
) {
77 extensions::app_file_handler_util::PathAndMimeTypeSet path_mime_set
;
78 path_mime_set
.insert(std::make_pair(path
, mime_type
));
80 std::vector
<GURL
> file_urls
;
81 file_urls
.push_back(url
);
83 std::vector
<file_tasks::FullTaskDescriptor
> tasks
;
84 file_tasks::FindAllTypesOfTasks(
86 drive::util::GetDriveAppRegistryByProfile(profile
),
91 // Select a default handler. If a default handler is not available, select
92 // a non-generic file handler.
93 const file_tasks::FullTaskDescriptor
* chosen_task
= nullptr;
94 for (const auto& task
: tasks
) {
95 if (!task
.is_generic_file_handler()) {
97 if (task
.is_default())
102 if (chosen_task
!= nullptr) {
103 if (shell_operations_allowed
)
104 ExecuteFileTaskForUrl(profile
, chosen_task
->task_descriptor(), url
);
105 callback
.Run(platform_util::OPEN_SUCCEEDED
);
107 callback
.Run(platform_util::OPEN_FAILED_NO_HANLDER_FOR_FILE_TYPE
);
111 // Opens the file specified by |url| by finding and executing a file task for
112 // the file. Calls |callback| with the result.
113 void OpenFile(Profile
* profile
,
114 const base::FilePath
& path
,
116 const platform_util::OpenOperationCallback
& callback
) {
117 extensions::app_file_handler_util::GetMimeTypeForLocalPath(
119 base::Bind(&OpenFileWithMimeType
, profile
, path
, url
, callback
));
122 void OpenItemWithMetadata(Profile
* profile
,
123 const base::FilePath
& file_path
,
125 platform_util::OpenItemType expected_type
,
126 const platform_util::OpenOperationCallback
& callback
,
127 base::File::Error error
,
128 const base::File::Info
& file_info
) {
129 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
130 if (error
!= base::File::FILE_OK
) {
131 callback
.Run(error
== base::File::FILE_ERROR_NOT_FOUND
132 ? platform_util::OPEN_FAILED_PATH_NOT_FOUND
133 : platform_util::OPEN_FAILED_FILE_ERROR
);
137 // Note that there exists a TOCTOU race between the time the metadata for
138 // |file_path| was determined and when it is opened based on the metadata.
139 if (expected_type
== platform_util::OPEN_FOLDER
&& file_info
.is_directory
) {
140 OpenFileManagerWithInternalActionId(profile
, url
, "open");
141 callback
.Run(platform_util::OPEN_SUCCEEDED
);
145 if (expected_type
== platform_util::OPEN_FILE
&& !file_info
.is_directory
) {
146 OpenFile(profile
, file_path
, url
, callback
);
150 callback
.Run(platform_util::OPEN_FAILED_INVALID_TYPE
);
155 void OpenItem(Profile
* profile
,
156 const base::FilePath
& file_path
,
157 platform_util::OpenItemType expected_type
,
158 const platform_util::OpenOperationCallback
& callback
) {
159 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
161 // This is unfortunately necessary as file browser handlers operate on URLs.
163 if (!ConvertAbsoluteFilePathToFileSystemUrl(profile
, file_path
,
164 kFileManagerAppId
, &url
)) {
165 callback
.Run(platform_util::OPEN_FAILED_PATH_NOT_FOUND
);
170 GetFileSystemContextForExtensionId(profile
, kFileManagerAppId
), file_path
,
171 base::Bind(&OpenItemWithMetadata
, profile
, file_path
, url
, expected_type
,
175 void ShowItemInFolder(Profile
* profile
,
176 const base::FilePath
& file_path
,
177 const platform_util::OpenOperationCallback
& callback
) {
178 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
181 if (!ConvertAbsoluteFilePathToFileSystemUrl(profile
, file_path
,
182 kFileManagerAppId
, &url
)) {
183 callback
.Run(platform_util::OPEN_FAILED_PATH_NOT_FOUND
);
187 // This action changes the selection so we do not reuse existing tabs.
188 OpenFileManagerWithInternalActionId(profile
, url
, "select");
189 callback
.Run(platform_util::OPEN_SUCCEEDED
);
192 void DisableShellOperationsForTesting() {
193 shell_operations_allowed
= false;
197 } // namespace file_manager