| Index: apps/launcher.cc
|
| diff --git a/apps/launcher.cc b/apps/launcher.cc
|
| index f357881ce96de18b4c9ab8848b9391e552245a7e..8cfbee970f12d58623f529a2b85c4b2d02c0be31 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);
|
| }
|
|
|
|
|