1 // Copyright 2013 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.
5 #include "chrome/browser/apps/ephemeral_app_launcher.h"
7 #include "base/command_line.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_install_checker.h"
10 #include "chrome/browser/extensions/extension_install_prompt.h"
11 #include "chrome/browser/extensions/extension_util.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/ui/browser_navigator.h"
14 #include "chrome/browser/ui/extensions/application_launch.h"
15 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
16 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
17 #include "chrome/common/chrome_switches.h"
18 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
19 #include "content/public/browser/web_contents.h"
20 #include "extensions/browser/extension_prefs.h"
21 #include "extensions/browser/extension_registry.h"
22 #include "extensions/browser/extension_system.h"
23 #include "extensions/browser/management_policy.h"
24 #include "extensions/common/permissions/permissions_data.h"
26 using content::WebContents;
27 using extensions::Extension;
28 using extensions::ExtensionInstallChecker;
29 using extensions::ExtensionPrefs;
30 using extensions::ExtensionRegistry;
31 using extensions::ExtensionSystem;
32 using extensions::ManagementPolicy;
33 using extensions::WebstoreInstaller;
34 namespace webstore_install = extensions::webstore_install;
38 const char kInvalidManifestError[] = "Invalid manifest";
39 const char kExtensionTypeError[] = "Not an app";
40 const char kAppTypeError[] = "Ephemeral legacy packaged apps not supported";
41 const char kUserCancelledError[] = "Launch cancelled by the user";
42 const char kBlacklistedError[] = "App is blacklisted for malware";
43 const char kRequirementsError[] = "App has missing requirements";
44 const char kFeatureDisabledError[] = "Launching ephemeral apps is not enabled";
45 const char kMissingAppError[] = "App is not installed";
46 const char kAppDisabledError[] = "App is disabled";
48 Profile* ProfileForWebContents(content::WebContents* contents) {
52 return Profile::FromBrowserContext(contents->GetBrowserContext());
55 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
59 return contents->GetTopLevelNativeWindow();
62 // Check whether an extension can be launched. The extension does not need to
63 // be currently installed.
64 bool CheckCommonLaunchCriteria(Profile* profile,
65 const Extension* extension,
66 webstore_install::Result* reason,
68 // Only apps can be launched.
69 if (!extension->is_app()) {
70 *reason = webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE;
71 *error = kExtensionTypeError;
75 // Do not launch apps blocked by management policies.
76 ManagementPolicy* management_policy =
77 ExtensionSystem::Get(profile)->management_policy();
78 base::string16 policy_error;
79 if (!management_policy->UserMayLoad(extension, &policy_error)) {
80 *reason = webstore_install::BLOCKED_BY_POLICY;
81 *error = base::UTF16ToUTF8(policy_error);
91 bool EphemeralAppLauncher::IsFeatureEnabled() {
92 return CommandLine::ForCurrentProcess()->HasSwitch(
93 switches::kEnableEphemeralApps);
97 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForLauncher(
98 const std::string& webstore_item_id,
100 gfx::NativeWindow parent_window,
101 const LaunchCallback& callback) {
102 scoped_refptr<EphemeralAppLauncher> installer =
103 new EphemeralAppLauncher(webstore_item_id,
107 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
112 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForWebContents(
113 const std::string& webstore_item_id,
114 content::WebContents* web_contents,
115 const LaunchCallback& callback) {
116 scoped_refptr<EphemeralAppLauncher> installer =
117 new EphemeralAppLauncher(webstore_item_id, web_contents, callback);
118 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
122 void EphemeralAppLauncher::Start() {
123 if (!IsFeatureEnabled()) {
124 InvokeCallback(webstore_install::LAUNCH_FEATURE_DISABLED,
125 kFeatureDisabledError);
129 // Check whether the app already exists in extension system before downloading
130 // from the webstore.
131 const Extension* extension =
132 ExtensionRegistry::Get(profile())
133 ->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
135 webstore_install::Result result = webstore_install::OTHER_ERROR;
137 if (!CanLaunchInstalledApp(extension, &result, &error)) {
138 InvokeCallback(result, error);
142 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
144 LaunchApp(extension);
145 InvokeCallback(webstore_install::SUCCESS, std::string());
149 EnableInstalledApp(extension);
153 // Install the app ephemerally and launch when complete.
157 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
159 gfx::NativeWindow parent_window,
160 const LaunchCallback& callback)
161 : WebstoreStandaloneInstaller(webstore_item_id, profile, Callback()),
162 launch_callback_(callback),
163 parent_window_(parent_window),
165 WebContents::Create(WebContents::CreateParams(profile))) {
168 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
169 content::WebContents* web_contents,
170 const LaunchCallback& callback)
171 : WebstoreStandaloneInstaller(webstore_item_id,
172 ProfileForWebContents(web_contents),
174 content::WebContentsObserver(web_contents),
175 launch_callback_(callback),
176 parent_window_(NativeWindowForWebContents(web_contents)) {
179 EphemeralAppLauncher::~EphemeralAppLauncher() {}
181 scoped_ptr<extensions::ExtensionInstallChecker>
182 EphemeralAppLauncher::CreateInstallChecker() {
183 return make_scoped_ptr(new ExtensionInstallChecker(profile()));
186 scoped_ptr<ExtensionInstallPrompt> EphemeralAppLauncher::CreateInstallUI() {
188 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
190 return make_scoped_ptr(new ExtensionInstallPrompt(profile(), parent_window_));
193 scoped_ptr<WebstoreInstaller::Approval> EphemeralAppLauncher::CreateApproval()
195 scoped_ptr<WebstoreInstaller::Approval> approval =
196 WebstoreStandaloneInstaller::CreateApproval();
197 approval->is_ephemeral = true;
198 return approval.Pass();
201 bool EphemeralAppLauncher::CanLaunchInstalledApp(
202 const extensions::Extension* extension,
203 webstore_install::Result* reason,
204 std::string* error) {
205 if (!CheckCommonLaunchCriteria(profile(), extension, reason, error))
208 // Do not launch blacklisted apps.
209 if (ExtensionPrefs::Get(profile())->IsExtensionBlacklisted(extension->id())) {
210 *reason = webstore_install::BLACKLISTED;
211 *error = kBlacklistedError;
215 // If the app has missing requirements, it cannot be launched.
216 if (!extensions::util::IsAppLaunchable(extension->id(), profile())) {
217 *reason = webstore_install::REQUIREMENT_VIOLATIONS;
218 *error = kRequirementsError;
225 void EphemeralAppLauncher::EnableInstalledApp(const Extension* extension) {
226 // Check whether an install is already in progress.
227 webstore_install::Result result = webstore_install::OTHER_ERROR;
229 if (!EnsureUniqueInstall(&result, &error)) {
230 InvokeCallback(result, error);
234 // Keep this object alive until the enable flow is complete. Either
235 // ExtensionEnableFlowFinished() or ExtensionEnableFlowAborted() will be
239 extension_enable_flow_.reset(
240 new ExtensionEnableFlow(profile(), extension->id(), this));
242 extension_enable_flow_->StartForWebContents(web_contents());
244 extension_enable_flow_->StartForNativeWindow(parent_window_);
247 void EphemeralAppLauncher::MaybeLaunchApp() {
248 webstore_install::Result result = webstore_install::OTHER_ERROR;
251 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
252 const Extension* extension =
253 registry->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
255 // Although the installation was successful, the app may not be
257 if (registry->enabled_extensions().Contains(extension->id())) {
258 result = webstore_install::SUCCESS;
259 LaunchApp(extension);
261 error = kAppDisabledError;
262 // Determine why the app cannot be launched.
263 CanLaunchInstalledApp(extension, &result, &error);
266 // The extension must be present in the registry if installed.
268 error = kMissingAppError;
271 InvokeCallback(result, error);
274 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
275 DCHECK(extension && extension->is_app() &&
276 ExtensionRegistry::Get(profile())
277 ->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED));
279 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
280 params.desktop_type =
281 chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
282 OpenApplication(params);
285 bool EphemeralAppLauncher::LaunchHostedApp(const Extension* extension) const {
286 GURL launch_url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
287 if (!launch_url.is_valid())
290 chrome::ScopedTabbedBrowserDisplayer displayer(
291 profile(), chrome::GetHostDesktopTypeForNativeWindow(parent_window_));
292 chrome::NavigateParams params(
293 displayer.browser(), launch_url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
294 params.disposition = NEW_FOREGROUND_TAB;
295 chrome::Navigate(¶ms);
299 void EphemeralAppLauncher::InvokeCallback(webstore_install::Result result,
300 const std::string& error) {
301 if (!launch_callback_.is_null()) {
302 LaunchCallback callback = launch_callback_;
303 launch_callback_.Reset();
304 callback.Run(result, error);
308 void EphemeralAppLauncher::AbortLaunch(webstore_install::Result result,
309 const std::string& error) {
310 InvokeCallback(result, error);
311 WebstoreStandaloneInstaller::CompleteInstall(result, error);
314 void EphemeralAppLauncher::CheckEphemeralInstallPermitted() {
315 scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
316 DCHECK(extension.get()); // Checked in OnManifestParsed().
318 install_checker_ = CreateInstallChecker();
319 DCHECK(install_checker_.get());
321 install_checker_->set_extension(extension);
322 install_checker_->Start(ExtensionInstallChecker::CHECK_BLACKLIST |
323 ExtensionInstallChecker::CHECK_REQUIREMENTS,
325 base::Bind(&EphemeralAppLauncher::OnInstallChecked,
326 base::Unretained(this)));
329 void EphemeralAppLauncher::OnInstallChecked(int check_failures) {
330 if (!CheckRequestorAlive()) {
331 AbortLaunch(webstore_install::OTHER_ERROR, std::string());
335 if (install_checker_->blacklist_state() == extensions::BLACKLISTED_MALWARE) {
336 AbortLaunch(webstore_install::BLACKLISTED, kBlacklistedError);
340 if (!install_checker_->requirement_errors().empty()) {
341 AbortLaunch(webstore_install::REQUIREMENT_VIOLATIONS,
342 install_checker_->requirement_errors().front());
346 // Proceed with the normal install flow.
347 ProceedWithInstallPrompt();
350 void EphemeralAppLauncher::InitInstallData(
351 extensions::ActiveInstallData* install_data) const {
352 install_data->is_ephemeral = true;
355 bool EphemeralAppLauncher::CheckRequestorAlive() const {
356 return dummy_web_contents_.get() != NULL || web_contents() != NULL;
359 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
360 return GURL::EmptyGURL();
363 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
367 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
371 WebContents* EphemeralAppLauncher::GetWebContents() const {
372 return web_contents() ? web_contents() : dummy_web_contents_.get();
375 scoped_refptr<ExtensionInstallPrompt::Prompt>
376 EphemeralAppLauncher::CreateInstallPrompt() const {
377 const Extension* extension = localized_extension_for_display();
378 DCHECK(extension); // Checked in OnManifestParsed().
380 // Skip the prompt by returning null if the app does not need to display
381 // permission warnings.
382 extensions::PermissionMessages permissions =
383 extension->permissions_data()->GetPermissionMessages();
384 if (permissions.empty())
387 return make_scoped_refptr(new ExtensionInstallPrompt::Prompt(
388 ExtensionInstallPrompt::LAUNCH_PROMPT));
391 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
392 const base::DictionaryValue& webstore_data,
393 std::string* error) const {
398 bool EphemeralAppLauncher::CheckRequestorPermitted(
399 const base::DictionaryValue& webstore_data,
400 std::string* error) const {
405 void EphemeralAppLauncher::OnManifestParsed() {
406 scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
407 if (!extension.get()) {
408 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
412 webstore_install::Result result = webstore_install::OTHER_ERROR;
414 if (!CheckCommonLaunchCriteria(profile(), extension.get(), &result, &error)) {
415 AbortLaunch(result, error);
419 if (extension->is_legacy_packaged_app()) {
420 AbortLaunch(webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE,
425 if (extension->is_hosted_app()) {
426 // Hosted apps do not need to be installed ephemerally. Just navigate to
428 if (LaunchHostedApp(extension.get()))
429 AbortLaunch(webstore_install::SUCCESS, std::string());
431 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
435 CheckEphemeralInstallPermitted();
438 void EphemeralAppLauncher::CompleteInstall(webstore_install::Result result,
439 const std::string& error) {
440 if (result == webstore_install::SUCCESS)
442 else if (!launch_callback_.is_null())
443 InvokeCallback(result, error);
445 WebstoreStandaloneInstaller::CompleteInstall(result, error);
448 void EphemeralAppLauncher::WebContentsDestroyed() {
449 launch_callback_.Reset();
453 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
456 // CompleteInstall will call Release.
457 WebstoreStandaloneInstaller::CompleteInstall(webstore_install::SUCCESS,
461 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
462 // CompleteInstall will call Release.
463 CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);