Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / chrome / browser / apps / ephemeral_app_launcher.cc
blobb480345ef25a0eb0a712db297a3baabca5091d21
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/app_launch_params.h"
15 #include "chrome/browser/ui/extensions/application_launch.h"
16 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
17 #include "chrome/browser/ui/native_window_tracker.h"
18 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
19 #include "chrome/common/chrome_switches.h"
20 #include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
21 #include "content/public/browser/web_contents.h"
22 #include "extensions/browser/extension_prefs.h"
23 #include "extensions/browser/extension_registry.h"
24 #include "extensions/browser/extension_system.h"
25 #include "extensions/browser/management_policy.h"
26 #include "extensions/common/constants.h"
27 #include "extensions/common/permissions/permissions_data.h"
28 #include "ui/app_list/app_list_switches.h"
30 using content::WebContents;
31 using extensions::Extension;
32 using extensions::ExtensionInstallChecker;
33 using extensions::ExtensionPrefs;
34 using extensions::ExtensionRegistry;
35 using extensions::ExtensionSystem;
36 using extensions::ManagementPolicy;
37 using extensions::WebstoreInstaller;
38 namespace webstore_install = extensions::webstore_install;
40 namespace {
42 const char kInvalidManifestError[] = "Invalid manifest";
43 const char kExtensionTypeError[] = "Not an app";
44 const char kAppTypeError[] = "Ephemeral legacy packaged apps not supported";
45 const char kUserCancelledError[] = "Launch cancelled by the user";
46 const char kBlacklistedError[] = "App is blacklisted for malware";
47 const char kRequirementsError[] = "App has missing requirements";
48 const char kFeatureDisabledError[] = "Launching ephemeral apps is not enabled";
49 const char kMissingAppError[] = "App is not installed";
50 const char kAppDisabledError[] = "App is disabled";
52 Profile* ProfileForWebContents(content::WebContents* contents) {
53 if (!contents)
54 return NULL;
56 return Profile::FromBrowserContext(contents->GetBrowserContext());
59 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
60 if (!contents)
61 return NULL;
63 return contents->GetTopLevelNativeWindow();
66 // Check whether an extension can be launched. The extension does not need to
67 // be currently installed.
68 bool CheckCommonLaunchCriteria(Profile* profile,
69 const Extension* extension,
70 webstore_install::Result* reason,
71 std::string* error) {
72 // Only apps can be launched.
73 if (!extension->is_app()) {
74 *reason = webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE;
75 *error = kExtensionTypeError;
76 return false;
79 // Do not launch apps blocked by management policies.
80 ManagementPolicy* management_policy =
81 ExtensionSystem::Get(profile)->management_policy();
82 base::string16 policy_error;
83 if (!management_policy->UserMayLoad(extension, &policy_error)) {
84 *reason = webstore_install::BLOCKED_BY_POLICY;
85 *error = base::UTF16ToUTF8(policy_error);
86 return false;
89 return true;
92 } // namespace
94 // static
95 bool EphemeralAppLauncher::IsFeatureEnabled() {
96 return base::CommandLine::ForCurrentProcess()->HasSwitch(
97 switches::kEnableEphemeralAppsInWebstore);
100 // static
101 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForLauncher(
102 const std::string& webstore_item_id,
103 Profile* profile,
104 gfx::NativeWindow parent_window,
105 const LaunchCallback& callback) {
106 scoped_refptr<EphemeralAppLauncher> installer =
107 new EphemeralAppLauncher(webstore_item_id,
108 profile,
109 parent_window,
110 callback);
111 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
112 return installer;
115 // static
116 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForWebContents(
117 const std::string& webstore_item_id,
118 content::WebContents* web_contents,
119 const LaunchCallback& callback) {
120 scoped_refptr<EphemeralAppLauncher> installer =
121 new EphemeralAppLauncher(webstore_item_id, web_contents, callback);
122 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
123 return installer;
126 void EphemeralAppLauncher::Start() {
127 if (!IsFeatureEnabled()) {
128 InvokeCallback(webstore_install::LAUNCH_FEATURE_DISABLED,
129 kFeatureDisabledError);
130 return;
133 // Check whether the app already exists in extension system before downloading
134 // from the webstore.
135 const Extension* extension =
136 ExtensionRegistry::Get(profile())
137 ->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
138 if (extension) {
139 webstore_install::Result result = webstore_install::OTHER_ERROR;
140 std::string error;
141 if (!CanLaunchInstalledApp(extension, &result, &error)) {
142 InvokeCallback(result, error);
143 return;
146 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
147 profile())) {
148 LaunchApp(extension);
149 InvokeCallback(webstore_install::SUCCESS, std::string());
150 return;
153 EnableInstalledApp(extension);
154 return;
157 // Install the app ephemerally and launch when complete.
158 BeginInstall();
161 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
162 Profile* profile,
163 gfx::NativeWindow parent_window,
164 const LaunchCallback& callback)
165 : WebstoreStandaloneInstaller(webstore_item_id, profile, Callback()),
166 launch_callback_(callback),
167 parent_window_(parent_window),
168 dummy_web_contents_(
169 WebContents::Create(WebContents::CreateParams(profile))) {
170 if (parent_window_)
171 parent_window_tracker_ = NativeWindowTracker::Create(parent_window);
174 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
175 content::WebContents* web_contents,
176 const LaunchCallback& callback)
177 : WebstoreStandaloneInstaller(webstore_item_id,
178 ProfileForWebContents(web_contents),
179 Callback()),
180 content::WebContentsObserver(web_contents),
181 launch_callback_(callback),
182 parent_window_(NativeWindowForWebContents(web_contents)) {
185 EphemeralAppLauncher::~EphemeralAppLauncher() {}
187 scoped_ptr<extensions::ExtensionInstallChecker>
188 EphemeralAppLauncher::CreateInstallChecker() {
189 return make_scoped_ptr(new ExtensionInstallChecker(profile()));
192 scoped_ptr<ExtensionInstallPrompt> EphemeralAppLauncher::CreateInstallUI() {
193 if (web_contents())
194 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
196 return make_scoped_ptr(new ExtensionInstallPrompt(profile(), parent_window_));
199 scoped_ptr<WebstoreInstaller::Approval> EphemeralAppLauncher::CreateApproval()
200 const {
201 scoped_ptr<WebstoreInstaller::Approval> approval =
202 WebstoreStandaloneInstaller::CreateApproval();
203 approval->is_ephemeral = true;
204 return approval.Pass();
207 bool EphemeralAppLauncher::CanLaunchInstalledApp(
208 const extensions::Extension* extension,
209 webstore_install::Result* reason,
210 std::string* error) {
211 if (!CheckCommonLaunchCriteria(profile(), extension, reason, error))
212 return false;
214 // Do not launch blacklisted apps.
215 if (ExtensionPrefs::Get(profile())->IsExtensionBlacklisted(extension->id())) {
216 *reason = webstore_install::BLACKLISTED;
217 *error = kBlacklistedError;
218 return false;
221 // If the app has missing requirements, it cannot be launched.
222 if (!extensions::util::IsAppLaunchable(extension->id(), profile())) {
223 *reason = webstore_install::REQUIREMENT_VIOLATIONS;
224 *error = kRequirementsError;
225 return false;
228 return true;
231 void EphemeralAppLauncher::EnableInstalledApp(const Extension* extension) {
232 // Check whether an install is already in progress.
233 webstore_install::Result result = webstore_install::OTHER_ERROR;
234 std::string error;
235 if (!EnsureUniqueInstall(&result, &error)) {
236 InvokeCallback(result, error);
237 return;
240 // Keep this object alive until the enable flow is complete. Either
241 // ExtensionEnableFlowFinished() or ExtensionEnableFlowAborted() will be
242 // called.
243 AddRef();
245 extension_enable_flow_.reset(
246 new ExtensionEnableFlow(profile(), extension->id(), this));
247 if (web_contents())
248 extension_enable_flow_->StartForWebContents(web_contents());
249 else
250 extension_enable_flow_->StartForNativeWindow(parent_window_);
253 void EphemeralAppLauncher::MaybeLaunchApp() {
254 webstore_install::Result result = webstore_install::OTHER_ERROR;
255 std::string error;
257 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
258 const Extension* extension =
259 registry->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
260 if (extension) {
261 // Although the installation was successful, the app may not be
262 // launchable.
263 if (registry->enabled_extensions().Contains(extension->id())) {
264 result = webstore_install::SUCCESS;
265 LaunchApp(extension);
266 } else {
267 error = kAppDisabledError;
268 // Determine why the app cannot be launched.
269 CanLaunchInstalledApp(extension, &result, &error);
271 } else {
272 // The extension must be present in the registry if installed.
273 NOTREACHED();
274 error = kMissingAppError;
277 InvokeCallback(result, error);
280 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
281 DCHECK(extension && extension->is_app() &&
282 ExtensionRegistry::Get(profile())
283 ->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED));
285 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB,
286 extensions::SOURCE_EPHEMERAL_APP);
287 params.desktop_type =
288 chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
289 OpenApplication(params);
292 bool EphemeralAppLauncher::LaunchHostedApp(const Extension* extension) const {
293 GURL launch_url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
294 if (!launch_url.is_valid())
295 return false;
297 chrome::ScopedTabbedBrowserDisplayer displayer(
298 profile(), chrome::GetHostDesktopTypeForNativeWindow(parent_window_));
299 chrome::NavigateParams params(
300 displayer.browser(), launch_url, ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
301 params.disposition = NEW_FOREGROUND_TAB;
302 chrome::Navigate(&params);
303 return true;
306 void EphemeralAppLauncher::InvokeCallback(webstore_install::Result result,
307 const std::string& error) {
308 if (!launch_callback_.is_null()) {
309 LaunchCallback callback = launch_callback_;
310 launch_callback_.Reset();
311 callback.Run(result, error);
315 void EphemeralAppLauncher::AbortLaunch(webstore_install::Result result,
316 const std::string& error) {
317 InvokeCallback(result, error);
318 WebstoreStandaloneInstaller::CompleteInstall(result, error);
321 void EphemeralAppLauncher::CheckEphemeralInstallPermitted() {
322 scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
323 DCHECK(extension.get()); // Checked in OnManifestParsed().
325 install_checker_ = CreateInstallChecker();
326 DCHECK(install_checker_.get());
328 install_checker_->set_extension(extension);
329 install_checker_->Start(ExtensionInstallChecker::CHECK_BLACKLIST |
330 ExtensionInstallChecker::CHECK_REQUIREMENTS,
331 true,
332 base::Bind(&EphemeralAppLauncher::OnInstallChecked,
333 base::Unretained(this)));
336 void EphemeralAppLauncher::OnInstallChecked(int check_failures) {
337 if (!CheckRequestorAlive()) {
338 AbortLaunch(webstore_install::OTHER_ERROR, std::string());
339 return;
342 if (install_checker_->blacklist_state() == extensions::BLACKLISTED_MALWARE) {
343 AbortLaunch(webstore_install::BLACKLISTED, kBlacklistedError);
344 return;
347 if (!install_checker_->requirement_errors().empty()) {
348 AbortLaunch(webstore_install::REQUIREMENT_VIOLATIONS,
349 install_checker_->requirement_errors().front());
350 return;
353 // Proceed with the normal install flow.
354 ProceedWithInstallPrompt();
357 void EphemeralAppLauncher::InitInstallData(
358 extensions::ActiveInstallData* install_data) const {
359 install_data->is_ephemeral = true;
362 bool EphemeralAppLauncher::CheckRequestorAlive() const {
363 if (!parent_window_) {
364 // Assume the requestor is always alive if |parent_window_| is null.
365 return true;
368 return (web_contents() != nullptr ||
369 (parent_window_tracker_ &&
370 !parent_window_tracker_->WasNativeWindowClosed()));
373 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
374 return GURL::EmptyGURL();
377 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
378 return false;
381 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
382 return false;
385 WebContents* EphemeralAppLauncher::GetWebContents() const {
386 return web_contents() ? web_contents() : dummy_web_contents_.get();
389 scoped_refptr<ExtensionInstallPrompt::Prompt>
390 EphemeralAppLauncher::CreateInstallPrompt() const {
391 const Extension* extension = localized_extension_for_display();
392 DCHECK(extension); // Checked in OnManifestParsed().
394 // Skip the prompt by returning null if the app does not need to display
395 // permission warnings.
396 if (extension->permissions_data()->GetPermissionMessageStrings().empty())
397 return NULL;
399 return make_scoped_refptr(new ExtensionInstallPrompt::Prompt(
400 ExtensionInstallPrompt::LAUNCH_PROMPT));
403 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
404 const base::DictionaryValue& webstore_data,
405 std::string* error) const {
406 *error = "";
407 return true;
410 bool EphemeralAppLauncher::CheckRequestorPermitted(
411 const base::DictionaryValue& webstore_data,
412 std::string* error) const {
413 *error = "";
414 return true;
417 void EphemeralAppLauncher::OnManifestParsed() {
418 scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
419 if (!extension.get()) {
420 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
421 return;
424 webstore_install::Result result = webstore_install::OTHER_ERROR;
425 std::string error;
426 if (!CheckCommonLaunchCriteria(profile(), extension.get(), &result, &error)) {
427 AbortLaunch(result, error);
428 return;
431 if (extension->is_legacy_packaged_app()) {
432 AbortLaunch(webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE,
433 kAppTypeError);
434 return;
437 if (extension->is_hosted_app()) {
438 // Hosted apps do not need to be installed ephemerally. Just navigate to
439 // their launch url.
440 if (LaunchHostedApp(extension.get()))
441 AbortLaunch(webstore_install::SUCCESS, std::string());
442 else
443 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
444 return;
447 CheckEphemeralInstallPermitted();
450 void EphemeralAppLauncher::CompleteInstall(webstore_install::Result result,
451 const std::string& error) {
452 if (result == webstore_install::SUCCESS)
453 MaybeLaunchApp();
454 else if (!launch_callback_.is_null())
455 InvokeCallback(result, error);
457 WebstoreStandaloneInstaller::CompleteInstall(result, error);
460 void EphemeralAppLauncher::WebContentsDestroyed() {
461 launch_callback_.Reset();
462 AbortInstall();
465 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
466 MaybeLaunchApp();
468 // CompleteInstall will call Release.
469 WebstoreStandaloneInstaller::CompleteInstall(webstore_install::SUCCESS,
470 std::string());
473 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
474 // CompleteInstall will call Release.
475 CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);