Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(133)

Unified Diff: chrome/browser/apps/ephemeral_app_launcher.cc

Issue 339103002: Update the EphemeralAppLauncher for use by the webstorePrivate API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase again Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/apps/ephemeral_app_launcher.cc
diff --git a/chrome/browser/apps/ephemeral_app_launcher.cc b/chrome/browser/apps/ephemeral_app_launcher.cc
index 90c4482e2f807e7a18095efd18d11a5a7d9f445e..8fe15fb8e416d8b7f9543f3d6ff54151ae57aef7 100644
--- a/chrome/browser/apps/ephemeral_app_launcher.cc
+++ b/chrome/browser/apps/ephemeral_app_launcher.cc
@@ -4,25 +4,42 @@
#include "chrome/browser/apps/ephemeral_app_launcher.h"
+#include "base/command_line.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/extensions/extension_install_checker.h"
#include "chrome/browser/extensions/extension_install_prompt.h"
#include "chrome/browser/extensions/extension_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/extensions/extension_enable_flow.h"
+#include "chrome/common/chrome_switches.h"
#include "content/public/browser/web_contents.h"
+#include "extensions/browser/extension_prefs.h"
#include "extensions/browser/extension_registry.h"
+#include "extensions/browser/extension_system.h"
+#include "extensions/browser/management_policy.h"
#include "extensions/common/permissions/permissions_data.h"
using content::WebContents;
using extensions::Extension;
+using extensions::ExtensionInstallChecker;
+using extensions::ExtensionPrefs;
using extensions::ExtensionRegistry;
+using extensions::ExtensionSystem;
+using extensions::ManagementPolicy;
using extensions::WebstoreInstaller;
+namespace webstore_install = extensions::webstore_install;
namespace {
const char kInvalidManifestError[] = "Invalid manifest";
-const char kExtensionTypeError[] = "Ephemeral extensions are not permitted";
-const char kLaunchAbortedError[] = "Launch aborted";
+const char kExtensionTypeError[] = "Cannot launch an extension";
+const char kUserCancelledError[] = "Launch cancelled by the user";
+const char kBlacklistedError[] = "App is blacklisted for malware";
+const char kRequirementsError[] = "App has missing requirements";
+const char kFeatureDisabledError[] = "Launching ephemeral apps is not enabled";
+const char kMissingAppError[] = "App is not installed";
+const char kAppDisabledError[] = "App is disabled";
Profile* ProfileForWebContents(content::WebContents* contents) {
if (!contents)
@@ -38,15 +55,46 @@ gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
return contents->GetTopLevelNativeWindow();
}
+// Check whether an extension can be launched. The extension does not need to
+// be currently installed.
+bool CheckCommonLaunchCriteria(Profile* profile,
+ const extensions::Extension* extension,
+ webstore_install::Result* reason,
+ std::string* error) {
+ // Only apps can be launched.
+ if (!extension->is_app()) {
+ *reason = webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE;
+ *error = kExtensionTypeError;
+ return false;
+ }
+
+ // Do not launch apps blocked by management policies.
+ ManagementPolicy* management_policy =
+ ExtensionSystem::Get(profile)->management_policy();
+ base::string16 policy_error;
+ if (!management_policy->UserMayLoad(extension, &policy_error)) {
+ *reason = webstore_install::BLOCKED_BY_POLICY;
+ *error = base::UTF16ToUTF8(policy_error);
+ return false;
+ }
+
+ return true;
+}
+
} // namespace
// static
-scoped_refptr<EphemeralAppLauncher>
-EphemeralAppLauncher::CreateForLauncher(
+bool EphemeralAppLauncher::IsFeatureEnabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableEphemeralApps);
+}
+
+// static
+scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForLauncher(
const std::string& webstore_item_id,
Profile* profile,
gfx::NativeWindow parent_window,
- const Callback& callback) {
+ const LaunchCallback& callback) {
scoped_refptr<EphemeralAppLauncher> installer =
new EphemeralAppLauncher(webstore_item_id,
profile,
@@ -57,54 +105,57 @@ EphemeralAppLauncher::CreateForLauncher(
}
// static
-scoped_refptr<EphemeralAppLauncher>
-EphemeralAppLauncher::CreateForLink(
+scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForWebContents(
const std::string& webstore_item_id,
- content::WebContents* web_contents) {
+ content::WebContents* web_contents,
+ const LaunchCallback& callback) {
scoped_refptr<EphemeralAppLauncher> installer =
- new EphemeralAppLauncher(webstore_item_id,
- web_contents,
- Callback());
+ new EphemeralAppLauncher(webstore_item_id, web_contents, callback);
installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
return installer;
}
void EphemeralAppLauncher::Start() {
+ if (!IsFeatureEnabled()) {
+ InvokeCallback(webstore_install::LAUNCH_FEATURE_DISABLED,
+ kFeatureDisabledError);
+ return;
+ }
+
+ // Check whether the app already exists in extension system before downloading
+ // from the webstore.
const Extension* extension =
ExtensionRegistry::Get(profile())
->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
if (extension) {
+ webstore_install::Result result = webstore_install::UNKNOWN_ERROR;
+ std::string error;
+ if (!CanLaunchInstalledApp(extension, &result, &error)) {
+ InvokeCallback(result, error);
+ return;
+ }
+
if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
profile())) {
LaunchApp(extension);
- InvokeCallback(std::string());
+ InvokeCallback(webstore_install::SUCCESS, std::string());
return;
}
- // The ephemeral app may have been updated and disabled as it requests
- // more permissions. In this case we should always prompt before
- // launching.
- extension_enable_flow_.reset(
- new ExtensionEnableFlow(profile(), extension->id(), this));
- if (web_contents())
- extension_enable_flow_->StartForWebContents(web_contents());
- else
- extension_enable_flow_->StartForNativeWindow(parent_window_);
-
- // Keep this object alive until the enable flow is complete.
- AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
+ EnableInstalledApp(extension);
return;
}
- // Fetch the app from the webstore.
+ // Install the app ephemerally and launch when complete.
BeginInstall();
}
EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
Profile* profile,
gfx::NativeWindow parent_window,
- const Callback& callback)
- : WebstoreStandaloneInstaller(webstore_item_id, profile, callback),
+ const LaunchCallback& callback)
+ : WebstoreStandaloneInstaller(webstore_item_id, profile, Callback()),
+ launch_callback_(callback),
parent_window_(parent_window),
dummy_web_contents_(
WebContents::Create(WebContents::CreateParams(profile))) {
@@ -112,30 +163,149 @@ EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
content::WebContents* web_contents,
- const Callback& callback)
+ const LaunchCallback& callback)
: WebstoreStandaloneInstaller(webstore_item_id,
ProfileForWebContents(web_contents),
- callback),
+ Callback()),
content::WebContentsObserver(web_contents),
+ launch_callback_(callback),
parent_window_(NativeWindowForWebContents(web_contents)) {
}
EphemeralAppLauncher::~EphemeralAppLauncher() {}
-void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
- DCHECK(extension);
- if (!extension->is_app()) {
- LOG(ERROR) << "Unable to launch extension " << extension->id()
- << ". It is not an app.";
- return;
+bool EphemeralAppLauncher::CanLaunchInstalledApp(
+ const extensions::Extension* extension,
+ webstore_install::Result* reason,
+ std::string* error) {
+ if (!CheckCommonLaunchCriteria(profile(), extension, reason, error))
+ return false;
+
+ // Do not launch blacklisted apps.
+ if (ExtensionPrefs::Get(profile())->IsExtensionBlacklisted(extension->id())) {
+ *reason = webstore_install::BLACKLISTED;
+ *error = kBlacklistedError;
+ return false;
+ }
+
+ // If the app has missing requirements, it cannot be launched.
+ if (!extensions::util::IsAppLaunchable(extension->id(), profile())) {
+ *reason = webstore_install::REQUIREMENT_VIOLATIONS;
+ *error = kRequirementsError;
+ return false;
}
+ return true;
+}
+
+void EphemeralAppLauncher::EnableInstalledApp(const Extension* extension) {
+ extension_enable_flow_.reset(
+ new ExtensionEnableFlow(profile(), extension->id(), this));
+ if (web_contents())
+ extension_enable_flow_->StartForWebContents(web_contents());
+ else
+ extension_enable_flow_->StartForNativeWindow(parent_window_);
+
+ // Keep this object alive until the enable flow is complete. Either
+ // ExtensionEnableFlowFinished() or ExtensionEnableFlowAborted() will be
+ // called.
+ AddRef();
+}
+
+void EphemeralAppLauncher::MaybeLaunchApp() {
+ webstore_install::Result result = webstore_install::UNKNOWN_ERROR;
+ std::string error;
+
+ ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
+ const Extension* extension =
+ registry->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
+ if (extension) {
+ // Although the installation was successful, the app may not be
+ // launchable.
+ if (registry->enabled_extensions().Contains(extension->id())) {
+ result = webstore_install::SUCCESS;
+ LaunchApp(extension);
+ } else {
+ error = kAppDisabledError;
+ // Determine why the app cannot be launched.
+ CanLaunchInstalledApp(extension, &result, &error);
+ }
+ } else {
+ // The extension must be present in the registry if installed.
+ NOTREACHED();
+ error = kMissingAppError;
+ }
+
+ InvokeCallback(result, error);
+}
+
+void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
+ DCHECK(extension && extension->is_app() &&
+ ExtensionRegistry::Get(profile())
+ ->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED));
+
AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
params.desktop_type =
chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
OpenApplication(params);
}
+void EphemeralAppLauncher::InvokeCallback(webstore_install::Result result,
+ const std::string& error) {
+ if (!launch_callback_.is_null()) {
+ launch_callback_.Run(result, error);
+ launch_callback_.Reset();
+ }
+}
+
+void EphemeralAppLauncher::AbortLaunch(webstore_install::Result result,
+ const std::string& error) {
+ InvokeCallback(result, error);
+ WebstoreStandaloneInstaller::CompleteInstall(webstore_install::ABORTED,
+ std::string());
+}
+
+scoped_ptr<extensions::ExtensionInstallChecker>
+EphemeralAppLauncher::CreateInstallChecker() {
+ return make_scoped_ptr(new ExtensionInstallChecker(profile()));
+}
+
+void EphemeralAppLauncher::CheckEphemeralInstallPermitted() {
+ scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
+ DCHECK(extension.get()); // Checked in OnManifestParsed().
+
+ install_checker_ = CreateInstallChecker();
+ DCHECK(install_checker_.get());
+
+ install_checker_->set_extension(extension);
+ install_checker_->Start(ExtensionInstallChecker::CHECK_BLACKLIST |
+ ExtensionInstallChecker::CHECK_REQUIREMENTS,
+ true,
+ base::Bind(&EphemeralAppLauncher::OnInstallChecked,
+ base::Unretained(this)));
+}
+
+void EphemeralAppLauncher::OnInstallChecked(int check_failures) {
+ if (!CheckRequestorAlive()) {
+ AbortLaunch(webstore_install::UNKNOWN_ERROR, std::string());
+ return;
+ }
+
+ if (install_checker_->blacklist_state() == extensions::BLACKLISTED_MALWARE) {
+ AbortLaunch(webstore_install::BLACKLISTED, kBlacklistedError);
+ return;
+ }
+
+ if (!install_checker_->requirement_errors().empty()) {
+ AbortLaunch(webstore_install::REQUIREMENT_VIOLATIONS,
+ install_checker_->requirement_errors().front());
+ return;
+ }
+
+ // Proceed with the normal install flow.
+ ProceedWithInstallPrompt();
+}
+
bool EphemeralAppLauncher::CheckRequestorAlive() const {
return dummy_web_contents_.get() != NULL || web_contents() != NULL;
}
@@ -158,12 +328,13 @@ WebContents* EphemeralAppLauncher::GetWebContents() const {
scoped_refptr<ExtensionInstallPrompt::Prompt>
EphemeralAppLauncher::CreateInstallPrompt() const {
- DCHECK(extension_.get() != NULL);
+ const Extension* extension = localized_extension_for_display();
+ DCHECK(extension); // Checked in OnManifestParsed().
// Skip the prompt by returning null if the app does not need to display
// permission warnings.
extensions::PermissionMessages permissions =
- extension_->permissions_data()->GetPermissionMessages();
+ extension->permissions_data()->GetPermissionMessages();
if (permissions.empty())
return NULL;
@@ -185,31 +356,24 @@ bool EphemeralAppLauncher::CheckRequestorPermitted(
return true;
}
-bool EphemeralAppLauncher::CheckInstallValid(
- const base::DictionaryValue& manifest,
- std::string* error) {
- extension_ = Extension::Create(
- base::FilePath(),
- extensions::Manifest::INTERNAL,
- manifest,
- Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
- id(),
- error);
- if (!extension_.get()) {
- *error = kInvalidManifestError;
- return false;
+void EphemeralAppLauncher::OnManifestParsed() {
+ const Extension* extension = GetLocalizedExtensionForDisplay();
+ if (!extension) {
+ AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
+ return;
}
- if (!extension_->is_app()) {
- *error = kExtensionTypeError;
- return false;
+ webstore_install::Result result = webstore_install::UNKNOWN_ERROR;
+ std::string error;
+ if (!CheckCommonLaunchCriteria(profile(), extension, &result, &error)) {
+ AbortLaunch(result, error);
+ return;
}
- return true;
+ CheckEphemeralInstallPermitted();
}
-scoped_ptr<ExtensionInstallPrompt>
-EphemeralAppLauncher::CreateInstallUI() {
+scoped_ptr<ExtensionInstallPrompt> EphemeralAppLauncher::CreateInstallUI() {
if (web_contents())
return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
@@ -225,26 +389,27 @@ EphemeralAppLauncher::CreateApproval() const {
return approval.Pass();
}
-void EphemeralAppLauncher::CompleteInstall(const std::string& error) {
- if (error.empty()) {
- const Extension* extension =
- ExtensionRegistry::Get(profile())
- ->GetExtensionById(id(), ExtensionRegistry::ENABLED);
- if (extension)
- LaunchApp(extension);
- }
+void EphemeralAppLauncher::CompleteInstall(webstore_install::Result result,
+ const std::string& error) {
+ if (result == webstore_install::SUCCESS)
+ MaybeLaunchApp();
+ else if (!launch_callback_.is_null())
+ InvokeCallback(result, error);
- WebstoreStandaloneInstaller::CompleteInstall(error);
+ WebstoreStandaloneInstaller::CompleteInstall(result, error);
}
void EphemeralAppLauncher::WebContentsDestroyed() {
+ launch_callback_.Reset();
AbortInstall();
}
void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
- CompleteInstall(std::string());
+ MaybeLaunchApp();
+ Release(); // Matches the AddRef in EnableInstalledApp().
}
void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
- CompleteInstall(kLaunchAbortedError);
+ InvokeCallback(webstore_install::USER_CANCELLED, kUserCancelledError);
+ Release(); // Matches the AddRef in EnableInstalledApp().
}
« no previous file with comments | « chrome/browser/apps/ephemeral_app_launcher.h ('k') | chrome/browser/apps/ephemeral_app_launcher_browsertest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698