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 "base/bind.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 "chrome/browser/ui/browser.h"
19#include "chrome/browser/ui/browser_finder.h"
20#include "chrome/browser/ui/browser_window.h"
21#include "chrome/browser/ui/simple_message_box.h"
22#include "chrome/grit/generated_resources.h"
23#include "content/public/browser/browser_thread.h"
24#include "content/public/browser/user_metrics.h"
25#include "storage/browser/fileapi/file_system_backend.h"
26#include "storage/browser/fileapi/file_system_context.h"
27#include "storage/browser/fileapi/file_system_operation_runner.h"
28#include "storage/browser/fileapi/file_system_url.h"
29#include "ui/base/l10n/l10n_util.h"
30
31using content::BrowserThread;
32using storage::FileSystemURL;
33
34namespace file_manager {
35namespace util {
36namespace {
37
38// Shows a warning message box saying that the file could not be opened.
39void ShowWarningMessageBox(Profile* profile,
40                           const base::FilePath& file_path,
41                           int message_id) {
42  Browser* browser = chrome::FindTabbedBrowser(
43      profile, false, chrome::HOST_DESKTOP_TYPE_ASH);
44  chrome::ShowMessageBox(
45      browser ? browser->window()->GetNativeWindow() : NULL,
46      l10n_util::GetStringFUTF16(
47          IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE,
48          base::UTF8ToUTF16(file_path.BaseName().AsUTF8Unsafe())),
49      l10n_util::GetStringUTF16(message_id),
50      chrome::MESSAGE_BOX_TYPE_WARNING);
51}
52
53// Executes the |task| for the file specified by |url|.
54void ExecuteFileTaskForUrl(Profile* profile,
55                           const file_tasks::TaskDescriptor& task,
56                           const GURL& url) {
57  storage::FileSystemContext* file_system_context =
58      GetFileSystemContextForExtensionId(profile, kFileManagerAppId);
59
60  file_tasks::ExecuteFileTask(
61      profile,
62      GetFileManagerMainPageUrl(), // Executing the task on behalf of Files.app.
63      task,
64      std::vector<FileSystemURL>(1, file_system_context->CrackURL(url)),
65      file_tasks::FileTaskFinishedCallback());
66}
67
68// Opens the file manager for the specified |url|. Used to implement
69// internal handlers of special action IDs:
70//
71// "open" - Open the file manager for the given folder.
72// "auto-open" - Open the file manager for the given removal drive and close
73//               the file manager when the removal drive is unmounted.
74// "select" - Open the file manager for the given file. The folder containing
75//            the file will be opened with the file selected.
76void OpenFileManagerWithInternalActionId(Profile* profile,
77                                         const GURL& url,
78                                         const std::string& action_id) {
79  DCHECK(action_id == "auto-open" ||
80         action_id == "open" ||
81         action_id == "select");
82  content::RecordAction(base::UserMetricsAction("ShowFileBrowserFullTab"));
83
84  file_tasks::TaskDescriptor task(kFileManagerAppId,
85                                  file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
86                                  action_id);
87  ExecuteFileTaskForUrl(profile, task, url);
88}
89
90// Opens the file with fetched MIME type and calls the callback.
91void OpenFileWithMimeType(Profile* profile,
92                          const base::FilePath& path,
93                          const GURL& url,
94                          const base::Callback<void(bool)>& callback,
95                          const std::string& mime_type) {
96  extensions::app_file_handler_util::PathAndMimeTypeSet path_mime_set;
97  path_mime_set.insert(std::make_pair(path, mime_type));
98
99  std::vector<GURL> file_urls;
100  file_urls.push_back(url);
101
102  std::vector<file_tasks::FullTaskDescriptor> tasks;
103  file_tasks::FindAllTypesOfTasks(
104      profile,
105      drive::util::GetDriveAppRegistryByProfile(profile),
106      path_mime_set,
107      file_urls,
108      &tasks);
109
110  if (tasks.empty()) {
111    callback.Run(false);
112    return;
113  }
114
115  const file_tasks::FullTaskDescriptor* chosen_task = &tasks[0];
116  for (size_t i = 0; i < tasks.size(); ++i) {
117    if (tasks[i].is_default()) {
118      chosen_task = &tasks[i];
119      break;
120    }
121  }
122
123  ExecuteFileTaskForUrl(profile, chosen_task->task_descriptor(), url);
124  callback.Run(true);
125}
126
127// Opens the file specified by |url| by finding and executing a file task for
128// the file. In case of success, calls |callback| with true. Otherwise the
129// returned value is false.
130void OpenFile(Profile* profile,
131              const base::FilePath& path,
132              const GURL& url,
133              const base::Callback<void(bool)>& callback) {
134  extensions::app_file_handler_util::GetMimeTypeForLocalPath(
135      profile,
136      path,
137      base::Bind(&OpenFileWithMimeType, profile, path, url, callback));
138}
139
140// Called when execution of ContinueOpenItem() is completed.
141void OnContinueOpenItemCompleted(Profile* profile,
142                                 const base::FilePath& file_path,
143                                 bool result) {
144  if (!result) {
145    int message;
146    if (file_path.Extension() == FILE_PATH_LITERAL(".dmg"))
147      message = IDS_FILE_BROWSER_ERROR_VIEWING_FILE_FOR_DMG;
148    else if (file_path.Extension() == FILE_PATH_LITERAL(".exe") ||
149             file_path.Extension() == FILE_PATH_LITERAL(".msi"))
150      message = IDS_FILE_BROWSER_ERROR_VIEWING_FILE_FOR_EXECUTABLE;
151    else
152      message = IDS_FILE_BROWSER_ERROR_VIEWING_FILE;
153    ShowWarningMessageBox(profile, file_path, message);
154  }
155}
156
157// Used to implement OpenItem().
158void ContinueOpenItem(Profile* profile,
159                      const base::FilePath& file_path,
160                      const GURL& url,
161                      base::File::Error error) {
162  DCHECK_CURRENTLY_ON(BrowserThread::UI);
163
164  if (error == base::File::FILE_OK) {
165    // A directory exists at |url|. Open it with the file manager.
166    OpenFileManagerWithInternalActionId(profile, url, "open");
167  } else {
168    // |url| should be a file. Open it.
169    OpenFile(profile,
170             file_path,
171             url,
172             base::Bind(&OnContinueOpenItemCompleted, profile, file_path));
173  }
174}
175
176// Converts the |given_path| passed from external callers to the form that the
177// file manager can correctly handle. It first migrates old Drive/Download
178// folder path to the new formats, and then converts path to filesystem URL.
179//
180// When conversion fails, it shows a warning dialog UI and returns false.
181bool ConvertPath(Profile* profile,
182                 const base::FilePath& given_path,
183                 base::FilePath* path,
184                 GURL* url) {
185  // The path may have been stored in preferences in old versions.
186  // We need migration here.
187  // TODO(kinaba): crbug.com/313539 remove it in the future.
188  if (!util::MigratePathFromOldFormat(profile, given_path, path))
189    *path = given_path;
190
191  if (!ConvertAbsoluteFilePathToFileSystemUrl(
192          profile, *path, kFileManagerAppId, url)) {
193    ShowWarningMessageBox(profile, *path,
194                          IDS_FILE_BROWSER_ERROR_UNRESOLVABLE_FILE);
195    return false;
196  }
197  return true;
198}
199
200}  // namespace
201
202void OpenRemovableDrive(Profile* profile, const base::FilePath& file_path) {
203  DCHECK_CURRENTLY_ON(BrowserThread::UI);
204
205  base::FilePath converted_path;
206  GURL url;
207  if (!ConvertPath(profile, file_path, &converted_path, &url))
208    return;
209
210  OpenFileManagerWithInternalActionId(profile, url, "auto-open");
211}
212
213void OpenItem(Profile* profile, const base::FilePath& file_path) {
214  DCHECK_CURRENTLY_ON(BrowserThread::UI);
215
216  base::FilePath converted_path;
217  GURL url;
218  if (!ConvertPath(profile, file_path, &converted_path, &url))
219    return;
220
221  CheckIfDirectoryExists(
222      GetFileSystemContextForExtensionId(profile, kFileManagerAppId),
223      url,
224      base::Bind(&ContinueOpenItem, profile, converted_path, url));
225}
226
227void ShowItemInFolder(Profile* profile, const base::FilePath& file_path) {
228  DCHECK_CURRENTLY_ON(BrowserThread::UI);
229
230  base::FilePath converted_path;
231  GURL url;
232  if (!ConvertPath(profile, file_path, &converted_path, &url))
233    return;
234
235  // This action changes the selection so we do not reuse existing tabs.
236  OpenFileManagerWithInternalActionId(profile, url, "select");
237}
238
239}  // namespace util
240}  // namespace file_manager
241