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

Side by Side Diff: chrome/browser/apps/ephemeral_app_launcher.cc

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