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 31 using content::BrowserThread; 32 using storage::FileSystemURL; 33 34 namespace file_manager { 35 namespace util { 36 namespace { 37 38 // Shows a warning message box saying that the file could not be opened. 39 void 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|. 54 void 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. 76 void 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. 91 void 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. 130 void 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. 141 void 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(). 158 void 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. 181 bool 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 202 void 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 213 void 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 227 void 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