| 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/file_browser_handlers.
h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/file_util.h" | |
| 9 #include "base/i18n/case_conversion.h" | |
| 10 #include "base/strings/utf_string_conversions.h" | |
| 11 #include "chrome/browser/chromeos/drive/file_system_util.h" | |
| 12 #include "chrome/browser/chromeos/extensions/file_manager/app_id.h" | |
| 13 #include "chrome/browser/chromeos/extensions/file_manager/fileapi_util.h" | |
| 14 #include "chrome/browser/chromeos/extensions/file_manager/open_with_browser.h" | |
| 15 #include "chrome/browser/chromeos/fileapi/file_system_backend.h" | |
| 16 #include "chrome/browser/extensions/event_router.h" | |
| 17 #include "chrome/browser/extensions/extension_host.h" | |
| 18 #include "chrome/browser/extensions/extension_service.h" | |
| 19 #include "chrome/browser/extensions/extension_system.h" | |
| 20 #include "chrome/browser/extensions/lazy_background_task_queue.h" | |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 22 #include "chrome/browser/ui/browser_finder.h" | |
| 23 #include "chrome/common/extensions/api/file_browser_handlers/file_browser_handle
r.h" | |
| 24 #include "chrome/common/extensions/background_info.h" | |
| 25 #include "content/public/browser/browser_thread.h" | |
| 26 #include "content/public/browser/child_process_security_policy.h" | |
| 27 #include "content/public/browser/render_process_host.h" | |
| 28 #include "content/public/browser/site_instance.h" | |
| 29 #include "content/public/browser/web_contents.h" | |
| 30 #include "net/base/escape.h" | |
| 31 #include "webkit/browser/fileapi/file_system_context.h" | |
| 32 #include "webkit/browser/fileapi/file_system_url.h" | |
| 33 | |
| 34 using content::BrowserThread; | |
| 35 using content::ChildProcessSecurityPolicy; | |
| 36 using content::SiteInstance; | |
| 37 using content::WebContents; | |
| 38 using extensions::Extension; | |
| 39 using fileapi::FileSystemURL; | |
| 40 | |
| 41 namespace file_manager { | |
| 42 namespace file_browser_handlers { | |
| 43 namespace { | |
| 44 | |
| 45 // Returns process id of the process the extension is running in. | |
| 46 int ExtractProcessFromExtensionId(Profile* profile, | |
| 47 const std::string& extension_id) { | |
| 48 GURL extension_url = | |
| 49 Extension::GetBaseURLFromExtensionId(extension_id); | |
| 50 ExtensionProcessManager* manager = | |
| 51 extensions::ExtensionSystem::Get(profile)->process_manager(); | |
| 52 | |
| 53 SiteInstance* site_instance = manager->GetSiteInstanceForURL(extension_url); | |
| 54 if (!site_instance || !site_instance->HasProcess()) | |
| 55 return -1; | |
| 56 content::RenderProcessHost* process = site_instance->GetProcess(); | |
| 57 | |
| 58 return process->GetID(); | |
| 59 } | |
| 60 | |
| 61 // Finds a file browser handler that matches |action_id|. Returns NULL if not | |
| 62 // found. | |
| 63 const FileBrowserHandler* FindFileBrowserHandlerForActionId( | |
| 64 const Extension* extension, | |
| 65 const std::string& action_id) { | |
| 66 FileBrowserHandler::List* handler_list = | |
| 67 FileBrowserHandler::GetHandlers(extension); | |
| 68 for (FileBrowserHandler::List::const_iterator handler_iter = | |
| 69 handler_list->begin(); | |
| 70 handler_iter != handler_list->end(); | |
| 71 ++handler_iter) { | |
| 72 if (handler_iter->get()->id() == action_id) | |
| 73 return handler_iter->get(); | |
| 74 } | |
| 75 return NULL; | |
| 76 } | |
| 77 | |
| 78 std::string EscapedUtf8ToLower(const std::string& str) { | |
| 79 string16 utf16 = UTF8ToUTF16( | |
| 80 net::UnescapeURLComponent(str, net::UnescapeRule::NORMAL)); | |
| 81 return net::EscapeUrlEncodedData( | |
| 82 UTF16ToUTF8(base::i18n::ToLower(utf16)), | |
| 83 false /* do not replace space with plus */); | |
| 84 } | |
| 85 | |
| 86 // Finds file browser handlers that can handle the |selected_file_url|. | |
| 87 FileBrowserHandlerList FindFileBrowserHandlersForURL( | |
| 88 Profile* profile, | |
| 89 const GURL& selected_file_url) { | |
| 90 ExtensionService* service = | |
| 91 extensions::ExtensionSystem::Get(profile)->extension_service(); | |
| 92 // In unit-tests, we may not have an ExtensionService. | |
| 93 if (!service) | |
| 94 return FileBrowserHandlerList(); | |
| 95 | |
| 96 // We need case-insensitive matching, and pattern in the handler is already | |
| 97 // in lower case. | |
| 98 const GURL lowercase_url(EscapedUtf8ToLower(selected_file_url.spec())); | |
| 99 | |
| 100 FileBrowserHandlerList results; | |
| 101 for (ExtensionSet::const_iterator iter = service->extensions()->begin(); | |
| 102 iter != service->extensions()->end(); | |
| 103 ++iter) { | |
| 104 const Extension* extension = iter->get(); | |
| 105 if (profile->IsOffTheRecord() && | |
| 106 !service->IsIncognitoEnabled(extension->id())) | |
| 107 continue; | |
| 108 | |
| 109 FileBrowserHandler::List* handler_list = | |
| 110 FileBrowserHandler::GetHandlers(extension); | |
| 111 if (!handler_list) | |
| 112 continue; | |
| 113 for (FileBrowserHandler::List::const_iterator handler_iter = | |
| 114 handler_list->begin(); | |
| 115 handler_iter != handler_list->end(); | |
| 116 ++handler_iter) { | |
| 117 const FileBrowserHandler* handler = handler_iter->get(); | |
| 118 if (!handler->MatchesURL(lowercase_url)) | |
| 119 continue; | |
| 120 | |
| 121 results.push_back(handler_iter->get()); | |
| 122 } | |
| 123 } | |
| 124 return results; | |
| 125 } | |
| 126 | |
| 127 // Finds a file browser handler that matches |extension_id| and |action_id| | |
| 128 // from |handler_list|. Returns a mutable iterator to the handler if | |
| 129 // found. Returns handler_list->end() if not found. | |
| 130 FileBrowserHandlerList::iterator | |
| 131 FindFileBrowserHandlerForExtensionIdAndActionId( | |
| 132 FileBrowserHandlerList* handler_list, | |
| 133 const std::string& extension_id, | |
| 134 const std::string& action_id) { | |
| 135 DCHECK(handler_list); | |
| 136 | |
| 137 FileBrowserHandlerList::iterator iter = handler_list->begin(); | |
| 138 while (iter != handler_list->end() && | |
| 139 !((*iter)->extension_id() == extension_id && | |
| 140 (*iter)->id() == action_id)) { | |
| 141 ++iter; | |
| 142 } | |
| 143 return iter; | |
| 144 } | |
| 145 | |
| 146 // This class is used to execute a file browser handler task. Here's how this | |
| 147 // works: | |
| 148 // | |
| 149 // 1) Open the "external" file system | |
| 150 // 2) Set up permissions for the target files on the external file system. | |
| 151 // 3) Raise onExecute event with the action ID and entries of the target | |
| 152 // files. The event will launch the file browser handler if not active. | |
| 153 // 4) In the file browser handler, onExecute event is handled and executes the | |
| 154 // task in JavaScript. | |
| 155 // | |
| 156 // That said, the class itself does not execute a task. The task will be | |
| 157 // executed in JavaScript. | |
| 158 class FileBrowserHandlerExecutor { | |
| 159 public: | |
| 160 FileBrowserHandlerExecutor(Profile* profile, | |
| 161 const Extension* extension, | |
| 162 int32 tab_id, | |
| 163 const std::string& action_id); | |
| 164 | |
| 165 // Executes the task for each file. |done| will be run with the result. | |
| 166 void Execute(const std::vector<FileSystemURL>& file_urls, | |
| 167 const file_tasks::FileTaskFinishedCallback& done); | |
| 168 | |
| 169 private: | |
| 170 // This object is responsible to delete itself. | |
| 171 virtual ~FileBrowserHandlerExecutor(); | |
| 172 | |
| 173 struct FileDefinition { | |
| 174 FileDefinition(); | |
| 175 ~FileDefinition(); | |
| 176 | |
| 177 base::FilePath virtual_path; | |
| 178 base::FilePath absolute_path; | |
| 179 bool is_directory; | |
| 180 }; | |
| 181 | |
| 182 typedef std::vector<FileDefinition> FileDefinitionList; | |
| 183 | |
| 184 // Checks legitimacy of file url and grants file RO access permissions from | |
| 185 // handler (target) extension and its renderer process. | |
| 186 static FileDefinitionList SetupFileAccessPermissions( | |
| 187 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
| 188 const scoped_refptr<const Extension>& handler_extension, | |
| 189 const std::vector<FileSystemURL>& file_urls); | |
| 190 | |
| 191 void DidOpenFileSystem(const std::vector<FileSystemURL>& file_urls, | |
| 192 base::PlatformFileError result, | |
| 193 const std::string& file_system_name, | |
| 194 const GURL& file_system_root); | |
| 195 | |
| 196 void ExecuteDoneOnUIThread(bool success); | |
| 197 void ExecuteFileActionsOnUIThread(const std::string& file_system_name, | |
| 198 const GURL& file_system_root, | |
| 199 const FileDefinitionList& file_list); | |
| 200 void SetupPermissionsAndDispatchEvent(const std::string& file_system_name, | |
| 201 const GURL& file_system_root, | |
| 202 const FileDefinitionList& file_list, | |
| 203 int handler_pid_in, | |
| 204 extensions::ExtensionHost* host); | |
| 205 | |
| 206 // Registers file permissions from |handler_host_permissions_| with | |
| 207 // ChildProcessSecurityPolicy for process with id |handler_pid|. | |
| 208 void SetupHandlerHostFileAccessPermissions( | |
| 209 const FileDefinitionList& file_list, | |
| 210 const Extension* extension, | |
| 211 int handler_pid); | |
| 212 | |
| 213 Profile* profile_; | |
| 214 scoped_refptr<const Extension> extension_; | |
| 215 int32 tab_id_; | |
| 216 const std::string action_id_; | |
| 217 file_tasks::FileTaskFinishedCallback done_; | |
| 218 base::WeakPtrFactory<FileBrowserHandlerExecutor> weak_ptr_factory_; | |
| 219 | |
| 220 DISALLOW_COPY_AND_ASSIGN(FileBrowserHandlerExecutor); | |
| 221 }; | |
| 222 | |
| 223 FileBrowserHandlerExecutor::FileDefinition::FileDefinition() | |
| 224 : is_directory(false) { | |
| 225 } | |
| 226 | |
| 227 FileBrowserHandlerExecutor::FileDefinition::~FileDefinition() { | |
| 228 } | |
| 229 | |
| 230 // static | |
| 231 FileBrowserHandlerExecutor::FileDefinitionList | |
| 232 FileBrowserHandlerExecutor::SetupFileAccessPermissions( | |
| 233 scoped_refptr<fileapi::FileSystemContext> file_system_context_handler, | |
| 234 const scoped_refptr<const Extension>& handler_extension, | |
| 235 const std::vector<FileSystemURL>& file_urls) { | |
| 236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 237 DCHECK(handler_extension.get()); | |
| 238 | |
| 239 fileapi::ExternalFileSystemBackend* backend = | |
| 240 file_system_context_handler->external_backend(); | |
| 241 | |
| 242 FileDefinitionList file_list; | |
| 243 for (size_t i = 0; i < file_urls.size(); ++i) { | |
| 244 const FileSystemURL& url = file_urls[i]; | |
| 245 | |
| 246 // Check if this file system entry exists first. | |
| 247 base::PlatformFileInfo file_info; | |
| 248 | |
| 249 base::FilePath local_path = url.path(); | |
| 250 base::FilePath virtual_path = url.virtual_path(); | |
| 251 | |
| 252 bool is_drive_file = url.type() == fileapi::kFileSystemTypeDrive; | |
| 253 DCHECK(!is_drive_file || drive::util::IsUnderDriveMountPoint(local_path)); | |
| 254 | |
| 255 // If the file is under drive mount point, there is no actual file to be | |
| 256 // found on the url.path(). | |
| 257 if (!is_drive_file) { | |
| 258 if (!base::PathExists(local_path) || | |
| 259 file_util::IsLink(local_path) || | |
| 260 !file_util::GetFileInfo(local_path, &file_info)) { | |
| 261 continue; | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 // Grant access to this particular file to target extension. This will | |
| 266 // ensure that the target extension can access only this FS entry and | |
| 267 // prevent from traversing FS hierarchy upward. | |
| 268 backend->GrantFileAccessToExtension( | |
| 269 handler_extension->id(), virtual_path); | |
| 270 | |
| 271 // Output values. | |
| 272 FileDefinition file; | |
| 273 file.virtual_path = virtual_path; | |
| 274 file.is_directory = file_info.is_directory; | |
| 275 file.absolute_path = local_path; | |
| 276 file_list.push_back(file); | |
| 277 } | |
| 278 return file_list; | |
| 279 } | |
| 280 | |
| 281 FileBrowserHandlerExecutor::FileBrowserHandlerExecutor( | |
| 282 Profile* profile, | |
| 283 const Extension* extension, | |
| 284 int32 tab_id, | |
| 285 const std::string& action_id) | |
| 286 : profile_(profile), | |
| 287 extension_(extension), | |
| 288 tab_id_(tab_id), | |
| 289 action_id_(action_id), | |
| 290 weak_ptr_factory_(this) { | |
| 291 } | |
| 292 | |
| 293 FileBrowserHandlerExecutor::~FileBrowserHandlerExecutor() {} | |
| 294 | |
| 295 void FileBrowserHandlerExecutor::Execute( | |
| 296 const std::vector<FileSystemURL>& file_urls, | |
| 297 const file_tasks::FileTaskFinishedCallback& done) { | |
| 298 done_ = done; | |
| 299 | |
| 300 // Get file system context for the extension to which onExecute event will be | |
| 301 // sent. The file access permissions will be granted to the extension in the | |
| 302 // file system context for the files in |file_urls|. | |
| 303 util::GetFileSystemContextForExtensionId( | |
| 304 profile_, extension_->id())->OpenFileSystem( | |
| 305 Extension::GetBaseURLFromExtensionId(extension_->id()).GetOrigin(), | |
| 306 fileapi::kFileSystemTypeExternal, | |
| 307 fileapi::OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT, | |
| 308 base::Bind(&FileBrowserHandlerExecutor::DidOpenFileSystem, | |
| 309 weak_ptr_factory_.GetWeakPtr(), | |
| 310 file_urls)); | |
| 311 } | |
| 312 | |
| 313 void FileBrowserHandlerExecutor::DidOpenFileSystem( | |
| 314 const std::vector<FileSystemURL>& file_urls, | |
| 315 base::PlatformFileError result, | |
| 316 const std::string& file_system_name, | |
| 317 const GURL& file_system_root) { | |
| 318 if (result != base::PLATFORM_FILE_OK) { | |
| 319 ExecuteDoneOnUIThread(false); | |
| 320 return; | |
| 321 } | |
| 322 | |
| 323 scoped_refptr<fileapi::FileSystemContext> file_system_context( | |
| 324 util::GetFileSystemContextForExtensionId( | |
| 325 profile_, extension_->id())); | |
| 326 BrowserThread::PostTaskAndReplyWithResult( | |
| 327 BrowserThread::FILE, | |
| 328 FROM_HERE, | |
| 329 base::Bind(&SetupFileAccessPermissions, | |
| 330 file_system_context, | |
| 331 extension_, | |
| 332 file_urls), | |
| 333 base::Bind(&FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread, | |
| 334 weak_ptr_factory_.GetWeakPtr(), | |
| 335 file_system_name, | |
| 336 file_system_root)); | |
| 337 } | |
| 338 | |
| 339 void FileBrowserHandlerExecutor::ExecuteDoneOnUIThread(bool success) { | |
| 340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 341 if (!done_.is_null()) | |
| 342 done_.Run(success); | |
| 343 delete this; | |
| 344 } | |
| 345 | |
| 346 void FileBrowserHandlerExecutor::ExecuteFileActionsOnUIThread( | |
| 347 const std::string& file_system_name, | |
| 348 const GURL& file_system_root, | |
| 349 const FileDefinitionList& file_list) { | |
| 350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 351 | |
| 352 if (file_list.empty()) { | |
| 353 ExecuteDoneOnUIThread(false); | |
| 354 return; | |
| 355 } | |
| 356 | |
| 357 int handler_pid = ExtractProcessFromExtensionId(profile_, extension_->id()); | |
| 358 if (handler_pid <= 0 && | |
| 359 !extensions::BackgroundInfo::HasLazyBackgroundPage(extension_.get())) { | |
| 360 ExecuteDoneOnUIThread(false); | |
| 361 return; | |
| 362 } | |
| 363 | |
| 364 if (handler_pid > 0) { | |
| 365 SetupPermissionsAndDispatchEvent(file_system_name, file_system_root, | |
| 366 file_list, handler_pid, NULL); | |
| 367 } else { | |
| 368 // We have to wake the handler background page before we proceed. | |
| 369 extensions::LazyBackgroundTaskQueue* queue = | |
| 370 extensions::ExtensionSystem::Get(profile_)-> | |
| 371 lazy_background_task_queue(); | |
| 372 if (!queue->ShouldEnqueueTask(profile_, extension_.get())) { | |
| 373 ExecuteDoneOnUIThread(false); | |
| 374 return; | |
| 375 } | |
| 376 queue->AddPendingTask( | |
| 377 profile_, extension_->id(), | |
| 378 base::Bind( | |
| 379 &FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent, | |
| 380 weak_ptr_factory_.GetWeakPtr(), | |
| 381 file_system_name, | |
| 382 file_system_root, | |
| 383 file_list, | |
| 384 handler_pid)); | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 void FileBrowserHandlerExecutor::SetupPermissionsAndDispatchEvent( | |
| 389 const std::string& file_system_name, | |
| 390 const GURL& file_system_root, | |
| 391 const FileDefinitionList& file_list, | |
| 392 int handler_pid_in, | |
| 393 extensions::ExtensionHost* host) { | |
| 394 int handler_pid = host ? host->render_process_host()->GetID() : | |
| 395 handler_pid_in; | |
| 396 | |
| 397 if (handler_pid <= 0) { | |
| 398 ExecuteDoneOnUIThread(false); | |
| 399 return; | |
| 400 } | |
| 401 | |
| 402 extensions::EventRouter* event_router = | |
| 403 extensions::ExtensionSystem::Get(profile_)->event_router(); | |
| 404 if (!event_router) { | |
| 405 ExecuteDoneOnUIThread(false); | |
| 406 return; | |
| 407 } | |
| 408 | |
| 409 SetupHandlerHostFileAccessPermissions( | |
| 410 file_list, extension_.get(), handler_pid); | |
| 411 | |
| 412 scoped_ptr<ListValue> event_args(new ListValue()); | |
| 413 event_args->Append(new base::StringValue(action_id_)); | |
| 414 DictionaryValue* details = new DictionaryValue(); | |
| 415 event_args->Append(details); | |
| 416 // Get file definitions. These will be replaced with Entry instances by | |
| 417 // dispatchEvent() method from event_binding.js. | |
| 418 ListValue* file_entries = new ListValue(); | |
| 419 details->Set("entries", file_entries); | |
| 420 for (FileDefinitionList::const_iterator iter = file_list.begin(); | |
| 421 iter != file_list.end(); | |
| 422 ++iter) { | |
| 423 DictionaryValue* file_def = new DictionaryValue(); | |
| 424 file_entries->Append(file_def); | |
| 425 file_def->SetString("fileSystemName", file_system_name); | |
| 426 file_def->SetString("fileSystemRoot", file_system_root.spec()); | |
| 427 base::FilePath root(FILE_PATH_LITERAL("/")); | |
| 428 base::FilePath full_path = root.Append(iter->virtual_path); | |
| 429 file_def->SetString("fileFullPath", full_path.value()); | |
| 430 file_def->SetBoolean("fileIsDirectory", iter->is_directory); | |
| 431 } | |
| 432 | |
| 433 details->SetInteger("tab_id", tab_id_); | |
| 434 | |
| 435 scoped_ptr<extensions::Event> event(new extensions::Event( | |
| 436 "fileBrowserHandler.onExecute", event_args.Pass())); | |
| 437 event->restrict_to_profile = profile_; | |
| 438 event_router->DispatchEventToExtension(extension_->id(), event.Pass()); | |
| 439 | |
| 440 ExecuteDoneOnUIThread(true); | |
| 441 } | |
| 442 | |
| 443 void FileBrowserHandlerExecutor::SetupHandlerHostFileAccessPermissions( | |
| 444 const FileDefinitionList& file_list, | |
| 445 const Extension* extension, | |
| 446 int handler_pid) { | |
| 447 const FileBrowserHandler* action = FindFileBrowserHandlerForActionId( | |
| 448 extension_, action_id_); | |
| 449 for (FileDefinitionList::const_iterator iter = file_list.begin(); | |
| 450 iter != file_list.end(); | |
| 451 ++iter) { | |
| 452 if (!action) | |
| 453 continue; | |
| 454 if (action->CanRead()) { | |
| 455 content::ChildProcessSecurityPolicy::GetInstance()->GrantReadFile( | |
| 456 handler_pid, iter->absolute_path); | |
| 457 } | |
| 458 if (action->CanWrite()) { | |
| 459 content::ChildProcessSecurityPolicy::GetInstance()-> | |
| 460 GrantCreateReadWriteFile(handler_pid, iter->absolute_path); | |
| 461 } | |
| 462 } | |
| 463 } | |
| 464 | |
| 465 // Returns true if |extension_id| and |action_id| indicate that the file | |
| 466 // currently being handled should be opened with the browser. This function | |
| 467 // is used to handle certain action IDs of the file manager. | |
| 468 bool ShouldBeOpenedWithBrowser(const std::string& extension_id, | |
| 469 const std::string& action_id) { | |
| 470 | |
| 471 return (extension_id == kFileManagerAppId && | |
| 472 (action_id == "view-pdf" || | |
| 473 action_id == "view-swf" || | |
| 474 action_id == "view-in-browser" || | |
| 475 action_id == "install-crx" || | |
| 476 action_id == "open-hosted-generic" || | |
| 477 action_id == "open-hosted-gdoc" || | |
| 478 action_id == "open-hosted-gsheet" || | |
| 479 action_id == "open-hosted-gslides")); | |
| 480 } | |
| 481 | |
| 482 // Opens the files specified by |file_urls| with the browser for |profile|. | |
| 483 // Returns true on success. It's a failure if no files are opened. | |
| 484 bool OpenFilesWithBrowser(Profile* profile, | |
| 485 const std::vector<FileSystemURL>& file_urls) { | |
| 486 int num_opened = 0; | |
| 487 for (size_t i = 0; i < file_urls.size(); ++i) { | |
| 488 const FileSystemURL& file_url = file_urls[i]; | |
| 489 if (chromeos::FileSystemBackend::CanHandleURL(file_url)) { | |
| 490 const base::FilePath& file_path = file_url.path(); | |
| 491 num_opened += util::OpenFileWithBrowser(profile, file_path); | |
| 492 } | |
| 493 } | |
| 494 return num_opened > 0; | |
| 495 } | |
| 496 | |
| 497 } // namespace | |
| 498 | |
| 499 bool ExecuteFileBrowserHandler( | |
| 500 Profile* profile, | |
| 501 const Extension* extension, | |
| 502 int32 tab_id, | |
| 503 const std::string& action_id, | |
| 504 const std::vector<FileSystemURL>& file_urls, | |
| 505 const file_tasks::FileTaskFinishedCallback& done) { | |
| 506 // Forbid calling undeclared handlers. | |
| 507 if (!FindFileBrowserHandlerForActionId(extension, action_id)) | |
| 508 return false; | |
| 509 | |
| 510 // Some action IDs of the file manager's file browser handlers require the | |
| 511 // files to be directly opened with the browser. | |
| 512 if (ShouldBeOpenedWithBrowser(extension->id(), action_id)) { | |
| 513 return OpenFilesWithBrowser(profile, file_urls); | |
| 514 } | |
| 515 | |
| 516 // The executor object will be self deleted on completion. | |
| 517 (new FileBrowserHandlerExecutor( | |
| 518 profile, extension, tab_id, action_id))->Execute(file_urls, done); | |
| 519 return true; | |
| 520 } | |
| 521 | |
| 522 bool IsFallbackFileBrowserHandler(const file_tasks::TaskDescriptor& task) { | |
| 523 return (task.task_type == file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER && | |
| 524 (task.app_id == kFileManagerAppId || | |
| 525 task.app_id == extension_misc::kQuickOfficeComponentExtensionId || | |
| 526 task.app_id == extension_misc::kQuickOfficeDevExtensionId || | |
| 527 task.app_id == extension_misc::kQuickOfficeExtensionId)); | |
| 528 } | |
| 529 | |
| 530 FileBrowserHandlerList FindDefaultFileBrowserHandlers( | |
| 531 const PrefService& pref_service, | |
| 532 const std::vector<base::FilePath>& file_list, | |
| 533 const FileBrowserHandlerList& common_handlers) { | |
| 534 FileBrowserHandlerList default_handlers; | |
| 535 | |
| 536 std::set<std::string> default_ids; | |
| 537 for (std::vector<base::FilePath>::const_iterator it = file_list.begin(); | |
| 538 it != file_list.end(); ++it) { | |
| 539 std::string task_id = file_tasks::GetDefaultTaskIdFromPrefs( | |
| 540 pref_service, "", it->Extension()); | |
| 541 if (!task_id.empty()) | |
| 542 default_ids.insert(task_id); | |
| 543 } | |
| 544 | |
| 545 const FileBrowserHandler* fallback_handler = NULL; | |
| 546 // Convert the default task IDs collected above to one of the handler pointers | |
| 547 // from common_handlers. | |
| 548 for (size_t i = 0; i < common_handlers.size(); ++i) { | |
| 549 const FileBrowserHandler* handler = common_handlers[i]; | |
| 550 const file_tasks::TaskDescriptor task_descriptor( | |
| 551 handler->extension_id(), | |
| 552 file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER, | |
| 553 handler->id()); | |
| 554 const std::string task_id = | |
| 555 file_tasks::TaskDescriptorToId(task_descriptor); | |
| 556 std::set<std::string>::iterator default_iter = default_ids.find(task_id); | |
| 557 if (default_iter != default_ids.end()) { | |
| 558 default_handlers.push_back(handler); | |
| 559 continue; | |
| 560 } | |
| 561 | |
| 562 // Remember the first fallback handler. | |
| 563 if (!fallback_handler && IsFallbackFileBrowserHandler(task_descriptor)) | |
| 564 fallback_handler = handler; | |
| 565 } | |
| 566 | |
| 567 // If there are no default handlers found, use fallback as default. | |
| 568 if (fallback_handler && default_handlers.empty()) | |
| 569 default_handlers.push_back(fallback_handler); | |
| 570 | |
| 571 return default_handlers; | |
| 572 } | |
| 573 | |
| 574 FileBrowserHandlerList FindCommonFileBrowserHandlers( | |
| 575 Profile* profile, | |
| 576 const std::vector<GURL>& file_list) { | |
| 577 FileBrowserHandlerList common_handlers; | |
| 578 for (std::vector<GURL>::const_iterator it = file_list.begin(); | |
| 579 it != file_list.end(); ++it) { | |
| 580 FileBrowserHandlerList handlers = | |
| 581 FindFileBrowserHandlersForURL(profile, *it); | |
| 582 // If there is nothing to do for one file, the intersection of handlers | |
| 583 // for all files will be empty at the end, so no need to check further. | |
| 584 if (handlers.empty()) | |
| 585 return FileBrowserHandlerList(); | |
| 586 | |
| 587 // For the very first file, just copy all the elements. | |
| 588 if (it == file_list.begin()) { | |
| 589 common_handlers = handlers; | |
| 590 } else { | |
| 591 // For all additional files, find intersection between the accumulated and | |
| 592 // file specific set. | |
| 593 FileBrowserHandlerList intersection; | |
| 594 std::set_intersection(common_handlers.begin(), common_handlers.end(), | |
| 595 handlers.begin(), handlers.end(), | |
| 596 std::back_inserter(intersection)); | |
| 597 common_handlers = intersection; | |
| 598 if (common_handlers.empty()) | |
| 599 return FileBrowserHandlerList(); | |
| 600 } | |
| 601 } | |
| 602 | |
| 603 // "watch" and "gallery" are defined in the file manager's manifest.json. | |
| 604 FileBrowserHandlerList::iterator watch_iter = | |
| 605 FindFileBrowserHandlerForExtensionIdAndActionId( | |
| 606 &common_handlers, kFileManagerAppId, "watch"); | |
| 607 FileBrowserHandlerList::iterator gallery_iter = | |
| 608 FindFileBrowserHandlerForExtensionIdAndActionId( | |
| 609 &common_handlers, kFileManagerAppId, "gallery"); | |
| 610 if (watch_iter != common_handlers.end() && | |
| 611 gallery_iter != common_handlers.end()) { | |
| 612 // Both "watch" and "gallery" actions are applicable which means that the | |
| 613 // selection is all videos. Showing them both is confusing, so we only keep | |
| 614 // the one that makes more sense ("watch" for single selection, "gallery" | |
| 615 // for multiple selection). | |
| 616 if (file_list.size() == 1) | |
| 617 common_handlers.erase(gallery_iter); | |
| 618 else | |
| 619 common_handlers.erase(watch_iter); | |
| 620 } | |
| 621 | |
| 622 return common_handlers; | |
| 623 } | |
| 624 | |
| 625 const FileBrowserHandler* FindFileBrowserHandlerForURLAndPath( | |
| 626 Profile* profile, | |
| 627 const GURL& url, | |
| 628 const base::FilePath& file_path) { | |
| 629 std::vector<GURL> file_urls; | |
| 630 file_urls.push_back(url); | |
| 631 | |
| 632 FileBrowserHandlerList common_handlers = | |
| 633 FindCommonFileBrowserHandlers(profile, file_urls); | |
| 634 if (common_handlers.empty()) | |
| 635 return NULL; | |
| 636 | |
| 637 std::vector<base::FilePath> file_paths; | |
| 638 file_paths.push_back(file_path); | |
| 639 | |
| 640 FileBrowserHandlerList default_handlers = | |
| 641 FindDefaultFileBrowserHandlers(*profile->GetPrefs(), | |
| 642 file_paths, | |
| 643 common_handlers); | |
| 644 | |
| 645 // If there's none, or more than one, then we don't have a canonical default. | |
| 646 if (!default_handlers.empty()) { | |
| 647 // There should not be multiple default handlers for a single URL. | |
| 648 DCHECK_EQ(1u, default_handlers.size()); | |
| 649 | |
| 650 return *default_handlers.begin(); | |
| 651 } | |
| 652 | |
| 653 // If there are no default handlers, use first handler in the list (file | |
| 654 // manager does the same in this situation). TODO(tbarzic): This is not so | |
| 655 // optimal behaviour. | |
| 656 return *common_handlers.begin(); | |
| 657 } | |
| 658 | |
| 659 } // namespace file_browser_handlers | |
| 660 } // namespace file_manager | |
| OLD | NEW |