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/mime_util.h"
16#include "chrome/browser/chromeos/file_manager/path_util.h"
17#include "chrome/browser/chromeos/file_manager/url_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 "content/public/browser/browser_thread.h"
23#include "content/public/browser/user_metrics.h"
24#include "grit/generated_resources.h"
25#include "ui/base/l10n/l10n_util.h"
26#include "webkit/browser/fileapi/file_system_backend.h"
27#include "webkit/browser/fileapi/file_system_context.h"
28#include "webkit/browser/fileapi/file_system_operation_runner.h"
29#include "webkit/browser/fileapi/file_system_url.h"
30
31using content::BrowserThread;
32using fileapi::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  fileapi::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 specified by |url| by finding and executing a file
91// task for the file. Returns false if failed to open the file (i.e. no file
92// task is found).
93bool OpenFile(Profile* profile, const base::FilePath& path, const GURL& url) {
94  // The file is opened per the file extension, hence extension-less files
95  // cannot be opened properly.
96  std::string mime_type = GetMimeTypeForPath(path);
97  extensions::app_file_handler_util::PathAndMimeTypeSet path_mime_set;
98  path_mime_set.insert(std::make_pair(path, mime_type));
99
100  std::vector<GURL> file_urls;
101  file_urls.push_back(url);
102
103  std::vector<file_tasks::FullTaskDescriptor> tasks;
104  file_tasks::FindAllTypesOfTasks(
105      profile,
106      drive::util::GetDriveAppRegistryByProfile(profile),
107      path_mime_set,
108      file_urls,
109      &tasks);
110  if (tasks.empty())
111    return false;
112
113  const file_tasks::FullTaskDescriptor* chosen_task = &tasks[0];
114  for (size_t i = 0; i < tasks.size(); ++i) {
115    if (tasks[i].is_default()) {
116      chosen_task = &tasks[i];
117      break;
118    }
119  }
120
121  ExecuteFileTaskForUrl(profile, chosen_task->task_descriptor(), url);
122  return true;
123}
124
125// Used to implement OpenItem().
126void ContinueOpenItem(Profile* profile,
127                      const base::FilePath& file_path,
128                      const GURL& url,
129                      base::File::Error error) {
130  DCHECK_CURRENTLY_ON(BrowserThread::UI);
131
132  if (error == base::File::FILE_OK) {
133    // A directory exists at |url|. Open it with the file manager.
134    OpenFileManagerWithInternalActionId(profile, url, "open");
135  } else {
136    // |url| should be a file. Open it.
137    if (!OpenFile(profile, file_path, url)) {
138      ShowWarningMessageBox(profile, file_path,
139                            IDS_FILE_BROWSER_ERROR_VIEWING_FILE);
140    }
141  }
142}
143
144// Converts the |given_path| passed from external callers to the form that the
145// file manager can correctly handle. It first migrates old Drive/Download
146// folder path to the new formats, and then converts path to filesystem URL.
147//
148// When conversion fails, it shows a warning dialog UI and returns false.
149bool ConvertPath(Profile* profile,
150                 const base::FilePath& given_path,
151                 base::FilePath* path,
152                 GURL* url) {
153  // The path may have been stored in preferences in old versions.
154  // We need migration here.
155  // TODO(kinaba): crbug.com/313539 remove it in the future.
156  if (!util::MigratePathFromOldFormat(profile, given_path, path))
157    *path = given_path;
158
159  if (!ConvertAbsoluteFilePathToFileSystemUrl(
160          profile, *path, kFileManagerAppId, url)) {
161    ShowWarningMessageBox(profile, *path,
162                          IDS_FILE_BROWSER_ERROR_UNRESOLVABLE_FILE);
163    return false;
164  }
165  return true;
166}
167
168}  // namespace
169
170void OpenRemovableDrive(Profile* profile, const base::FilePath& file_path) {
171  DCHECK_CURRENTLY_ON(BrowserThread::UI);
172
173  base::FilePath converted_path;
174  GURL url;
175  if (!ConvertPath(profile, file_path, &converted_path, &url))
176    return;
177
178  OpenFileManagerWithInternalActionId(profile, url, "auto-open");
179}
180
181void OpenItem(Profile* profile, const base::FilePath& file_path) {
182  DCHECK_CURRENTLY_ON(BrowserThread::UI);
183
184  base::FilePath converted_path;
185  GURL url;
186  if (!ConvertPath(profile, file_path, &converted_path, &url))
187    return;
188
189  CheckIfDirectoryExists(
190      GetFileSystemContextForExtensionId(profile, kFileManagerAppId),
191      url,
192      base::Bind(&ContinueOpenItem, profile, converted_path, url));
193}
194
195void ShowItemInFolder(Profile* profile, const base::FilePath& file_path) {
196  DCHECK_CURRENTLY_ON(BrowserThread::UI);
197
198  base::FilePath converted_path;
199  GURL url;
200  if (!ConvertPath(profile, file_path, &converted_path, &url))
201    return;
202
203  // This action changes the selection so we do not reuse existing tabs.
204  OpenFileManagerWithInternalActionId(profile, url, "select");
205}
206
207}  // namespace util
208}  // namespace file_manager
209