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.
4
5 #include "chrome/browser/chromeos/file_manager/open_util.h"
6
7 #include <memory>
8 #include <set>
9 #include <string>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/check_op.h"
14 #include "base/files/file_path.h"
15 #include "base/metrics/user_metrics.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/chromeos/file_manager/app_id.h"
19 #include "chrome/browser/chromeos/file_manager/file_tasks.h"
20 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
21 #include "chrome/browser/chromeos/file_manager/path_util.h"
22 #include "chrome/browser/chromeos/file_manager/url_util.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "extensions/browser/api/file_handlers/directory_util.h"
26 #include "extensions/browser/api/file_handlers/mime_util.h"
27 #include "extensions/browser/entry_info.h"
28 #include "storage/browser/file_system/file_system_backend.h"
29 #include "storage/browser/file_system/file_system_context.h"
30 #include "storage/browser/file_system/file_system_operation_runner.h"
31 #include "storage/browser/file_system/file_system_url.h"
32
33 using content::BrowserThread;
34 using storage::FileSystemURL;
35
36 namespace file_manager {
37 namespace util {
38 namespace {
39
40 bool shell_operations_allowed = true;
41
IgnoreFileTaskExecuteResult(extensions::api::file_manager_private::TaskResult result,std::string failure_reason)42 void IgnoreFileTaskExecuteResult(
43 extensions::api::file_manager_private::TaskResult result,
44 std::string failure_reason) {}
45
46 // Executes the |task| for the file specified by |url|.
ExecuteFileTaskForUrl(Profile * profile,const file_tasks::TaskDescriptor & task,const GURL & url)47 void ExecuteFileTaskForUrl(Profile* profile,
48 const file_tasks::TaskDescriptor& task,
49 const GURL& url) {
50 if (!shell_operations_allowed)
51 return;
52 storage::FileSystemContext* file_system_context =
53 GetFileSystemContextForExtensionId(profile, kFileManagerAppId);
54
55 file_tasks::ExecuteFileTask(
56 profile,
57 GetFileManagerMainPageUrl(), // Executing task on behalf of the Files
58 // app.
59 task, std::vector<FileSystemURL>(1, file_system_context->CrackURL(url)),
60 base::BindOnce(&IgnoreFileTaskExecuteResult));
61 }
62
63 // Opens the file manager for the specified |url|. Used to implement
64 // internal handlers of special action IDs:
65 //
66 // "open" - Open the file manager for the given folder.
67 // "select" - Open the file manager for the given file. The folder containing
68 // the file will be opened with the file selected.
OpenFileManagerWithInternalActionId(Profile * profile,const GURL & url,const std::string & action_id)69 void OpenFileManagerWithInternalActionId(Profile* profile,
70 const GURL& url,
71 const std::string& action_id) {
72 DCHECK(action_id == "open" || action_id == "select");
73 if (!shell_operations_allowed)
74 return;
75 base::RecordAction(base::UserMetricsAction("ShowFileBrowserFullTab"));
76
77 file_tasks::TaskDescriptor task(
78 kFileManagerAppId, file_tasks::TASK_TYPE_FILE_HANDLER, action_id);
79 ExecuteFileTaskForUrl(profile, task, url);
80 }
81
OpenFileMimeTypeAfterTasksListed(Profile * profile,const GURL & url,platform_util::OpenOperationCallback callback,std::unique_ptr<std::vector<file_tasks::FullTaskDescriptor>> tasks)82 void OpenFileMimeTypeAfterTasksListed(
83 Profile* profile,
84 const GURL& url,
85 platform_util::OpenOperationCallback callback,
86 std::unique_ptr<std::vector<file_tasks::FullTaskDescriptor>> tasks) {
87 // Select a default handler. If a default handler is not available, select
88 // the first non-generic file handler.
89 const file_tasks::FullTaskDescriptor* chosen_task = nullptr;
90 for (const auto& task : *tasks) {
91 if (!task.is_generic_file_handler()) {
92 if (task.is_default()) {
93 chosen_task = &task;
94 break;
95 }
96 if (!chosen_task)
97 chosen_task = &task;
98 }
99 }
100
101 if (chosen_task != nullptr) {
102 if (shell_operations_allowed)
103 ExecuteFileTaskForUrl(profile, chosen_task->task_descriptor(), url);
104 std::move(callback).Run(platform_util::OPEN_SUCCEEDED);
105 } else {
106 std::move(callback).Run(
107 platform_util::OPEN_FAILED_NO_HANLDER_FOR_FILE_TYPE);
108 }
109 }
110
111 // Opens the file with fetched MIME type and calls the callback.
OpenFileWithMimeType(Profile * profile,const base::FilePath & path,const GURL & url,platform_util::OpenOperationCallback callback,const std::string & mime_type)112 void OpenFileWithMimeType(Profile* profile,
113 const base::FilePath& path,
114 const GURL& url,
115 platform_util::OpenOperationCallback callback,
116 const std::string& mime_type) {
117 std::vector<extensions::EntryInfo> entries;
118 entries.emplace_back(path, mime_type, false);
119
120 std::vector<GURL> file_urls;
121 file_urls.push_back(url);
122
123 file_tasks::FindAllTypesOfTasks(
124 profile, entries, file_urls,
125 base::BindOnce(&OpenFileMimeTypeAfterTasksListed, profile, url,
126 std::move(callback)));
127 }
128
129 // Opens the file specified by |url| by finding and executing a file task for
130 // the file. Calls |callback| with the result.
OpenFile(Profile * profile,const base::FilePath & path,const GURL & url,platform_util::OpenOperationCallback callback)131 void OpenFile(Profile* profile,
132 const base::FilePath& path,
133 const GURL& url,
134 platform_util::OpenOperationCallback callback) {
135 extensions::app_file_handler_util::GetMimeTypeForLocalPath(
136 profile, path,
137 base::BindOnce(&OpenFileWithMimeType, profile, path, url,
138 std::move(callback)));
139 }
140
OpenItemWithMetadata(Profile * profile,const base::FilePath & file_path,const GURL & url,platform_util::OpenItemType expected_type,platform_util::OpenOperationCallback callback,base::File::Error error,const base::File::Info & file_info)141 void OpenItemWithMetadata(Profile* profile,
142 const base::FilePath& file_path,
143 const GURL& url,
144 platform_util::OpenItemType expected_type,
145 platform_util::OpenOperationCallback callback,
146 base::File::Error error,
147 const base::File::Info& file_info) {
148 DCHECK_CURRENTLY_ON(BrowserThread::UI);
149 if (error != base::File::FILE_OK) {
150 std::move(callback).Run(error == base::File::FILE_ERROR_NOT_FOUND
151 ? platform_util::OPEN_FAILED_PATH_NOT_FOUND
152 : platform_util::OPEN_FAILED_FILE_ERROR);
153 return;
154 }
155
156 // Note that there exists a TOCTOU race between the time the metadata for
157 // |file_path| was determined and when it is opened based on the metadata.
158 if (expected_type == platform_util::OPEN_FOLDER && file_info.is_directory) {
159 OpenFileManagerWithInternalActionId(profile, url, "open");
160 std::move(callback).Run(platform_util::OPEN_SUCCEEDED);
161 return;
162 }
163
164 if (expected_type == platform_util::OPEN_FILE && !file_info.is_directory) {
165 OpenFile(profile, file_path, url, std::move(callback));
166 return;
167 }
168
169 std::move(callback).Run(platform_util::OPEN_FAILED_INVALID_TYPE);
170 }
171
ShowItemInFolderWithMetadata(Profile * profile,const base::FilePath & file_path,const GURL & url,platform_util::OpenOperationCallback callback,base::File::Error error,const base::File::Info & file_info)172 void ShowItemInFolderWithMetadata(Profile* profile,
173 const base::FilePath& file_path,
174 const GURL& url,
175 platform_util::OpenOperationCallback callback,
176 base::File::Error error,
177 const base::File::Info& file_info) {
178 DCHECK_CURRENTLY_ON(BrowserThread::UI);
179 if (error != base::File::FILE_OK) {
180 std::move(callback).Run(error == base::File::FILE_ERROR_NOT_FOUND
181 ? platform_util::OPEN_FAILED_PATH_NOT_FOUND
182 : platform_util::OPEN_FAILED_FILE_ERROR);
183 return;
184 }
185
186 // This action changes the selection so we do not reuse existing tabs.
187 OpenFileManagerWithInternalActionId(profile, url, "select");
188 std::move(callback).Run(platform_util::OPEN_SUCCEEDED);
189 }
190
191 } // namespace
192
OpenItem(Profile * profile,const base::FilePath & file_path,platform_util::OpenItemType expected_type,platform_util::OpenOperationCallback callback)193 void OpenItem(Profile* profile,
194 const base::FilePath& file_path,
195 platform_util::OpenItemType expected_type,
196 platform_util::OpenOperationCallback callback) {
197 DCHECK_CURRENTLY_ON(BrowserThread::UI);
198
199 // This is unfortunately necessary as file browser handlers operate on URLs.
200 GURL url;
201 if (!ConvertAbsoluteFilePathToFileSystemUrl(profile, file_path,
202 kFileManagerAppId, &url)) {
203 std::move(callback).Run(platform_util::OPEN_FAILED_PATH_NOT_FOUND);
204 return;
205 }
206
207 GetMetadataForPath(
208 GetFileSystemContextForExtensionId(profile, kFileManagerAppId), file_path,
209 storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
210 base::BindOnce(&OpenItemWithMetadata, profile, file_path, url,
211 expected_type, std::move(callback)));
212 }
213
ShowItemInFolder(Profile * profile,const base::FilePath & file_path,platform_util::OpenOperationCallback callback)214 void ShowItemInFolder(Profile* profile,
215 const base::FilePath& file_path,
216 platform_util::OpenOperationCallback callback) {
217 DCHECK_CURRENTLY_ON(BrowserThread::UI);
218
219 GURL url;
220 if (!ConvertAbsoluteFilePathToFileSystemUrl(profile, file_path,
221 kFileManagerAppId, &url)) {
222 std::move(callback).Run(platform_util::OPEN_FAILED_PATH_NOT_FOUND);
223 return;
224 }
225
226 GetMetadataForPath(
227 GetFileSystemContextForExtensionId(profile, kFileManagerAppId), file_path,
228 storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
229 base::BindOnce(&ShowItemInFolderWithMetadata, profile, file_path, url,
230 std::move(callback)));
231 }
232
DisableShellOperationsForTesting()233 void DisableShellOperationsForTesting() {
234 shell_operations_allowed = false;
235 }
236
237 } // namespace util
238 } // namespace file_manager
239