diff options
author | hirono@chromium.org <hirono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-04 09:40:29 +0000 |
---|---|---|
committer | hirono@chromium.org <hirono@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-06-04 09:40:29 +0000 |
commit | 3113a232cc1ca764298b3eca4e062d10982c2672 (patch) | |
tree | e2ff0c2f53e6b28e977fe74f753016fd7c22b6b9 | |
parent | c2b43dbac2d7feff547cf7e1caf0689b713d6195 (diff) | |
download | chromium_src-3113a232cc1ca764298b3eca4e062d10982c2672.zip chromium_src-3113a232cc1ca764298b3eca4e062d10982c2672.tar.gz chromium_src-3113a232cc1ca764298b3eca4e062d10982c2672.tar.bz2 |
Files.app: Let Files.app pass mutliple files to file handlers.
Previously if multiple files are selected for launching a file handler,
Files.app sends multiple launch events for each file. But according to the
specification of the launch event, we can put multiple entries in a single
launch event. This CL lets Files.app follow the specificaiton.
BUG=358694
TEST=manually
R=benwells@chromium.org, kinaba@chromium.org
Review URL: https://codereview.chromium.org/300063006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@274755 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | apps/browser/api/app_runtime/app_runtime_api.cc | 21 | ||||
-rw-r--r-- | apps/browser/api/app_runtime/app_runtime_api.h | 9 | ||||
-rw-r--r-- | apps/launcher.cc | 245 | ||||
-rw-r--r-- | apps/launcher.h | 12 | ||||
-rw-r--r-- | chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc | 84 | ||||
-rw-r--r-- | chrome/browser/chromeos/file_manager/file_tasks.cc | 7 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json | 23 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/test.js | 183 |
8 files changed, 480 insertions, 104 deletions
diff --git a/apps/browser/api/app_runtime/app_runtime_api.cc b/apps/browser/api/app_runtime/app_runtime_api.cc index 65ebf61..68be588 100644 --- a/apps/browser/api/app_runtime/app_runtime_api.cc +++ b/apps/browser/api/app_runtime/app_runtime_api.cc @@ -65,23 +65,26 @@ void AppEventRouter::DispatchOnRestartedEvent(BrowserContext* context, } // static. -void AppEventRouter::DispatchOnLaunchedEventWithFileEntry( +void AppEventRouter::DispatchOnLaunchedEventWithFileEntries( BrowserContext* context, const Extension* extension, const std::string& handler_id, - const std::string& mime_type, - const file_handler_util::GrantedFileEntry& file_entry) { + const std::vector<std::string>& mime_types, + const std::vector<file_handler_util::GrantedFileEntry>& file_entries) { // TODO(sergeygs): Use the same way of creating an event (using the generated // boilerplate) as below in DispatchOnLaunchedEventWithUrl. scoped_ptr<base::DictionaryValue> launch_data(new base::DictionaryValue); launch_data->SetString("id", handler_id); - scoped_ptr<base::DictionaryValue> launch_item(new base::DictionaryValue); - launch_item->SetString("fileSystemId", file_entry.filesystem_id); - launch_item->SetString("baseName", file_entry.registered_name); - launch_item->SetString("mimeType", mime_type); - launch_item->SetString("entryId", file_entry.id); scoped_ptr<base::ListValue> items(new base::ListValue); - items->Append(launch_item.release()); + DCHECK(file_entries.size() == mime_types.size()); + for (size_t i = 0; i < file_entries.size(); ++i) { + scoped_ptr<base::DictionaryValue> launch_item(new base::DictionaryValue); + launch_item->SetString("fileSystemId", file_entries[i].filesystem_id); + launch_item->SetString("baseName", file_entries[i].registered_name); + launch_item->SetString("mimeType", mime_types[i]); + launch_item->SetString("entryId", file_entries[i].id); + items->Append(launch_item.release()); + } launch_data->Set("items", items.release()); DispatchOnLaunchedEventImpl(extension->id(), launch_data.Pass(), context); } diff --git a/apps/browser/api/app_runtime/app_runtime_api.h b/apps/browser/api/app_runtime/app_runtime_api.h index 829becf..6853696 100644 --- a/apps/browser/api/app_runtime/app_runtime_api.h +++ b/apps/browser/api/app_runtime/app_runtime_api.h @@ -6,6 +6,7 @@ #define APPS_BROWSER_API_APP_RUNTIME_APP_RUNTIME_API_H_ #include <string> +#include <vector> class GURL; @@ -47,15 +48,15 @@ class AppEventRouter { // } // } - // The FileEntry is created from |file_system_id| and |base_name|. + // The FileEntries are created from |file_system_id| and |base_name|. // |handler_id| corresponds to the id of the file_handlers item in the // manifest that resulted in a match which triggered this launch. - static void DispatchOnLaunchedEventWithFileEntry( + static void DispatchOnLaunchedEventWithFileEntries( content::BrowserContext* context, const extensions::Extension* extension, const std::string& handler_id, - const std::string& mime_type, - const file_handler_util::GrantedFileEntry& file_entry); + const std::vector<std::string>& mime_types, + const std::vector<file_handler_util::GrantedFileEntry>& file_entries); // |handler_id| corresponds to the id of the url_handlers item // in the manifest that resulted in a match which triggered this launch. diff --git a/apps/launcher.cc b/apps/launcher.cc index f357881..8cfbee9 100644 --- a/apps/launcher.cc +++ b/apps/launcher.cc @@ -89,7 +89,7 @@ void LaunchPlatformAppWithNoData(Profile* profile, const Extension* extension) { AppEventRouter::DispatchOnLaunchedEvent(profile, extension); } -// Class to handle launching of platform apps to open a specific path. +// Class to handle launching of platform apps to open specific paths. // An instance of this class is created for each launch. The lifetime of these // instances is managed by reference counted pointers. As long as an instance // has outstanding tasks on a message queue it will be retained; once all @@ -99,23 +99,31 @@ class PlatformAppPathLauncher public: PlatformAppPathLauncher(Profile* profile, const Extension* extension, + const std::vector<base::FilePath>& file_paths) + : profile_(profile), extension_(extension), file_paths_(file_paths) {} + + PlatformAppPathLauncher(Profile* profile, + const Extension* extension, const base::FilePath& file_path) - : profile_(profile), extension_(extension), file_path_(file_path) {} + : profile_(profile), extension_(extension) { + if (!file_path.empty()) + file_paths_.push_back(file_path); + } void Launch() { DCHECK_CURRENTLY_ON(BrowserThread::UI); - if (file_path_.empty()) { + if (file_paths_.empty()) { LaunchPlatformAppWithNoData(profile_, extension_); return; } - DCHECK(file_path_.IsAbsolute()); + for (size_t i = 0; i < file_paths_.size(); ++i) { + DCHECK(file_paths_[i].IsAbsolute()); + } if (HasFileSystemWritePermission(extension_)) { - std::vector<base::FilePath> paths; - paths.push_back(file_path_); PrepareFilesForWritableApp( - paths, + file_paths_, profile_, false, base::Bind(&PlatformAppPathLauncher::OnFileValid, this), @@ -148,9 +156,17 @@ class PlatformAppPathLauncher void MakePathAbsolute(const base::FilePath& current_directory) { DCHECK_CURRENTLY_ON(BrowserThread::FILE); - if (!DoMakePathAbsolute(current_directory, &file_path_)) { - LOG(WARNING) << "Cannot make absolute path from " << file_path_.value(); - file_path_ = base::FilePath(); + for (std::vector<base::FilePath>::iterator it = file_paths_.begin(); + it != file_paths_.end(); + ++it) { + if (!DoMakePathAbsolute(current_directory, &*it)) { + LOG(WARNING) << "Cannot make absolute path from " << it->value(); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); + return; + } } BrowserThread::PostTask(BrowserThread::UI, @@ -159,93 +175,149 @@ class PlatformAppPathLauncher } void OnFileValid() { + mime_types_.resize(file_paths_.size()); #if defined(OS_CHROMEOS) - if (file_manager::util::IsUnderNonNativeLocalPath(profile_, file_path_)) { - file_manager::util::GetNonNativeLocalPathMimeType( - profile_, - file_path_, - base::Bind(&PlatformAppPathLauncher::OnGotMimeType, this)); - return; - } -#endif - + GetNextNonNativeMimeType(); +#else BrowserThread::PostTask( BrowserThread::FILE, FROM_HERE, - base::Bind(&PlatformAppPathLauncher::GetMimeTypeAndLaunch, this)); + base::Bind(&PlatformAppPathLauncher::GetMimeTypesAndLaunch, this)); +#endif } void OnFileInvalid(const base::FilePath& /* error_path */) { LaunchWithNoLaunchData(); } - void GetMimeTypeAndLaunch() { - DCHECK_CURRENTLY_ON(BrowserThread::FILE); +#if defined(OS_CHROMEOS) + void GetNextNonNativeMimeType() { + DCHECK_CURRENTLY_ON(BrowserThread::UI); - // If the file doesn't exist, or is a directory, launch with no launch data. - if (!base::PathExists(file_path_) || - base::DirectoryExists(file_path_)) { - LOG(WARNING) << "No file exists with path " << file_path_.value(); - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); - return; + bool any_native_files = false; + for (size_t i = 0; i < mime_types_.size(); ++i) { + if (!mime_types_[i].empty()) + continue; + const base::FilePath& file_path = file_paths_[i]; + if (file_manager::util::IsUnderNonNativeLocalPath(profile_, file_path)) { + file_manager::util::GetNonNativeLocalPathMimeType( + profile_, + file_path, + base::Bind(&PlatformAppPathLauncher::OnGotMimeType, this, i)); + return; + } + any_native_files = true; } - std::string mime_type; - if (!net::GetMimeTypeFromFile(file_path_, &mime_type)) { - // If MIME type of the file can't be determined by its path, - // try to sniff it by its content. - std::vector<char> content(net::kMaxBytesToSniff); - int bytes_read = base::ReadFile(file_path_, &content[0], content.size()); - if (bytes_read >= 0) { - net::SniffMimeType(&content[0], - bytes_read, - net::FilePathToFileURL(file_path_), - std::string(), // type_hint (passes no hint) - &mime_type); - } - if (mime_type.empty()) - mime_type = kFallbackMimeType; + // If there are any native files, we need to call GetMimeTypesAndLaunch to + // obtain mime types for the files. + if (any_native_files) { + BrowserThread::PostTask( + BrowserThread::FILE, + FROM_HERE, + base::Bind(&PlatformAppPathLauncher::GetMimeTypesAndLaunch, this)); + return; } - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( - &PlatformAppPathLauncher::LaunchWithMimeType, this, mime_type)); + // Otherwise, we can call LaunchWithMimeTypes directly. + LaunchWithMimeTypes(); } -#if defined(OS_CHROMEOS) - void OnGotMimeType(bool success, const std::string& mime_type) { + void OnGotMimeType(size_t index, bool success, const std::string& mime_type) { if (!success) { LaunchWithNoLaunchData(); return; } - LaunchWithMimeType(mime_type.empty() ? kFallbackMimeType : mime_type); + mime_types_[index] = mime_type.empty() ? kFallbackMimeType : mime_type; + GetNextNonNativeMimeType(); } #endif + void GetMimeTypesAndLaunch() { + DCHECK_CURRENTLY_ON(BrowserThread::FILE); + + for (size_t i = 0; i < mime_types_.size(); ++i) { + if (!this->mime_types_[i].empty()) + continue; + const base::FilePath& file_path = file_paths_[i]; + + // If the file doesn't exist, or is a directory, launch with no launch + // data. + if (!base::PathExists(file_path) || base::DirectoryExists(file_path)) { + LOG(WARNING) << "No file exists with path " << file_path.value(); + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&PlatformAppPathLauncher::LaunchWithNoLaunchData, this)); + return; + } + + std::string mime_type; + if (!net::GetMimeTypeFromFile(file_path, &mime_type)) { + // If MIME type of the file can't be determined by its path, + // try to sniff it by its content. + std::vector<char> content(net::kMaxBytesToSniff); + int bytes_read = base::ReadFile(file_path, &content[0], content.size()); + if (bytes_read >= 0) { + net::SniffMimeType(&content[0], + bytes_read, + net::FilePathToFileURL(file_path), + std::string(), // type_hint (passes no hint) + &mime_type); + } + if (mime_type.empty()) + mime_type = kFallbackMimeType; + } + mime_types_[i] = mime_type; + } + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&PlatformAppPathLauncher::LaunchWithMimeTypes, this)); + } + void LaunchWithNoLaunchData() { // This method is required as an entry point on the UI thread. LaunchPlatformAppWithNoData(profile_, extension_); } - void LaunchWithMimeType(const std::string& mime_type) { + void LaunchWithMimeTypes() { + DCHECK(file_paths_.size() == mime_types_.size()); + // Find file handler from the platform app for the file being opened. const extensions::FileHandlerInfo* handler = NULL; - if (!handler_id_.empty()) + if (!handler_id_.empty()) { handler = FileHandlerForId(*extension_, handler_id_); - else - handler = FirstFileHandlerForFile(*extension_, mime_type, file_path_); - if (handler && !FileHandlerCanHandleFile(*handler, mime_type, file_path_)) { - LOG(WARNING) << "Extension does not provide a valid file handler for " - << file_path_.value(); - LaunchWithNoLaunchData(); - return; + if (handler) { + for (size_t i = 0; i < file_paths_.size(); ++i) { + if (!FileHandlerCanHandleFile( + *handler, mime_types_[i], file_paths_[i])) { + LOG(WARNING) + << "Extension does not provide a valid file handler for " + << file_paths_[i].value(); + handler = NULL; + break; + } + } + } + } else { + std::set<std::pair<base::FilePath, std::string> > path_and_file_type_set; + for (size_t i = 0; i < file_paths_.size(); ++i) { + path_and_file_type_set.insert( + std::make_pair(file_paths_[i], mime_types_[i])); + } + const std::vector<const extensions::FileHandlerInfo*>& handlers = + extensions::app_file_handler_util::FindFileHandlersForFiles( + *extension_, path_and_file_type_set); + if (!handlers.empty()) + handler = handlers[0]; } // If this app doesn't have a file handler that supports the file, launch // with no launch data. if (!handler) { - LOG(WARNING) << "Extension does not provide a valid file handler for " - << file_path_.value(); + LOG(WARNING) << "Extension does not provide a valid file handler."; LaunchWithNoLaunchData(); return; } @@ -258,39 +330,44 @@ class PlatformAppPathLauncher // available, or it might be in the process of being unloaded, in which case // the lazy background task queue is used to load the extension and then // call back to us. - extensions::LazyBackgroundTaskQueue* queue = + extensions::LazyBackgroundTaskQueue* const queue = ExtensionSystem::Get(profile_)->lazy_background_task_queue(); if (queue->ShouldEnqueueTask(profile_, extension_)) { - queue->AddPendingTask(profile_, extension_->id(), base::Bind( - &PlatformAppPathLauncher::GrantAccessToFileAndLaunch, - this, mime_type)); + queue->AddPendingTask( + profile_, + extension_->id(), + base::Bind(&PlatformAppPathLauncher::GrantAccessToFilesAndLaunch, + this)); return; } - extensions::ProcessManager* process_manager = + extensions::ProcessManager* const process_manager = ExtensionSystem::Get(profile_)->process_manager(); - ExtensionHost* host = + ExtensionHost* const host = process_manager->GetBackgroundHostForExtension(extension_->id()); DCHECK(host); - GrantAccessToFileAndLaunch(mime_type, host); + GrantAccessToFilesAndLaunch(host); } - void GrantAccessToFileAndLaunch(const std::string& mime_type, - ExtensionHost* host) { + void GrantAccessToFilesAndLaunch(ExtensionHost* host) { // If there was an error loading the app page, |host| will be NULL. if (!host) { LOG(ERROR) << "Could not load app page for " << extension_->id(); return; } - GrantedFileEntry file_entry = - CreateFileEntry(profile_, - extension_, - host->render_process_host()->GetID(), - file_path_, - false); - AppEventRouter::DispatchOnLaunchedEventWithFileEntry( - profile_, extension_, handler_id_, mime_type, file_entry); + std::vector<GrantedFileEntry> file_entries; + for (size_t i = 0; i < file_paths_.size(); ++i) { + file_entries.push_back( + CreateFileEntry(profile_, + extension_, + host->render_process_host()->GetID(), + file_paths_[i], + false)); + } + + AppEventRouter::DispatchOnLaunchedEventWithFileEntries( + profile_, extension_, handler_id_, mime_types_, file_entries); } // The profile the app should be run in. @@ -301,7 +378,8 @@ class PlatformAppPathLauncher // See http://crbug.com/372270 for details. const Extension* extension_; // The path to be passed through to the app. - base::FilePath file_path_; + std::vector<base::FilePath> file_paths_; + std::vector<std::string> mime_types_; // The ID of the file handler used to launch the app. std::string handler_id_; @@ -369,12 +447,13 @@ void LaunchPlatformApp(Profile* profile, const Extension* extension) { base::FilePath()); } -void LaunchPlatformAppWithFileHandler(Profile* profile, - const Extension* extension, - const std::string& handler_id, - const base::FilePath& file_path) { +void LaunchPlatformAppWithFileHandler( + Profile* profile, + const Extension* extension, + const std::string& handler_id, + const std::vector<base::FilePath>& file_paths) { scoped_refptr<PlatformAppPathLauncher> launcher = - new PlatformAppPathLauncher(profile, extension, file_path); + new PlatformAppPathLauncher(profile, extension, file_paths); launcher->LaunchWithHandler(handler_id); } diff --git a/apps/launcher.h b/apps/launcher.h index d41f517..eb2d98f 100644 --- a/apps/launcher.h +++ b/apps/launcher.h @@ -6,6 +6,7 @@ #define APPS_LAUNCHER_H_ #include <string> +#include <vector> class GURL; class Profile; @@ -41,13 +42,14 @@ void LaunchPlatformApp(Profile* profile, const extensions::Extension* extension); // Launches the platform app |extension| with |handler_id| and the contents of -// |file_path| available through the launch data. |handler_id| corresponds to +// |file_paths| available through the launch data. |handler_id| corresponds to // the id of the file_handlers item in the manifest that resulted in a match // that triggered this launch. -void LaunchPlatformAppWithFileHandler(Profile* profile, - const extensions::Extension* extension, - const std::string& handler_id, - const base::FilePath& file_path); +void LaunchPlatformAppWithFileHandler( + Profile* profile, + const extensions::Extension* extension, + const std::string& handler_id, + const std::vector<base::FilePath>& file_paths); // Launches the platform app |extension| with |handler_id|, |url| and // |referrer_url| available through the launch data. |handler_id| corresponds to diff --git a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc index c4fd4f6..70e67f2a 100644 --- a/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc +++ b/chrome/browser/chromeos/file_manager/external_filesystem_apitest.cc @@ -599,6 +599,78 @@ class MultiProfileDriveFileSystemExtensionApiTest : std::map<std::string, std::string> resource_ids_; }; +class LocalAndDriveFileSystemExtensionApiTest + : public FileSystemExtensionApiTestBase { + public: + LocalAndDriveFileSystemExtensionApiTest() {} + virtual ~LocalAndDriveFileSystemExtensionApiTest() {} + + // FileSystemExtensionApiTestBase OVERRIDE. + virtual void InitTestFileSystem() OVERRIDE { + ASSERT_TRUE(InitializeLocalFileSystem( + kLocalMountPointName, &local_tmp_dir_, &local_mount_point_dir_)) + << "Failed to initialize file system."; + + // Set up cache root to be used by DriveIntegrationService. This has to be + // done before the browser is created because the service instance is + // initialized by EventRouter. + ASSERT_TRUE(test_cache_root_.CreateUniqueTempDir()); + + // This callback will get called during Profile creation. + create_drive_integration_service_ = base::Bind( + &LocalAndDriveFileSystemExtensionApiTest::CreateDriveIntegrationService, + base::Unretained(this)); + service_factory_for_test_.reset( + new DriveIntegrationServiceFactory::ScopedFactoryForTest( + &create_drive_integration_service_)); + } + + // FileSystemExtensionApiTestBase OVERRIDE. + virtual void AddTestMountPoint() OVERRIDE { + EXPECT_TRUE(content::BrowserContext::GetMountPoints(browser()->profile()) + ->RegisterFileSystem(kLocalMountPointName, + fileapi::kFileSystemTypeNativeLocal, + fileapi::FileSystemMountOption(), + local_mount_point_dir_)); + VolumeManager::Get(browser()->profile()) + ->AddVolumeInfoForTesting(local_mount_point_dir_, + VOLUME_TYPE_TESTING, + chromeos::DEVICE_TYPE_UNKNOWN); + test_util::WaitUntilDriveMountPointIsAdded(browser()->profile()); + } + + protected: + // DriveIntegrationService factory function for this test. + drive::DriveIntegrationService* CreateDriveIntegrationService( + Profile* profile) { + fake_drive_service_ = new drive::FakeDriveService; + fake_drive_service_->LoadAppListForDriveApi("drive/applist.json"); + + std::map<std::string, std::string> resource_ids; + EXPECT_TRUE(InitializeDriveService(fake_drive_service_, &resource_ids)); + + return new drive::DriveIntegrationService(profile, + NULL, + fake_drive_service_, + "drive", + test_cache_root_.path(), + NULL); + } + + private: + // For local volume. + base::ScopedTempDir local_tmp_dir_; + base::FilePath local_mount_point_dir_; + + // For drive volume. + base::ScopedTempDir test_cache_root_; + drive::FakeDriveService* fake_drive_service_; + DriveIntegrationServiceFactory::FactoryCallback + create_drive_integration_service_; + scoped_ptr<DriveIntegrationServiceFactory::ScopedFactoryForTest> + service_factory_for_test_; +}; + // // LocalFileSystemExtensionApiTests. // @@ -712,5 +784,17 @@ IN_PROC_BROWSER_TEST_F(MultiProfileDriveFileSystemExtensionApiTest, FLAGS_NONE)) << message_; } +// +// LocalAndDriveFileSystemExtensionApiTests. +// +IN_PROC_BROWSER_TEST_F(LocalAndDriveFileSystemExtensionApiTest, + AppFileHandlerMulti) { + EXPECT_TRUE( + RunFileSystemExtensionApiTest("file_browser/app_file_handler_multi", + FILE_PATH_LITERAL("manifest.json"), + "", + FLAGS_NONE)) + << message_; +} } // namespace } // namespace file_manager diff --git a/chrome/browser/chromeos/file_manager/file_tasks.cc b/chrome/browser/chromeos/file_manager/file_tasks.cc index 2cb78d3..c74cd2c 100644 --- a/chrome/browser/chromeos/file_manager/file_tasks.cc +++ b/chrome/browser/chromeos/file_manager/file_tasks.cc @@ -311,11 +311,12 @@ bool ExecuteFileTask(Profile* profile, file_urls, done); } else if (task.task_type == TASK_TYPE_FILE_HANDLER) { + std::vector<base::FilePath> paths; for (size_t i = 0; i != file_urls.size(); ++i) { - apps::LaunchPlatformAppWithFileHandler( - profile, extension, task.action_id, file_urls[i].path()); + paths.push_back(file_urls[i].path()); } - + apps::LaunchPlatformAppWithFileHandler( + profile, extension, task.action_id, paths); if (!done.is_null()) done.Run(extensions::api::file_browser_private::TASK_RESULT_MESSAGE_SENT); return true; diff --git a/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json b/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json new file mode 100644 index 0000000..01c3d35 --- /dev/null +++ b/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/manifest.json @@ -0,0 +1,23 @@ +{ + "key": "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQChptAQ0n4R56N03nWQ1ogR7DVRBjGo80Vw6G9KLjzZv44D8rq5Q5IkeQrtKgWyZfXevlsCe3LaLo18rcz8iZx6lK2xhLdUR+ORjsjuBfdEL5a5cWeRTSxf75AcqndQsmpwMBdrMTCZ8jQNusUI+XlrihLNNJuI5TM4vNINI5bYFQIBIw==", + "name": "ChromeOS File handler extension", + "version": "0.1", + "manifest_version": 2, + "description": "Tests of the file handler for multiple items", + "app": { + "background": { + "scripts": ["test.js"] + } + }, + "file_handlers": { + "textAction": { + "extensions": ["txt"], + "title": "foo action handler" + } + }, + "permissions": [ + "fileBrowserPrivate", + "fileBrowserHandler", + "unlimitedStorage" + ] +} diff --git a/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/test.js b/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/test.js new file mode 100644 index 0000000..5af5d73 --- /dev/null +++ b/chrome/test/data/extensions/api_test/file_browser/app_file_handler_multi/test.js @@ -0,0 +1,183 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +'use strict'; + +/** + * Promise of the volume list. + * @type {Promise} + */ +var volumeListPromise = new Promise(function(fulfill, reject) { + chrome.fileBrowserPrivate.getVolumeMetadataList(fulfill); +}); + +/** + * Obtains file system of the volume type. + * @param {string} volumeType VolumeType. + * @return {Promise} Promise to be fulfilled with a file system. + */ +function getFileSystem(volumeType) { + return volumeListPromise.then(function(list) { + for (var i = 0; i < list.length; i++) { + if (list[i].volumeType == volumeType) { + return new Promise(function(fulfill) { + chrome.fileBrowserPrivate.requestFileSystem( + list[i].volumeId, fulfill); + }); + } + } + throw new Error('The volume is not found: ' + volumeType + '.'); + }); +} + +/** + * Prepares a file on the file system. + * @param {FileSystem} filesystem File system. + * @param {string} name Name of the file. + * @param {Blob} contents Contents of the file. + * @return {Promise} Promise to be fulfilled with FileEntry of the new file. + */ +function prepareFile(filesystem, name, contents) { + return new Promise(function(fulfill, reject) { + filesystem.root.getFile(name, {create: true}, function(file) { + file.createWriter(function(writer) { + writer.write(contents); + writer.onwrite = fulfill.bind(null, file); + writer.onerror = reject; + }); + fulfill(file); + }, reject); + }); +} + +/** + * Prepares two test files on the file system. + * @param {FileSystem} filesystem File system. + * @return {Promise} Promise to be fullfilled with an object {filesystem: + * FileSystem, entries: Array.<FileEntry>} that contains the passed file + * system and the created entries. + */ +function prepareFiles(filesystem) { + var testFileA = + prepareFile(filesystem, 'test_file_a.txt', TEST_FILE_CONTENTS); + var testFileB = + prepareFile(filesystem, 'test_file_b.txt', TEST_FILE_CONTENTS); + return Promise.all([testFileA, testFileB]).then(function(entries) { + return {filesystem: filesystem, entries: entries}; + }); +} + +/** + * Contents of the test file. + * @type {Blob} + * @const + */ +var TEST_FILE_CONTENTS = new Blob(['This is a test file.']); + +/** + * File system of the drive volume. + * @type {Promise} + */ +var driveFileSystemPromise = getFileSystem('drive').then(prepareFiles); + +/** + * File system of the local volume. + * @type {Promise} + */ +var localFileSystemPromise = getFileSystem('testing').then(prepareFiles); + +/** + * Calls test functions depends on the result of the promise. + * @param {Promise} promise Promise to be fulfilled or to be rejected depends on + * the test results. + */ +function testPromise(promise) { + promise.then( + chrome.test.callbackPass(), + function(error) { + chrome.test.fail(error.stack || error); + }); +} + +/** + * Calls the executeTask API with the entries and checks the launch data passed + * to onLaunched events. + * @param {entries} entries Entries to be tested. + * @return {Promise} Promise to be fulfilled on success. + */ +function launchWithEntries(entries) { + var urls = entries.map(function(entry) { return entry.toURL(); }); + var mimeTypes = urls.map(function() { return ''; }); + var tasksPromise = new Promise(function(fulfill) { + chrome.fileBrowserPrivate.getFileTasks(urls, mimeTypes, fulfill); + }).then(function(tasks) { + chrome.test.assertEq(1, tasks.length); + chrome.test.assertEq('kidcpjlbjdmcnmccjhjdckhbngnhnepk|app|textAction', + tasks[0].taskId); + return tasks[0]; + }); + var launchDataPromise = new Promise(function(fulfill) { + chrome.app.runtime.onLaunched.addListener(function handler(launchData) { + chrome.app.runtime.onLaunched.removeListener(handler); + fulfill(launchData); + }); + }); + var taskExecutedPromise = tasksPromise.then(function(task) { + return new Promise(function(fulfill, reject) { + chrome.fileBrowserPrivate.executeTask( + task.taskId, + urls, + function(result) { + if (result) + fulfill(); + else + reject(); + }); + }); + }); + return Promise.all([taskExecutedPromise, launchDataPromise]).then( + function(args) { + chrome.test.assertEq(entries.length, args[1].items.length); + chrome.test.assertEq( + entries.map(function(entry) { return entry.name; }), + args[1].items.map(function(item) { return item.entry.name; })); + }); +} + +/** + * Tests the file handler feature with entries on the local volume. + */ +function testForLocalFiles() { + testPromise(localFileSystemPromise.then(function(volume) { + return launchWithEntries(volume.entries); + })); +} + +/** + * Tests the file handler feature with entries on the drive volume. + */ +function testForDriveFiles() { + testPromise(driveFileSystemPromise.then(function(volume) { + return launchWithEntries(volume.entries); + })); +} + +/** + * Tests the file handler with entries both on the local and on the drive + * volumes. + */ +function testForMixedFiles() { + testPromise( + Promise.all([localFileSystemPromise, driveFileSystemPromise]).then( + function(args) { + return launchWithEntries(args[0].entries.concat(args[1].entries)); + })); +} + +// Run the tests. +chrome.test.runTests([ + testForLocalFiles, + testForDriveFiles, + testForMixedFiles +]); |