diff options
author | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-07 14:09:48 +0000 |
---|---|---|
committer | benwells@chromium.org <benwells@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-07 14:09:48 +0000 |
commit | 24c81d69598fce9558b6a1ae2cd802ad6f7e371a (patch) | |
tree | fa8e85f88af52a3f26ed1e4a5f5bd303ed12c142 /apps | |
parent | dab407b5264bee7800f9c6f4d0c8cc2493797d91 (diff) | |
download | chromium_src-24c81d69598fce9558b6a1ae2cd802ad6f7e371a.zip chromium_src-24c81d69598fce9558b6a1ae2cd802ad6f7e371a.tar.gz chromium_src-24c81d69598fce9558b6a1ae2cd802ad6f7e371a.tar.bz2 |
Move platform_app_launcher to apps
chrome/browser/extensions/platform_app_launcher.* -> apps/launcher.*
TBR=yoz@chromium.org, willchan@chromium.ogt, satorux@chromium.org
TBR for simple refactors:
yoz for extensions
willchan for new dependency on net/base
satorux for new dependency on chrome/browser/chromeos/drive
BUG=159366
TEST=Ensure launching apps in Windows 8 while chrome is running in
single window mode still prompts to run chrome in desktop mode.
Review URL: https://chromiumcodereview.appspot.com/22536002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@216181 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'apps')
-rw-r--r-- | apps/DEPS | 2 | ||||
-rw-r--r-- | apps/app_launch_for_metro_restart_win.cc | 2 | ||||
-rw-r--r-- | apps/app_load_service.cc | 8 | ||||
-rw-r--r-- | apps/app_restore_service.cc | 4 | ||||
-rw-r--r-- | apps/apps.gypi | 9 | ||||
-rw-r--r-- | apps/launcher.cc | 352 | ||||
-rw-r--r-- | apps/launcher.h | 54 |
7 files changed, 424 insertions, 7 deletions
@@ -5,6 +5,7 @@ include_rules = [ "+components/user_prefs/pref_registry_syncable.h", "+components/web_modal", "+extensions", + "+net/base", "+skia/ext", "+third_party/skia/include", "+ui", @@ -14,6 +15,7 @@ include_rules = [ "+chrome/browser/browser_process.h", "+chrome/browser/browser_shutdown.h", "+chrome/browser/chrome_notification_types.h", + "+chrome/browser/chromeos/drive", "+chrome/browser/extensions", "+chrome/browser/lifetime/application_lifetime.h", "+chrome/browser/profiles", diff --git a/apps/app_launch_for_metro_restart_win.cc b/apps/app_launch_for_metro_restart_win.cc index 9d6cb22..2b10274 100644 --- a/apps/app_launch_for_metro_restart_win.cc +++ b/apps/app_launch_for_metro_restart_win.cc @@ -4,6 +4,7 @@ #include "apps/app_launch_for_metro_restart_win.h" +#include "apps/launcher.h" #include "apps/pref_names.h" #include "base/bind.h" #include "base/files/file_path.h" @@ -14,7 +15,6 @@ #include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/platform_app_launcher.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" #include "win8/util/win8_util.h" diff --git a/apps/app_load_service.cc b/apps/app_load_service.cc index 982822e..d2423c0 100644 --- a/apps/app_load_service.cc +++ b/apps/app_load_service.cc @@ -5,12 +5,12 @@ #include "apps/app_load_service.h" #include "apps/app_load_service_factory.h" +#include "apps/launcher.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/platform_app_launcher.h" #include "chrome/browser/extensions/shell_window_registry.h" #include "chrome/browser/extensions/unpacked_installer.h" #include "chrome/browser/profiles/profile.h" @@ -89,13 +89,13 @@ void AppLoadService::Observe(int type, switch (it->second.action_type) { case LAUNCH: - extensions::LaunchPlatformApp(profile_, extension); + LaunchPlatformApp(profile_, extension); break; case RESTART: - extensions::RestartPlatformApp(profile_, extension); + RestartPlatformApp(profile_, extension); break; case LAUNCH_WITH_COMMAND_LINE: - extensions::LaunchPlatformAppWithCommandLine( + LaunchPlatformAppWithCommandLine( profile_, extension, &it->second.command_line, it->second.current_dir); break; diff --git a/apps/app_restore_service.cc b/apps/app_restore_service.cc index 2ed8c47..e2d5a85 100644 --- a/apps/app_restore_service.cc +++ b/apps/app_restore_service.cc @@ -6,6 +6,7 @@ #include "apps/app_lifetime_monitor_factory.h" #include "apps/app_restore_service_factory.h" +#include "apps/launcher.h" #include "apps/saved_files_service.h" #include "apps/shell_window.h" #include "chrome/browser/chrome_notification_types.h" @@ -14,7 +15,6 @@ #include "chrome/browser/extensions/extension_prefs.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_system.h" -#include "chrome/browser/extensions/platform_app_launcher.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_set.h" @@ -138,7 +138,7 @@ void AppRestoreService::RecordAppActiveState(const std::string& id, } void AppRestoreService::RestoreApp(const Extension* extension) { - extensions::RestartPlatformApp(profile_, extension); + RestartPlatformApp(profile_, extension); } void AppRestoreService::StartObservingAppLifetime() { diff --git a/apps/apps.gypi b/apps/apps.gypi index 2a1bc99..59f4c75 100644 --- a/apps/apps.gypi +++ b/apps/apps.gypi @@ -54,6 +54,8 @@ 'app_window_contents.h', 'field_trial_names.cc', 'field_trial_names.h', + 'launcher.cc', + 'launcher.h', 'metrics_names.h', 'native_app_window.h', 'pref_names.cc', @@ -72,6 +74,13 @@ 'switches.h', ], 'conditions': [ + ['chromeos==1', + { + 'dependencies': [ + 'browser_chromeos', + ] + } + ], ['enable_extensions==0', { 'sources/': [ diff --git a/apps/launcher.cc b/apps/launcher.cc new file mode 100644 index 0000000..d892da6 --- /dev/null +++ b/apps/launcher.cc @@ -0,0 +1,352 @@ +// Copyright 2013 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. + +#include "apps/launcher.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/extensions/api/app_runtime/app_runtime_api.h" +#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h" +#include "chrome/browser/extensions/api/file_system/file_system_api.h" +#include "chrome/browser/extensions/event_names.h" +#include "chrome/browser/extensions/event_router.h" +#include "chrome/browser/extensions/extension_host.h" +#include "chrome/browser/extensions/extension_prefs.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/extensions/extension_system.h" +#include "chrome/browser/extensions/lazy_background_task_queue.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/extension_messages.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/web_contents.h" +#include "net/base/mime_util.h" +#include "net/base/net_util.h" + +#if defined(OS_CHROMEOS) +#include "chrome/browser/chromeos/drive/drive_integration_service.h" +#include "chrome/browser/chromeos/drive/file_errors.h" +#include "chrome/browser/chromeos/drive/file_system_interface.h" +#include "chrome/browser/chromeos/drive/file_system_util.h" +#endif + +#if defined(OS_WIN) +#include "win8/util/win8_util.h" +#endif + +using content::BrowserThread; +using extensions::app_file_handler_util::FileHandlerForId; +using extensions::app_file_handler_util::FileHandlerCanHandleFile; +using extensions::app_file_handler_util::FirstFileHandlerForFile; +using extensions::app_file_handler_util::CreateFileEntry; +using extensions::app_file_handler_util::GrantedFileEntry; +using extensions::Extension; +using extensions::ExtensionHost; +using extensions::ExtensionSystem; + +namespace apps { + +namespace { + +const char kFallbackMimeType[] = "application/octet-stream"; + +bool MakePathAbsolute(const base::FilePath& current_directory, + base::FilePath* file_path) { + DCHECK(file_path); + if (file_path->IsAbsolute()) + return true; + + if (current_directory.empty()) { + *file_path = base::MakeAbsoluteFilePath(*file_path); + return !file_path->empty(); + } + + if (!current_directory.IsAbsolute()) + return false; + + *file_path = current_directory.Append(*file_path); + return true; +} + +bool GetAbsolutePathFromCommandLine(const CommandLine* command_line, + const base::FilePath& current_directory, + base::FilePath* path) { + if (!command_line || !command_line->GetArgs().size()) + return false; + + base::FilePath relative_path(command_line->GetArgs()[0]); + base::FilePath absolute_path(relative_path); + if (!MakePathAbsolute(current_directory, &absolute_path)) { + LOG(WARNING) << "Cannot make absolute path from " << relative_path.value(); + return false; + } + *path = absolute_path; + return true; +} + +// Helper method to launch the platform app |extension| with no data. This +// should be called in the fallback case, where it has been impossible to +// load or obtain file launch data. +void LaunchPlatformAppWithNoData(Profile* profile, const Extension* extension) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + extensions::AppEventRouter::DispatchOnLaunchedEvent(profile, extension); +} + +// Class to handle launching of platform apps to open a specific path. +// 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 +// outstanding tasks are completed it will be deleted. +class PlatformAppPathLauncher + : public base::RefCountedThreadSafe<PlatformAppPathLauncher> { + public: + PlatformAppPathLauncher(Profile* profile, + const Extension* extension, + const base::FilePath& file_path) + : profile_(profile), extension_(extension), file_path_(file_path) {} + + void Launch() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + if (file_path_.empty()) { + LaunchPlatformAppWithNoData(profile_, extension_); + return; + } + + DCHECK(file_path_.IsAbsolute()); + +#if defined(OS_CHROMEOS) + if (drive::util::IsUnderDriveMountPoint(file_path_)) { + GetMimeTypeAndLaunchForDriveFile(); + return; + } +#endif + + BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, base::Bind( + &PlatformAppPathLauncher::GetMimeTypeAndLaunch, this)); + } + + void LaunchWithHandler(const std::string& handler_id) { + handler_id_ = handler_id; + Launch(); + } + + private: + friend class base::RefCountedThreadSafe<PlatformAppPathLauncher>; + + virtual ~PlatformAppPathLauncher() {} + + void GetMimeTypeAndLaunch() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + + // 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)) + mime_type = kFallbackMimeType; + + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( + &PlatformAppPathLauncher::LaunchWithMimeType, this, mime_type)); + } + +#if defined(OS_CHROMEOS) + void GetMimeTypeAndLaunchForDriveFile() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + drive::DriveIntegrationService* service = + drive::DriveIntegrationServiceFactory::FindForProfile(profile_); + if (!service) { + LaunchWithNoLaunchData(); + return; + } + + service->file_system()->GetFileByPath( + drive::util::ExtractDrivePath(file_path_), + base::Bind(&PlatformAppPathLauncher::OnGotDriveFile, this)); + } + + void OnGotDriveFile(drive::FileError error, + const base::FilePath& file_path, + scoped_ptr<drive::ResourceEntry> entry) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + if (error != drive::FILE_ERROR_OK || + !entry || entry->file_specific_info().is_hosted_document()) { + LaunchWithNoLaunchData(); + return; + } + + const std::string& mime_type = + entry->file_specific_info().content_mime_type(); + LaunchWithMimeType(mime_type.empty() ? kFallbackMimeType : mime_type); + } +#endif // defined(OS_CHROMEOS) + + void LaunchWithNoLaunchData() { + // This method is required as an entry point on the UI thread. + LaunchPlatformAppWithNoData(profile_, extension_); + } + + void LaunchWithMimeType(const std::string& mime_type) { + // Find file handler from the platform app for the file being opened. + const extensions::FileHandlerInfo* handler = NULL; + 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 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(); + LaunchWithNoLaunchData(); + return; + } + + if (handler_id_.empty()) + handler_id_ = handler->id; + + // Access needs to be granted to the file for the process associated with + // the extension. To do this the ExtensionHost is needed. This might not be + // 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 = + ExtensionSystem::Get(profile_)->lazy_background_task_queue(); + if (queue->ShouldEnqueueTask(profile_, extension_)) { + queue->AddPendingTask(profile_, extension_->id(), base::Bind( + &PlatformAppPathLauncher::GrantAccessToFileAndLaunch, + this, mime_type)); + return; + } + + ExtensionProcessManager* process_manager = + ExtensionSystem::Get(profile_)->process_manager(); + ExtensionHost* host = + process_manager->GetBackgroundHostForExtension(extension_->id()); + DCHECK(host); + GrantAccessToFileAndLaunch(mime_type, host); + } + + void GrantAccessToFileAndLaunch(const std::string& mime_type, + 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_->id(), + host->render_process_host()->GetID(), + file_path_, + false); + extensions::AppEventRouter::DispatchOnLaunchedEventWithFileEntry( + profile_, extension_, handler_id_, mime_type, file_entry); + } + + // The profile the app should be run in. + Profile* profile_; + // The extension providing the app. + const Extension* extension_; + // The path to be passed through to the app. + const base::FilePath file_path_; + // The ID of the file handler used to launch the app. + std::string handler_id_; + + DISALLOW_COPY_AND_ASSIGN(PlatformAppPathLauncher); +}; + +} // namespace + +void LaunchPlatformAppWithCommandLine(Profile* profile, + const Extension* extension, + const CommandLine* command_line, + const base::FilePath& current_directory) { + base::FilePath path; + if (!GetAbsolutePathFromCommandLine(command_line, current_directory, &path)) { + LaunchPlatformAppWithNoData(profile, extension); + return; + } + + // TODO(benwells): add a command-line argument to provide a handler ID. + LaunchPlatformAppWithPath(profile, extension, path); +} + +void LaunchPlatformAppWithPath(Profile* profile, + const Extension* extension, + const base::FilePath& file_path) { + // launcher will be freed when nothing has a reference to it. The message + // queue will retain a reference for any outstanding task, so when the + // launcher has finished it will be freed. + scoped_refptr<PlatformAppPathLauncher> launcher = + new PlatformAppPathLauncher(profile, extension, file_path); + launcher->Launch(); +} + +void LaunchPlatformApp(Profile* profile, const Extension* extension) { + LaunchPlatformAppWithCommandLine(profile, extension, NULL, base::FilePath()); +} + +void LaunchPlatformAppWithFileHandler(Profile* profile, + const Extension* extension, + const std::string& handler_id, + const base::FilePath& file_path) { + scoped_refptr<PlatformAppPathLauncher> launcher = + new PlatformAppPathLauncher(profile, extension, file_path); + launcher->LaunchWithHandler(handler_id); +} + +void RestartPlatformApp(Profile* profile, const Extension* extension) { +#if defined(OS_WIN) + // On Windows 8's single window Metro mode we can not launch platform apps. + // In restart we are just making sure launch doesn't slip through. + if (win8::IsSingleWindowMetroMode()) + return; +#endif + extensions::EventRouter* event_router = + ExtensionSystem::Get(profile)->event_router(); + bool listening_to_restart = event_router-> + ExtensionHasEventListener(extension->id(), + extensions::event_names::kOnRestarted); + + if (listening_to_restart) { + extensions::AppEventRouter::DispatchOnRestartedEvent(profile, extension); + return; + } + + extensions::ExtensionPrefs* extension_prefs = ExtensionSystem::Get(profile)-> + extension_service()->extension_prefs(); + bool had_windows = extension_prefs->IsActive(extension->id()); + extension_prefs->SetIsActive(extension->id(), false); + bool listening_to_launch = event_router-> + ExtensionHasEventListener(extension->id(), + extensions::event_names::kOnLaunched); + + if (listening_to_launch && had_windows) + LaunchPlatformAppWithNoData(profile, extension); +} + +} // namespace apps diff --git a/apps/launcher.h b/apps/launcher.h new file mode 100644 index 0000000..9412c11 --- /dev/null +++ b/apps/launcher.h @@ -0,0 +1,54 @@ +// Copyright 2013 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. + +#ifndef APPS_LAUNCHER_H_ +#define APPS_LAUNCHER_H_ + +#include <string> + +class CommandLine; +class Profile; + +namespace base { +class FilePath; +} + +namespace extensions { +class Extension; +} + +namespace apps { + +// Launches the platform app |extension|. Creates appropriate launch data for +// the |command_line| fields present. |extension| and |profile| must not be +// NULL. A NULL |command_line| means there is no launch data. If non-empty, +// |current_directory| is used to expand any relative paths on the command line. +void LaunchPlatformAppWithCommandLine(Profile* profile, + const extensions::Extension* extension, + const CommandLine* command_line, + const base::FilePath& current_directory); + +// Launches the platform app |extension| with the contents of |file_path| +// available through the launch data. +void LaunchPlatformAppWithPath(Profile* profile, + const extensions::Extension* extension, + const base::FilePath& file_path); + +// Launches the platform app |extension| with no launch data. +void LaunchPlatformApp(Profile* profile, + const extensions::Extension* extension); + +// Launches the platform app |extension| with the contents of |file_path| +// available through the launch data. +void LaunchPlatformAppWithFileHandler(Profile* profile, + const extensions::Extension* extension, + const std::string& handler_id, + const base::FilePath& file_path); + +void RestartPlatformApp(Profile* profile, + const extensions::Extension* extension); + +} // namespace apps + +#endif // APPS_LAUNCHER_H_ |