Update mojo surfaces bindings and mojo/cc/ glue
[chromium-blink-merge.git] / chrome / browser / apps / ephemeral_app_launcher.cc
blob51b1908b91a73b2fd5491bff6c7948decaa1fcae
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;
36 namespace {
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) {
49 if (!contents)
50 return NULL;
52 return Profile::FromBrowserContext(contents->GetBrowserContext());
55 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
56 if (!contents)
57 return NULL;
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,
67 std::string* error) {
68 // Only apps can be launched.
69 if (!extension->is_app()) {
70 *reason = webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE;
71 *error = kExtensionTypeError;
72 return false;
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);
82 return false;
85 return true;
88 } // namespace
90 // static
91 bool EphemeralAppLauncher::IsFeatureEnabled() {
92 return CommandLine::ForCurrentProcess()->HasSwitch(
93 switches::kEnableEphemeralApps);
96 // static
97 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForLauncher(
98 const std::string& webstore_item_id,
99 Profile* profile,
100 gfx::NativeWindow parent_window,
101 const LaunchCallback& callback) {
102 scoped_refptr<EphemeralAppLauncher> installer =
103 new EphemeralAppLauncher(webstore_item_id,
104 profile,
105 parent_window,
106 callback);
107 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
108 return installer;
111 // static
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);
119 return installer;
122 void EphemeralAppLauncher::Start() {
123 if (!IsFeatureEnabled()) {
124 InvokeCallback(webstore_install::LAUNCH_FEATURE_DISABLED,
125 kFeatureDisabledError);
126 return;
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);
134 if (extension) {
135 webstore_install::Result result = webstore_install::OTHER_ERROR;
136 std::string error;
137 if (!CanLaunchInstalledApp(extension, &result, &error)) {
138 InvokeCallback(result, error);
139 return;
142 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
143 profile())) {
144 LaunchApp(extension);
145 InvokeCallback(webstore_install::SUCCESS, std::string());
146 return;
149 EnableInstalledApp(extension);
150 return;
153 // Install the app ephemerally and launch when complete.
154 BeginInstall();
157 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
158 Profile* profile,
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),
164 dummy_web_contents_(
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),
173 Callback()),
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() {
187 if (web_contents())
188 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
190 return make_scoped_ptr(
191 new ExtensionInstallPrompt(profile(), parent_window_, NULL));
194 scoped_ptr<WebstoreInstaller::Approval> EphemeralAppLauncher::CreateApproval()
195 const {
196 scoped_ptr<WebstoreInstaller::Approval> approval =
197 WebstoreStandaloneInstaller::CreateApproval();
198 approval->is_ephemeral = true;
199 return approval.Pass();
202 bool EphemeralAppLauncher::CanLaunchInstalledApp(
203 const extensions::Extension* extension,
204 webstore_install::Result* reason,
205 std::string* error) {
206 if (!CheckCommonLaunchCriteria(profile(), extension, reason, error))
207 return false;
209 // Do not launch blacklisted apps.
210 if (ExtensionPrefs::Get(profile())->IsExtensionBlacklisted(extension->id())) {
211 *reason = webstore_install::BLACKLISTED;
212 *error = kBlacklistedError;
213 return false;
216 // If the app has missing requirements, it cannot be launched.
217 if (!extensions::util::IsAppLaunchable(extension->id(), profile())) {
218 *reason = webstore_install::REQUIREMENT_VIOLATIONS;
219 *error = kRequirementsError;
220 return false;
223 return true;
226 void EphemeralAppLauncher::EnableInstalledApp(const Extension* extension) {
227 // Check whether an install is already in progress.
228 webstore_install::Result result = webstore_install::OTHER_ERROR;
229 std::string error;
230 if (!EnsureUniqueInstall(&result, &error)) {
231 InvokeCallback(result, error);
232 return;
235 // Keep this object alive until the enable flow is complete. Either
236 // ExtensionEnableFlowFinished() or ExtensionEnableFlowAborted() will be
237 // called.
238 AddRef();
240 extension_enable_flow_.reset(
241 new ExtensionEnableFlow(profile(), extension->id(), this));
242 if (web_contents())
243 extension_enable_flow_->StartForWebContents(web_contents());
244 else
245 extension_enable_flow_->StartForNativeWindow(parent_window_);
248 void EphemeralAppLauncher::MaybeLaunchApp() {
249 webstore_install::Result result = webstore_install::OTHER_ERROR;
250 std::string error;
252 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
253 const Extension* extension =
254 registry->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
255 if (extension) {
256 // Although the installation was successful, the app may not be
257 // launchable.
258 if (registry->enabled_extensions().Contains(extension->id())) {
259 result = webstore_install::SUCCESS;
260 LaunchApp(extension);
261 } else {
262 error = kAppDisabledError;
263 // Determine why the app cannot be launched.
264 CanLaunchInstalledApp(extension, &result, &error);
266 } else {
267 // The extension must be present in the registry if installed.
268 NOTREACHED();
269 error = kMissingAppError;
272 InvokeCallback(result, error);
275 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
276 DCHECK(extension && extension->is_app() &&
277 ExtensionRegistry::Get(profile())
278 ->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED));
280 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
281 params.desktop_type =
282 chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
283 OpenApplication(params);
286 bool EphemeralAppLauncher::LaunchHostedApp(const Extension* extension) const {
287 GURL launch_url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
288 if (!launch_url.is_valid())
289 return false;
291 chrome::ScopedTabbedBrowserDisplayer displayer(
292 profile(), chrome::GetHostDesktopTypeForNativeWindow(parent_window_));
293 chrome::NavigateParams params(
294 displayer.browser(), launch_url, content::PAGE_TRANSITION_AUTO_TOPLEVEL);
295 params.disposition = NEW_FOREGROUND_TAB;
296 chrome::Navigate(&params);
297 return true;
300 void EphemeralAppLauncher::InvokeCallback(webstore_install::Result result,
301 const std::string& error) {
302 if (!launch_callback_.is_null()) {
303 LaunchCallback callback = launch_callback_;
304 launch_callback_.Reset();
305 callback.Run(result, error);
309 void EphemeralAppLauncher::AbortLaunch(webstore_install::Result result,
310 const std::string& error) {
311 InvokeCallback(result, error);
312 WebstoreStandaloneInstaller::CompleteInstall(result, error);
315 void EphemeralAppLauncher::CheckEphemeralInstallPermitted() {
316 scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
317 DCHECK(extension.get()); // Checked in OnManifestParsed().
319 install_checker_ = CreateInstallChecker();
320 DCHECK(install_checker_.get());
322 install_checker_->set_extension(extension);
323 install_checker_->Start(ExtensionInstallChecker::CHECK_BLACKLIST |
324 ExtensionInstallChecker::CHECK_REQUIREMENTS,
325 true,
326 base::Bind(&EphemeralAppLauncher::OnInstallChecked,
327 base::Unretained(this)));
330 void EphemeralAppLauncher::OnInstallChecked(int check_failures) {
331 if (!CheckRequestorAlive()) {
332 AbortLaunch(webstore_install::OTHER_ERROR, std::string());
333 return;
336 if (install_checker_->blacklist_state() == extensions::BLACKLISTED_MALWARE) {
337 AbortLaunch(webstore_install::BLACKLISTED, kBlacklistedError);
338 return;
341 if (!install_checker_->requirement_errors().empty()) {
342 AbortLaunch(webstore_install::REQUIREMENT_VIOLATIONS,
343 install_checker_->requirement_errors().front());
344 return;
347 // Proceed with the normal install flow.
348 ProceedWithInstallPrompt();
351 void EphemeralAppLauncher::InitInstallData(
352 extensions::ActiveInstallData* install_data) const {
353 install_data->is_ephemeral = true;
356 bool EphemeralAppLauncher::CheckRequestorAlive() const {
357 return dummy_web_contents_.get() != NULL || web_contents() != NULL;
360 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
361 return GURL::EmptyGURL();
364 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
365 return false;
368 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
369 return false;
372 WebContents* EphemeralAppLauncher::GetWebContents() const {
373 return web_contents() ? web_contents() : dummy_web_contents_.get();
376 scoped_refptr<ExtensionInstallPrompt::Prompt>
377 EphemeralAppLauncher::CreateInstallPrompt() const {
378 const Extension* extension = localized_extension_for_display();
379 DCHECK(extension); // Checked in OnManifestParsed().
381 // Skip the prompt by returning null if the app does not need to display
382 // permission warnings.
383 extensions::PermissionMessages permissions =
384 extension->permissions_data()->GetPermissionMessages();
385 if (permissions.empty())
386 return NULL;
388 return make_scoped_refptr(new ExtensionInstallPrompt::Prompt(
389 ExtensionInstallPrompt::LAUNCH_PROMPT));
392 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
393 const base::DictionaryValue& webstore_data,
394 std::string* error) const {
395 *error = "";
396 return true;
399 bool EphemeralAppLauncher::CheckRequestorPermitted(
400 const base::DictionaryValue& webstore_data,
401 std::string* error) const {
402 *error = "";
403 return true;
406 void EphemeralAppLauncher::OnManifestParsed() {
407 const Extension* extension = GetLocalizedExtensionForDisplay();
408 if (!extension) {
409 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
410 return;
413 webstore_install::Result result = webstore_install::OTHER_ERROR;
414 std::string error;
415 if (!CheckCommonLaunchCriteria(profile(), extension, &result, &error)) {
416 AbortLaunch(result, error);
417 return;
420 if (extension->is_legacy_packaged_app()) {
421 AbortLaunch(webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE,
422 kAppTypeError);
423 return;
426 if (extension->is_hosted_app()) {
427 // Hosted apps do not need to be installed ephemerally. Just navigate to
428 // their launch url.
429 if (LaunchHostedApp(extension))
430 AbortLaunch(webstore_install::SUCCESS, std::string());
431 else
432 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
433 return;
436 CheckEphemeralInstallPermitted();
439 void EphemeralAppLauncher::CompleteInstall(webstore_install::Result result,
440 const std::string& error) {
441 if (result == webstore_install::SUCCESS)
442 MaybeLaunchApp();
443 else if (!launch_callback_.is_null())
444 InvokeCallback(result, error);
446 WebstoreStandaloneInstaller::CompleteInstall(result, error);
449 void EphemeralAppLauncher::WebContentsDestroyed() {
450 launch_callback_.Reset();
451 AbortInstall();
454 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
455 MaybeLaunchApp();
457 // CompleteInstall will call Release.
458 WebstoreStandaloneInstaller::CompleteInstall(webstore_install::SUCCESS,
459 std::string());
462 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
463 // CompleteInstall will call Release.
464 CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);