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_browser_handlers.h" 14 #include "chrome/browser/chromeos/file_manager/file_tasks.h" 15 #include "chrome/browser/chromeos/file_manager/fileapi_util.h" 16 #include "chrome/browser/chromeos/file_manager/mime_util.h" 17 #include "chrome/browser/chromeos/file_manager/url_util.h" 18 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" 19 #include "chrome/browser/extensions/extension_service.h" 20 #include "chrome/browser/extensions/extension_system.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "chrome/browser/profiles/profile_manager.h" 23 #include "chrome/browser/ui/browser.h" 24 #include "chrome/browser/ui/browser_finder.h" 25 #include "chrome/browser/ui/browser_tabstrip.h" 26 #include "chrome/browser/ui/browser_window.h" 27 #include "chrome/browser/ui/extensions/application_launch.h" 28 #include "chrome/browser/ui/simple_message_box.h" 29 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h" 30 #include "content/public/browser/browser_thread.h" 31 #include "content/public/browser/storage_partition.h" 32 #include "content/public/browser/user_metrics.h" 33 #include "google_apis/drive/task_util.h" 34 #include "grit/generated_resources.h" 35 #include "ui/base/l10n/l10n_util.h" 36 #include "webkit/browser/fileapi/file_system_backend.h" 37 #include "webkit/browser/fileapi/file_system_context.h" 38 #include "webkit/browser/fileapi/file_system_operation_runner.h" 39 #include "webkit/browser/fileapi/file_system_url.h" 40 41 using content::BrowserContext; 42 using content::BrowserThread; 43 using content::UserMetricsAction; 44 using extensions::Extension; 45 using extensions::app_file_handler_util::FindFileHandlersForFiles; 46 using extensions::app_file_handler_util::PathAndMimeTypeSet; 47 using fileapi::FileSystemURL; 48 49 namespace file_manager { 50 namespace util { 51 namespace { 52 53 // Shows a warning message box saying that the file could not be opened. 54 void ShowWarningMessageBox(Profile* profile, const base::FilePath& file_path) { 55 Browser* browser = chrome::FindTabbedBrowser( 56 profile, false, chrome::HOST_DESKTOP_TYPE_ASH); 57 chrome::ShowMessageBox( 58 browser ? browser->window()->GetNativeWindow() : NULL, 59 l10n_util::GetStringFUTF16( 60 IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE, 61 UTF8ToUTF16(file_path.BaseName().value())), 62 l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_VIEWING_FILE), 63 chrome::MESSAGE_BOX_TYPE_WARNING); 64 } 65 66 // Grants file system access to the file manager. 67 bool GrantFileSystemAccessToFileBrowser(Profile* profile) { 68 // The file manager always runs in the site for its extension id, so that 69 // is the site for which file access permissions should be granted. 70 fileapi::ExternalFileSystemBackend* backend = 71 GetFileSystemContextForExtensionId( 72 profile, kFileManagerAppId)->external_backend(); 73 if (!backend) 74 return false; 75 backend->GrantFullAccessToExtension(kFileManagerAppId); 76 return true; 77 } 78 79 // Executes the |task| for the file specified by |url|. 80 void ExecuteFileTaskForUrl(Profile* profile, 81 const file_tasks::TaskDescriptor& task, 82 const GURL& url) { 83 // If the file manager has not been open yet then it did not request access 84 // to the file system. Do it now. 85 if (!GrantFileSystemAccessToFileBrowser(profile)) 86 return; 87 88 fileapi::FileSystemContext* file_system_context = 89 GetFileSystemContextForExtensionId( 90 profile, kFileManagerAppId); 91 92 // We are executing the task on behalf of the file manager. 93 const GURL source_url = GetFileManagerMainPageUrl(); 94 std::vector<FileSystemURL> urls; 95 urls.push_back(file_system_context->CrackURL(url)); 96 97 file_tasks::ExecuteFileTask( 98 profile, 99 source_url, 100 kFileManagerAppId, 101 task, 102 urls, 103 file_tasks::FileTaskFinishedCallback()); 104 } 105 106 // Opens the file manager for the specified |file_path|. Used to implement 107 // internal handlers of special action IDs: 108 // 109 // "open" - Open the file manager for the given folder. 110 // "auto-open" - Open the file manager for the given removal drive and close 111 // the file manager when the removal drive is unmounted. 112 // "select" - Open the file manager for the given file. The folder containing 113 // the file will be opened with the file selected. 114 void OpenFileManagerWithInternalActionId(Profile* profile, 115 const base::FilePath& file_path, 116 const std::string& action_id) { 117 DCHECK(action_id == "auto-open" || 118 action_id == "open" || 119 action_id == "select"); 120 121 content::RecordAction(UserMetricsAction("ShowFileBrowserFullTab")); 122 123 GURL url; 124 if (!ConvertAbsoluteFilePathToFileSystemUrl( 125 profile, file_path, kFileManagerAppId, &url)) 126 return; 127 128 file_tasks::TaskDescriptor task(kFileManagerAppId, 129 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER, 130 action_id); 131 ExecuteFileTaskForUrl(profile, task, url); 132 } 133 134 // Opens the file specified by |file_path| by finding and executing a file 135 // task for the file. Returns false if failed to open the file (i.e. no file 136 // task is found). 137 bool OpenFile(Profile* profile, const base::FilePath& file_path) { 138 GURL url; 139 if (!ConvertAbsoluteFilePathToFileSystemUrl( 140 profile, file_path, kFileManagerAppId, &url)) 141 return false; 142 143 // The file is opened per the file extension, hence extension-less files 144 // cannot be opened properly. 145 std::string mime_type = GetMimeTypeForPath(file_path); 146 extensions::app_file_handler_util::PathAndMimeTypeSet path_mime_set; 147 path_mime_set.insert(std::make_pair(file_path, mime_type)); 148 149 std::vector<GURL> file_urls; 150 file_urls.push_back(url); 151 152 std::vector<file_tasks::FullTaskDescriptor> tasks; 153 file_tasks::FindAllTypesOfTasks( 154 profile, 155 drive::util::GetDriveAppRegistryByProfile(profile), 156 path_mime_set, 157 file_urls, 158 &tasks); 159 if (tasks.empty()) 160 return false; 161 162 const file_tasks::FullTaskDescriptor* chosen_task = &tasks[0]; 163 for (size_t i = 0; i < tasks.size(); ++i) { 164 if (tasks[i].is_default()) { 165 chosen_task = &tasks[i]; 166 break; 167 } 168 } 169 170 ExecuteFileTaskForUrl(profile, chosen_task->task_descriptor(), url); 171 return true; 172 } 173 174 // Used to implement OpenItem(). 175 void ContinueOpenItem(Profile* profile, 176 const base::FilePath& file_path, 177 base::PlatformFileError error) { 178 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 179 180 if (error == base::PLATFORM_FILE_OK) { 181 // A directory exists at |file_path|. Open it with the file manager. 182 OpenFileManagerWithInternalActionId(profile, file_path, "open"); 183 } else { 184 // |file_path| should be a file. Open it. 185 if (!OpenFile(profile, file_path)) 186 ShowWarningMessageBox(profile, file_path); 187 } 188 } 189 190 // Used to implement CheckIfDirectoryExists(). 191 void CheckIfDirectoryExistsOnIOThread( 192 scoped_refptr<fileapi::FileSystemContext> file_system_context, 193 const GURL& url, 194 const fileapi::FileSystemOperationRunner::StatusCallback& callback) { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 196 197 fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url); 198 file_system_context->operation_runner()->DirectoryExists( 199 file_system_url, callback); 200 } 201 202 // Checks if a directory exists at |url|. 203 void CheckIfDirectoryExists( 204 scoped_refptr<fileapi::FileSystemContext> file_system_context, 205 const GURL& url, 206 const fileapi::FileSystemOperationRunner::StatusCallback& callback) { 207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 208 209 BrowserThread::PostTask( 210 BrowserThread::IO, FROM_HERE, 211 base::Bind(&CheckIfDirectoryExistsOnIOThread, 212 file_system_context, 213 url, 214 google_apis::CreateRelayCallback(callback))); 215 } 216 217 } // namespace 218 219 void OpenRemovableDrive(Profile* profile, const base::FilePath& file_path) { 220 OpenFileManagerWithInternalActionId(profile, file_path, "auto-open"); 221 } 222 223 void OpenItem(Profile* profile, const base::FilePath& file_path) { 224 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 225 226 GURL url; 227 if (!ConvertAbsoluteFilePathToFileSystemUrl( 228 profile, file_path, kFileManagerAppId, &url) || 229 !GrantFileSystemAccessToFileBrowser(profile)) { 230 ShowWarningMessageBox(profile, file_path); 231 return; 232 } 233 234 scoped_refptr<fileapi::FileSystemContext> file_system_context = 235 GetFileSystemContextForExtensionId( 236 profile, kFileManagerAppId); 237 238 CheckIfDirectoryExists(file_system_context, url, 239 base::Bind(&ContinueOpenItem, profile, file_path)); 240 } 241 242 void ShowItemInFolder(Profile* profile, const base::FilePath& file_path) { 243 // This action changes the selection so we do not reuse existing tabs. 244 OpenFileManagerWithInternalActionId(profile, file_path, "select"); 245 } 246 247 } // namespace util 248 } // namespace file_manager 249