| OLD | NEW |
| (Empty) |
| 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/extensions/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/extensions/file_manager/app_id.h" | |
| 12 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_handlers.
h" | |
| 13 #include "chrome/browser/chromeos/extensions/file_manager/file_tasks.h" | |
| 14 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h" | |
| 15 #include "chrome/browser/chromeos/extensions/file_manager/mime_util.h" | |
| 16 #include "chrome/browser/chromeos/extensions/file_manager/url_util.h" | |
| 17 #include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" | |
| 18 #include "chrome/browser/extensions/extension_service.h" | |
| 19 #include "chrome/browser/extensions/extension_system.h" | |
| 20 #include "chrome/browser/google_apis/task_util.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_handle
r.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 "grit/generated_resources.h" | |
| 34 #include "ui/base/l10n/l10n_util.h" | |
| 35 #include "webkit/browser/fileapi/file_system_backend.h" | |
| 36 #include "webkit/browser/fileapi/file_system_context.h" | |
| 37 #include "webkit/browser/fileapi/file_system_operation_runner.h" | |
| 38 #include "webkit/browser/fileapi/file_system_url.h" | |
| 39 | |
| 40 using content::BrowserContext; | |
| 41 using content::BrowserThread; | |
| 42 using content::UserMetricsAction; | |
| 43 using extensions::Extension; | |
| 44 using extensions::app_file_handler_util::FindFileHandlersForFiles; | |
| 45 using extensions::app_file_handler_util::PathAndMimeTypeSet; | |
| 46 using fileapi::FileSystemURL; | |
| 47 | |
| 48 namespace file_manager { | |
| 49 namespace util { | |
| 50 namespace { | |
| 51 | |
| 52 // Shows a warning message box saying that the file could not be opened. | |
| 53 void ShowWarningMessageBox(Profile* profile, const base::FilePath& file_path) { | |
| 54 // TODO: if FindOrCreateTabbedBrowser creates a new browser the returned | |
| 55 // browser is leaked. | |
| 56 Browser* browser = | |
| 57 chrome::FindOrCreateTabbedBrowser(profile, | |
| 58 chrome::HOST_DESKTOP_TYPE_ASH); | |
| 59 chrome::ShowMessageBox( | |
| 60 browser->window()->GetNativeWindow(), | |
| 61 l10n_util::GetStringFUTF16( | |
| 62 IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE, | |
| 63 UTF8ToUTF16(file_path.BaseName().value())), | |
| 64 l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_VIEWING_FILE), | |
| 65 chrome::MESSAGE_BOX_TYPE_WARNING); | |
| 66 } | |
| 67 | |
| 68 // Grants file system access to the file manager. | |
| 69 bool GrantFileSystemAccessToFileBrowser(Profile* profile) { | |
| 70 // The file manager always runs in the site for its extension id, so that | |
| 71 // is the site for which file access permissions should be granted. | |
| 72 fileapi::ExternalFileSystemBackend* backend = | |
| 73 GetFileSystemContextForExtensionId( | |
| 74 profile, kFileManagerAppId)->external_backend(); | |
| 75 if (!backend) | |
| 76 return false; | |
| 77 backend->GrantFullAccessToExtension(kFileManagerAppId); | |
| 78 return true; | |
| 79 } | |
| 80 | |
| 81 // Executes the |task| for the file specified by |url|. | |
| 82 void ExecuteFileTaskForUrl(Profile* profile, | |
| 83 const file_tasks::TaskDescriptor& task, | |
| 84 const GURL& url) { | |
| 85 // If the file manager has not been open yet then it did not request access | |
| 86 // to the file system. Do it now. | |
| 87 if (!GrantFileSystemAccessToFileBrowser(profile)) | |
| 88 return; | |
| 89 | |
| 90 fileapi::FileSystemContext* file_system_context = | |
| 91 GetFileSystemContextForExtensionId( | |
| 92 profile, kFileManagerAppId); | |
| 93 | |
| 94 // We are executing the task on behalf of the file manager. | |
| 95 const GURL source_url = GetFileManagerMainPageUrl(); | |
| 96 std::vector<FileSystemURL> urls; | |
| 97 urls.push_back(file_system_context->CrackURL(url)); | |
| 98 | |
| 99 file_tasks::ExecuteFileTask( | |
| 100 profile, | |
| 101 source_url, | |
| 102 kFileManagerAppId, | |
| 103 0, // no tab id | |
| 104 task, | |
| 105 urls, | |
| 106 file_tasks::FileTaskFinishedCallback()); | |
| 107 } | |
| 108 | |
| 109 // Opens the file manager for the specified |file_path|. Used to implement | |
| 110 // internal handlers of special action IDs: | |
| 111 // | |
| 112 // "open" - Open the file manager for the given folder. | |
| 113 // "auto-open" - Open the file manager for the given removal drive and close | |
| 114 // the file manager when the removal drive is unmounted. | |
| 115 // "select" - Open the file manager for the given file. The folder containing | |
| 116 // the file will be opened with the file selected. | |
| 117 void OpenFileManagerWithInternalActionId(const base::FilePath& file_path, | |
| 118 const std::string& action_id) { | |
| 119 DCHECK(action_id == "auto-open" || | |
| 120 action_id == "open" || | |
| 121 action_id == "select"); | |
| 122 | |
| 123 content::RecordAction(UserMetricsAction("ShowFileBrowserFullTab")); | |
| 124 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); | |
| 125 | |
| 126 GURL url; | |
| 127 if (!ConvertAbsoluteFilePathToFileSystemUrl( | |
| 128 profile, file_path, kFileManagerAppId, &url)) | |
| 129 return; | |
| 130 | |
| 131 file_tasks::TaskDescriptor task(kFileManagerAppId, | |
| 132 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER, | |
| 133 action_id); | |
| 134 ExecuteFileTaskForUrl(profile, task, url); | |
| 135 } | |
| 136 | |
| 137 // Opens the file specified by |file_path| and |url| with a file handler, | |
| 138 // preferably the default handler for the type of the file. Returns false if | |
| 139 // no file handler is found. | |
| 140 bool OpenFileWithFileHandler(Profile* profile, | |
| 141 const base::FilePath& file_path, | |
| 142 const GURL& url, | |
| 143 const std::string& mime_type, | |
| 144 const std::string& default_task_id) { | |
| 145 ExtensionService* service = profile->GetExtensionService(); | |
| 146 if (!service) | |
| 147 return false; | |
| 148 | |
| 149 PathAndMimeTypeSet files; | |
| 150 files.insert(std::make_pair(file_path, mime_type)); | |
| 151 const extensions::FileHandlerInfo* first_handler = NULL; | |
| 152 const extensions::Extension* extension_for_first_handler = NULL; | |
| 153 | |
| 154 // If we find the default handler, we execute it immediately, but otherwise, | |
| 155 // we remember the first handler, and if there was no default handler, simply | |
| 156 // execute the first one. | |
| 157 for (ExtensionSet::const_iterator iter = service->extensions()->begin(); | |
| 158 iter != service->extensions()->end(); | |
| 159 ++iter) { | |
| 160 const Extension* extension = iter->get(); | |
| 161 | |
| 162 // We don't support using hosted apps to open files. | |
| 163 if (!extension->is_platform_app()) | |
| 164 continue; | |
| 165 | |
| 166 // We only support apps that specify "incognito: split" if in incognito | |
| 167 // mode. | |
| 168 if (profile->IsOffTheRecord() && | |
| 169 !service->IsIncognitoEnabled(extension->id())) | |
| 170 continue; | |
| 171 | |
| 172 typedef std::vector<const extensions::FileHandlerInfo*> FileHandlerList; | |
| 173 FileHandlerList file_handlers = FindFileHandlersForFiles(*extension, files); | |
| 174 for (FileHandlerList::iterator i = file_handlers.begin(); | |
| 175 i != file_handlers.end(); ++i) { | |
| 176 const extensions::FileHandlerInfo* handler = *i; | |
| 177 std::string task_id = file_tasks::MakeTaskID( | |
| 178 extension->id(), | |
| 179 file_tasks::TASK_TYPE_FILE_HANDLER, | |
| 180 handler->id); | |
| 181 if (task_id == default_task_id) { | |
| 182 file_tasks::TaskDescriptor task(extension->id(), | |
| 183 file_tasks::TASK_TYPE_FILE_HANDLER, | |
| 184 handler->id); | |
| 185 ExecuteFileTaskForUrl(profile, task, url); | |
| 186 return true; | |
| 187 | |
| 188 } else if (!first_handler) { | |
| 189 first_handler = handler; | |
| 190 extension_for_first_handler = extension; | |
| 191 } | |
| 192 } | |
| 193 } | |
| 194 if (first_handler) { | |
| 195 file_tasks::TaskDescriptor task(extension_for_first_handler->id(), | |
| 196 file_tasks::TASK_TYPE_FILE_HANDLER, | |
| 197 first_handler->id); | |
| 198 ExecuteFileTaskForUrl(profile, task, url); | |
| 199 return true; | |
| 200 } | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 // Opens the file specified by |file_path| and |url| with the file browser | |
| 205 // handler specified by |handler|. Returns false if failed to open the file. | |
| 206 bool OpenFileWithFileBrowserHandler(Profile* profile, | |
| 207 const base::FilePath& file_path, | |
| 208 const FileBrowserHandler& handler, | |
| 209 const GURL& url) { | |
| 210 file_tasks::TaskDescriptor task(handler.extension_id(), | |
| 211 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER, | |
| 212 handler.id()); | |
| 213 ExecuteFileTaskForUrl(profile, task, url); | |
| 214 return true; | |
| 215 } | |
| 216 | |
| 217 // Opens the file specified by |file_path| with a handler (either of file | |
| 218 // browser handler or file handler, preferably the default handler for the | |
| 219 // type of the file), or opens the file with the browser. Returns false if | |
| 220 // failed to open the file. | |
| 221 bool OpenFileWithHandler(Profile* profile, const base::FilePath& file_path) { | |
| 222 GURL url; | |
| 223 if (!ConvertAbsoluteFilePathToFileSystemUrl( | |
| 224 profile, file_path, kFileManagerAppId, &url)) | |
| 225 return false; | |
| 226 | |
| 227 std::string mime_type = GetMimeTypeForPath(file_path); | |
| 228 std::string default_task_id = file_tasks::GetDefaultTaskIdFromPrefs( | |
| 229 *profile->GetPrefs(), mime_type, file_path.Extension()); | |
| 230 | |
| 231 // We choose the file handler from the following in decreasing priority or | |
| 232 // fail if none support the file type: | |
| 233 // 1. default file browser handler | |
| 234 // 2. default file handler | |
| 235 // 3. a fallback handler (e.g. opening in the browser) | |
| 236 // 4. non-default file handler | |
| 237 // 5. non-default file browser handler | |
| 238 // Note that there can be at most one of default extension and default app. | |
| 239 const FileBrowserHandler* handler = | |
| 240 file_browser_handlers::FindFileBrowserHandlerForURLAndPath( | |
| 241 profile, url, file_path); | |
| 242 if (!handler) { | |
| 243 return OpenFileWithFileHandler( | |
| 244 profile, file_path, url, mime_type, default_task_id); | |
| 245 } | |
| 246 | |
| 247 const file_tasks::TaskDescriptor task_descriptor( | |
| 248 handler->extension_id(), | |
| 249 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER, | |
| 250 handler->id()); | |
| 251 const std::string handler_task_id = | |
| 252 file_tasks::TaskDescriptorToId(task_descriptor); | |
| 253 if (handler_task_id != default_task_id && | |
| 254 !file_browser_handlers::IsFallbackFileBrowserHandler(task_descriptor) && | |
| 255 OpenFileWithFileHandler( | |
| 256 profile, file_path, url, mime_type, default_task_id)) { | |
| 257 return true; | |
| 258 } | |
| 259 return OpenFileWithFileBrowserHandler(profile, file_path, *handler, url); | |
| 260 } | |
| 261 | |
| 262 // Used to implement OpenItem(). | |
| 263 void ContinueOpenItem(Profile* profile, | |
| 264 const base::FilePath& file_path, | |
| 265 base::PlatformFileError error) { | |
| 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 267 | |
| 268 if (error == base::PLATFORM_FILE_OK) { | |
| 269 // A directory exists at |file_path|. Open it with the file manager. | |
| 270 OpenFileManagerWithInternalActionId(file_path, "open"); | |
| 271 } else { | |
| 272 // |file_path| should be a file. Open it with a handler. | |
| 273 if (!OpenFileWithHandler(profile, file_path)) | |
| 274 ShowWarningMessageBox(profile, file_path); | |
| 275 } | |
| 276 } | |
| 277 | |
| 278 // Used to implement CheckIfDirectoryExists(). | |
| 279 void CheckIfDirectoryExistsOnIOThread( | |
| 280 scoped_refptr<fileapi::FileSystemContext> file_system_context, | |
| 281 const GURL& url, | |
| 282 const fileapi::FileSystemOperationRunner::StatusCallback& callback) { | |
| 283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 284 | |
| 285 fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url); | |
| 286 file_system_context->operation_runner()->DirectoryExists( | |
| 287 file_system_url, callback); | |
| 288 } | |
| 289 | |
| 290 // Checks if a directory exists at |url|. | |
| 291 void CheckIfDirectoryExists( | |
| 292 scoped_refptr<fileapi::FileSystemContext> file_system_context, | |
| 293 const GURL& url, | |
| 294 const fileapi::FileSystemOperationRunner::StatusCallback& callback) { | |
| 295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 296 | |
| 297 BrowserThread::PostTask( | |
| 298 BrowserThread::IO, FROM_HERE, | |
| 299 base::Bind(&CheckIfDirectoryExistsOnIOThread, | |
| 300 file_system_context, | |
| 301 url, | |
| 302 google_apis::CreateRelayCallback(callback))); | |
| 303 } | |
| 304 | |
| 305 } // namespace | |
| 306 | |
| 307 void OpenRemovableDrive(const base::FilePath& file_path) { | |
| 308 OpenFileManagerWithInternalActionId(file_path, "auto-open"); | |
| 309 } | |
| 310 | |
| 311 void OpenItem(const base::FilePath& file_path) { | |
| 312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 313 | |
| 314 Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord(); | |
| 315 GURL url; | |
| 316 if (!ConvertAbsoluteFilePathToFileSystemUrl( | |
| 317 profile, file_path, kFileManagerAppId, &url) || | |
| 318 !GrantFileSystemAccessToFileBrowser(profile)) { | |
| 319 ShowWarningMessageBox(profile, file_path); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 scoped_refptr<fileapi::FileSystemContext> file_system_context = | |
| 324 GetFileSystemContextForExtensionId( | |
| 325 profile, kFileManagerAppId); | |
| 326 | |
| 327 CheckIfDirectoryExists(file_system_context, url, | |
| 328 base::Bind(&ContinueOpenItem, profile, file_path)); | |
| 329 } | |
| 330 | |
| 331 void ShowItemInFolder(const base::FilePath& file_path) { | |
| 332 // This action changes the selection so we do not reuse existing tabs. | |
| 333 OpenFileManagerWithInternalActionId(file_path, "select"); | |
| 334 } | |
| 335 | |
| 336 } // namespace util | |
| 337 } // namespace file_manager | |
| OLD | NEW |