Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / apps / ephemeral_app_launcher.cc
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.
4
5 #include "chrome/browser/apps/ephemeral_app_launcher.h"
6
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"
25
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;
35
36 namespace {
37
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";
47
48 Profile* ProfileForWebContents(content::WebContents* contents) {
49   if (!contents)
50     return NULL;
51
52   return Profile::FromBrowserContext(contents->GetBrowserContext());
53 }
54
55 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
56   if (!contents)
57     return NULL;
58
59   return contents->GetTopLevelNativeWindow();
60 }
61
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;
73   }
74
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;
83   }
84
85   return true;
86 }
87
88 }  // namespace
89
90 // static
91 bool EphemeralAppLauncher::IsFeatureEnabled() {
92   return CommandLine::ForCurrentProcess()->HasSwitch(
93       switches::kEnableEphemeralApps);
94 }
95
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;
109 }
110
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;
120 }
121
122 void EphemeralAppLauncher::Start() {
123   if (!IsFeatureEnabled()) {
124     InvokeCallback(webstore_install::LAUNCH_FEATURE_DISABLED,
125                    kFeatureDisabledError);
126     return;
127   }
128
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;
140     }
141
142     if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
143                                                          profile())) {
144       LaunchApp(extension);
145       InvokeCallback(webstore_install::SUCCESS, std::string());
146       return;
147     }
148
149     EnableInstalledApp(extension);
150     return;
151   }
152
153   // Install the app ephemerally and launch when complete.
154   BeginInstall();
155 }
156
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))) {
166 }
167
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)) {
177 }
178
179 EphemeralAppLauncher::~EphemeralAppLauncher() {}
180
181 scoped_ptr<extensions::ExtensionInstallChecker>
182 EphemeralAppLauncher::CreateInstallChecker() {
183   return make_scoped_ptr(new ExtensionInstallChecker(profile()));
184 }
185
186 scoped_ptr<ExtensionInstallPrompt> EphemeralAppLauncher::CreateInstallUI() {
187   if (web_contents())
188     return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
189
190   return make_scoped_ptr(new ExtensionInstallPrompt(profile(), parent_window_));
191 }
192
193 scoped_ptr<WebstoreInstaller::Approval> EphemeralAppLauncher::CreateApproval()
194     const {
195   scoped_ptr<WebstoreInstaller::Approval> approval =
196       WebstoreStandaloneInstaller::CreateApproval();
197   approval->is_ephemeral = true;
198   return approval.Pass();
199 }
200
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))
206     return false;
207
208   // Do not launch blacklisted apps.
209   if (ExtensionPrefs::Get(profile())->IsExtensionBlacklisted(extension->id())) {
210     *reason = webstore_install::BLACKLISTED;
211     *error = kBlacklistedError;
212     return false;
213   }
214
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;
219     return false;
220   }
221
222   return true;
223 }
224
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;
228   std::string error;
229   if (!EnsureUniqueInstall(&result, &error)) {
230     InvokeCallback(result, error);
231     return;
232   }
233
234   // Keep this object alive until the enable flow is complete. Either
235   // ExtensionEnableFlowFinished() or ExtensionEnableFlowAborted() will be
236   // called.
237   AddRef();
238
239   extension_enable_flow_.reset(
240       new ExtensionEnableFlow(profile(), extension->id(), this));
241   if (web_contents())
242     extension_enable_flow_->StartForWebContents(web_contents());
243   else
244     extension_enable_flow_->StartForNativeWindow(parent_window_);
245 }
246
247 void EphemeralAppLauncher::MaybeLaunchApp() {
248   webstore_install::Result result = webstore_install::OTHER_ERROR;
249   std::string error;
250
251   ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
252   const Extension* extension =
253       registry->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
254   if (extension) {
255     // Although the installation was successful, the app may not be
256     // launchable.
257     if (registry->enabled_extensions().Contains(extension->id())) {
258       result = webstore_install::SUCCESS;
259       LaunchApp(extension);
260     } else {
261       error = kAppDisabledError;
262       // Determine why the app cannot be launched.
263       CanLaunchInstalledApp(extension, &result, &error);
264     }
265   } else {
266     // The extension must be present in the registry if installed.
267     NOTREACHED();
268     error = kMissingAppError;
269   }
270
271   InvokeCallback(result, error);
272 }
273
274 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
275   DCHECK(extension && extension->is_app() &&
276          ExtensionRegistry::Get(profile())
277              ->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED));
278
279   AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
280   params.desktop_type =
281       chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
282   OpenApplication(params);
283 }
284
285 bool EphemeralAppLauncher::LaunchHostedApp(const Extension* extension) const {
286   GURL launch_url = extensions::AppLaunchInfo::GetLaunchWebURL(extension);
287   if (!launch_url.is_valid())
288     return false;
289
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(&params);
296   return true;
297 }
298
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);
305   }
306 }
307
308 void EphemeralAppLauncher::AbortLaunch(webstore_install::Result result,
309                                        const std::string& error) {
310   InvokeCallback(result, error);
311   WebstoreStandaloneInstaller::CompleteInstall(result, error);
312 }
313
314 void EphemeralAppLauncher::CheckEphemeralInstallPermitted() {
315   scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
316   DCHECK(extension.get());  // Checked in OnManifestParsed().
317
318   install_checker_ = CreateInstallChecker();
319   DCHECK(install_checker_.get());
320
321   install_checker_->set_extension(extension);
322   install_checker_->Start(ExtensionInstallChecker::CHECK_BLACKLIST |
323                               ExtensionInstallChecker::CHECK_REQUIREMENTS,
324                           true,
325                           base::Bind(&EphemeralAppLauncher::OnInstallChecked,
326                                      base::Unretained(this)));
327 }
328
329 void EphemeralAppLauncher::OnInstallChecked(int check_failures) {
330   if (!CheckRequestorAlive()) {
331     AbortLaunch(webstore_install::OTHER_ERROR, std::string());
332     return;
333   }
334
335   if (install_checker_->blacklist_state() == extensions::BLACKLISTED_MALWARE) {
336     AbortLaunch(webstore_install::BLACKLISTED, kBlacklistedError);
337     return;
338   }
339
340   if (!install_checker_->requirement_errors().empty()) {
341     AbortLaunch(webstore_install::REQUIREMENT_VIOLATIONS,
342                 install_checker_->requirement_errors().front());
343     return;
344   }
345
346   // Proceed with the normal install flow.
347   ProceedWithInstallPrompt();
348 }
349
350 void EphemeralAppLauncher::InitInstallData(
351     extensions::ActiveInstallData* install_data) const {
352   install_data->is_ephemeral = true;
353 }
354
355 bool EphemeralAppLauncher::CheckRequestorAlive() const {
356   return dummy_web_contents_.get() != NULL || web_contents() != NULL;
357 }
358
359 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
360   return GURL::EmptyGURL();
361 }
362
363 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
364   return false;
365 }
366
367 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
368   return false;
369 }
370
371 WebContents* EphemeralAppLauncher::GetWebContents() const {
372   return web_contents() ? web_contents() : dummy_web_contents_.get();
373 }
374
375 scoped_refptr<ExtensionInstallPrompt::Prompt>
376 EphemeralAppLauncher::CreateInstallPrompt() const {
377   const Extension* extension = localized_extension_for_display();
378   DCHECK(extension);  // Checked in OnManifestParsed().
379
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())
385     return NULL;
386
387   return make_scoped_refptr(new ExtensionInstallPrompt::Prompt(
388       ExtensionInstallPrompt::LAUNCH_PROMPT));
389 }
390
391 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
392     const base::DictionaryValue& webstore_data,
393     std::string* error) const {
394   *error = "";
395   return true;
396 }
397
398 bool EphemeralAppLauncher::CheckRequestorPermitted(
399     const base::DictionaryValue& webstore_data,
400     std::string* error) const {
401   *error = "";
402   return true;
403 }
404
405 void EphemeralAppLauncher::OnManifestParsed() {
406   scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
407   if (!extension.get()) {
408     AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
409     return;
410   }
411
412   webstore_install::Result result = webstore_install::OTHER_ERROR;
413   std::string error;
414   if (!CheckCommonLaunchCriteria(profile(), extension.get(), &result, &error)) {
415     AbortLaunch(result, error);
416     return;
417   }
418
419   if (extension->is_legacy_packaged_app()) {
420     AbortLaunch(webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE,
421                 kAppTypeError);
422     return;
423   }
424
425   if (extension->is_hosted_app()) {
426     // Hosted apps do not need to be installed ephemerally. Just navigate to
427     // their launch url.
428     if (LaunchHostedApp(extension.get()))
429       AbortLaunch(webstore_install::SUCCESS, std::string());
430     else
431       AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
432     return;
433   }
434
435   CheckEphemeralInstallPermitted();
436 }
437
438 void EphemeralAppLauncher::CompleteInstall(webstore_install::Result result,
439                                            const std::string& error) {
440   if (result == webstore_install::SUCCESS)
441     MaybeLaunchApp();
442   else if (!launch_callback_.is_null())
443     InvokeCallback(result, error);
444
445   WebstoreStandaloneInstaller::CompleteInstall(result, error);
446 }
447
448 void EphemeralAppLauncher::WebContentsDestroyed() {
449   launch_callback_.Reset();
450   AbortInstall();
451 }
452
453 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
454   MaybeLaunchApp();
455
456   // CompleteInstall will call Release.
457   WebstoreStandaloneInstaller::CompleteInstall(webstore_install::SUCCESS,
458                                                std::string());
459 }
460
461 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
462   // CompleteInstall will call Release.
463   CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
464 }