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

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

Issue 339103002: Update the EphemeralAppLauncher for use by the webstorePrivate API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase again Created 6 years, 6 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/apps/ephemeral_app_launcher.h" 5 #include "chrome/browser/apps/ephemeral_app_launcher.h"
6 6
7 #include "base/command_line.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/extensions/extension_install_checker.h"
7 #include "chrome/browser/extensions/extension_install_prompt.h" 10 #include "chrome/browser/extensions/extension_install_prompt.h"
8 #include "chrome/browser/extensions/extension_util.h" 11 #include "chrome/browser/extensions/extension_util.h"
9 #include "chrome/browser/profiles/profile.h" 12 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/ui/extensions/application_launch.h" 13 #include "chrome/browser/ui/extensions/application_launch.h"
11 #include "chrome/browser/ui/extensions/extension_enable_flow.h" 14 #include "chrome/browser/ui/extensions/extension_enable_flow.h"
15 #include "chrome/common/chrome_switches.h"
12 #include "content/public/browser/web_contents.h" 16 #include "content/public/browser/web_contents.h"
17 #include "extensions/browser/extension_prefs.h"
13 #include "extensions/browser/extension_registry.h" 18 #include "extensions/browser/extension_registry.h"
19 #include "extensions/browser/extension_system.h"
20 #include "extensions/browser/management_policy.h"
14 #include "extensions/common/permissions/permissions_data.h" 21 #include "extensions/common/permissions/permissions_data.h"
15 22
16 using content::WebContents; 23 using content::WebContents;
17 using extensions::Extension; 24 using extensions::Extension;
25 using extensions::ExtensionInstallChecker;
26 using extensions::ExtensionPrefs;
18 using extensions::ExtensionRegistry; 27 using extensions::ExtensionRegistry;
28 using extensions::ExtensionSystem;
29 using extensions::ManagementPolicy;
19 using extensions::WebstoreInstaller; 30 using extensions::WebstoreInstaller;
31 namespace webstore_install = extensions::webstore_install;
20 32
21 namespace { 33 namespace {
22 34
23 const char kInvalidManifestError[] = "Invalid manifest"; 35 const char kInvalidManifestError[] = "Invalid manifest";
24 const char kExtensionTypeError[] = "Ephemeral extensions are not permitted"; 36 const char kExtensionTypeError[] = "Cannot launch an extension";
25 const char kLaunchAbortedError[] = "Launch aborted"; 37 const char kUserCancelledError[] = "Launch cancelled by the user";
38 const char kBlacklistedError[] = "App is blacklisted for malware";
39 const char kRequirementsError[] = "App has missing requirements";
40 const char kFeatureDisabledError[] = "Launching ephemeral apps is not enabled";
41 const char kMissingAppError[] = "App is not installed";
42 const char kAppDisabledError[] = "App is disabled";
26 43
27 Profile* ProfileForWebContents(content::WebContents* contents) { 44 Profile* ProfileForWebContents(content::WebContents* contents) {
28 if (!contents) 45 if (!contents)
29 return NULL; 46 return NULL;
30 47
31 return Profile::FromBrowserContext(contents->GetBrowserContext()); 48 return Profile::FromBrowserContext(contents->GetBrowserContext());
32 } 49 }
33 50
34 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) { 51 gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
35 if (!contents) 52 if (!contents)
36 return NULL; 53 return NULL;
37 54
38 return contents->GetTopLevelNativeWindow(); 55 return contents->GetTopLevelNativeWindow();
39 } 56 }
40 57
58 // Check whether an extension can be launched. The extension does not need to
59 // be currently installed.
60 bool CheckCommonLaunchCriteria(Profile* profile,
61 const extensions::Extension* extension,
62 webstore_install::Result* reason,
63 std::string* error) {
64 // Only apps can be launched.
65 if (!extension->is_app()) {
66 *reason = webstore_install::LAUNCH_UNSUPPORTED_EXTENSION_TYPE;
67 *error = kExtensionTypeError;
68 return false;
69 }
70
71 // Do not launch apps blocked by management policies.
72 ManagementPolicy* management_policy =
73 ExtensionSystem::Get(profile)->management_policy();
74 base::string16 policy_error;
75 if (!management_policy->UserMayLoad(extension, &policy_error)) {
76 *reason = webstore_install::BLOCKED_BY_POLICY;
77 *error = base::UTF16ToUTF8(policy_error);
78 return false;
79 }
80
81 return true;
82 }
83
41 } // namespace 84 } // namespace
42 85
43 // static 86 // static
44 scoped_refptr<EphemeralAppLauncher> 87 bool EphemeralAppLauncher::IsFeatureEnabled() {
45 EphemeralAppLauncher::CreateForLauncher( 88 return CommandLine::ForCurrentProcess()->HasSwitch(
89 switches::kEnableEphemeralApps);
90 }
91
92 // static
93 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForLauncher(
46 const std::string& webstore_item_id, 94 const std::string& webstore_item_id,
47 Profile* profile, 95 Profile* profile,
48 gfx::NativeWindow parent_window, 96 gfx::NativeWindow parent_window,
49 const Callback& callback) { 97 const LaunchCallback& callback) {
50 scoped_refptr<EphemeralAppLauncher> installer = 98 scoped_refptr<EphemeralAppLauncher> installer =
51 new EphemeralAppLauncher(webstore_item_id, 99 new EphemeralAppLauncher(webstore_item_id,
52 profile, 100 profile,
53 parent_window, 101 parent_window,
54 callback); 102 callback);
55 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER); 103 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_APP_LAUNCHER);
56 return installer; 104 return installer;
57 } 105 }
58 106
59 // static 107 // static
60 scoped_refptr<EphemeralAppLauncher> 108 scoped_refptr<EphemeralAppLauncher> EphemeralAppLauncher::CreateForWebContents(
61 EphemeralAppLauncher::CreateForLink(
62 const std::string& webstore_item_id, 109 const std::string& webstore_item_id,
63 content::WebContents* web_contents) { 110 content::WebContents* web_contents,
111 const LaunchCallback& callback) {
64 scoped_refptr<EphemeralAppLauncher> installer = 112 scoped_refptr<EphemeralAppLauncher> installer =
65 new EphemeralAppLauncher(webstore_item_id, 113 new EphemeralAppLauncher(webstore_item_id, web_contents, callback);
66 web_contents,
67 Callback());
68 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER); 114 installer->set_install_source(WebstoreInstaller::INSTALL_SOURCE_OTHER);
69 return installer; 115 return installer;
70 } 116 }
71 117
72 void EphemeralAppLauncher::Start() { 118 void EphemeralAppLauncher::Start() {
119 if (!IsFeatureEnabled()) {
120 InvokeCallback(webstore_install::LAUNCH_FEATURE_DISABLED,
121 kFeatureDisabledError);
122 return;
123 }
124
125 // Check whether the app already exists in extension system before downloading
126 // from the webstore.
73 const Extension* extension = 127 const Extension* extension =
74 ExtensionRegistry::Get(profile()) 128 ExtensionRegistry::Get(profile())
75 ->GetExtensionById(id(), ExtensionRegistry::EVERYTHING); 129 ->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
76 if (extension) { 130 if (extension) {
131 webstore_install::Result result = webstore_install::UNKNOWN_ERROR;
132 std::string error;
133 if (!CanLaunchInstalledApp(extension, &result, &error)) {
134 InvokeCallback(result, error);
135 return;
136 }
137
77 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(), 138 if (extensions::util::IsAppLaunchableWithoutEnabling(extension->id(),
78 profile())) { 139 profile())) {
79 LaunchApp(extension); 140 LaunchApp(extension);
80 InvokeCallback(std::string()); 141 InvokeCallback(webstore_install::SUCCESS, std::string());
81 return; 142 return;
82 } 143 }
83 144
84 // The ephemeral app may have been updated and disabled as it requests 145 EnableInstalledApp(extension);
85 // more permissions. In this case we should always prompt before
86 // launching.
87 extension_enable_flow_.reset(
88 new ExtensionEnableFlow(profile(), extension->id(), this));
89 if (web_contents())
90 extension_enable_flow_->StartForWebContents(web_contents());
91 else
92 extension_enable_flow_->StartForNativeWindow(parent_window_);
93
94 // Keep this object alive until the enable flow is complete.
95 AddRef(); // Balanced in WebstoreStandaloneInstaller::CompleteInstall.
96 return; 146 return;
97 } 147 }
98 148
99 // Fetch the app from the webstore. 149 // Install the app ephemerally and launch when complete.
100 BeginInstall(); 150 BeginInstall();
101 } 151 }
102 152
103 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id, 153 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
104 Profile* profile, 154 Profile* profile,
105 gfx::NativeWindow parent_window, 155 gfx::NativeWindow parent_window,
106 const Callback& callback) 156 const LaunchCallback& callback)
107 : WebstoreStandaloneInstaller(webstore_item_id, profile, callback), 157 : WebstoreStandaloneInstaller(webstore_item_id, profile, Callback()),
158 launch_callback_(callback),
108 parent_window_(parent_window), 159 parent_window_(parent_window),
109 dummy_web_contents_( 160 dummy_web_contents_(
110 WebContents::Create(WebContents::CreateParams(profile))) { 161 WebContents::Create(WebContents::CreateParams(profile))) {
111 } 162 }
112 163
113 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id, 164 EphemeralAppLauncher::EphemeralAppLauncher(const std::string& webstore_item_id,
114 content::WebContents* web_contents, 165 content::WebContents* web_contents,
115 const Callback& callback) 166 const LaunchCallback& callback)
116 : WebstoreStandaloneInstaller(webstore_item_id, 167 : WebstoreStandaloneInstaller(webstore_item_id,
117 ProfileForWebContents(web_contents), 168 ProfileForWebContents(web_contents),
118 callback), 169 Callback()),
119 content::WebContentsObserver(web_contents), 170 content::WebContentsObserver(web_contents),
171 launch_callback_(callback),
120 parent_window_(NativeWindowForWebContents(web_contents)) { 172 parent_window_(NativeWindowForWebContents(web_contents)) {
121 } 173 }
122 174
123 EphemeralAppLauncher::~EphemeralAppLauncher() {} 175 EphemeralAppLauncher::~EphemeralAppLauncher() {}
124 176
177 bool EphemeralAppLauncher::CanLaunchInstalledApp(
178 const extensions::Extension* extension,
179 webstore_install::Result* reason,
180 std::string* error) {
181 if (!CheckCommonLaunchCriteria(profile(), extension, reason, error))
182 return false;
183
184 // Do not launch blacklisted apps.
185 if (ExtensionPrefs::Get(profile())->IsExtensionBlacklisted(extension->id())) {
186 *reason = webstore_install::BLACKLISTED;
187 *error = kBlacklistedError;
188 return false;
189 }
190
191 // If the app has missing requirements, it cannot be launched.
192 if (!extensions::util::IsAppLaunchable(extension->id(), profile())) {
193 *reason = webstore_install::REQUIREMENT_VIOLATIONS;
194 *error = kRequirementsError;
195 return false;
196 }
197
198 return true;
199 }
200
201 void EphemeralAppLauncher::EnableInstalledApp(const Extension* extension) {
202 extension_enable_flow_.reset(
203 new ExtensionEnableFlow(profile(), extension->id(), this));
204 if (web_contents())
205 extension_enable_flow_->StartForWebContents(web_contents());
206 else
207 extension_enable_flow_->StartForNativeWindow(parent_window_);
208
209 // Keep this object alive until the enable flow is complete. Either
210 // ExtensionEnableFlowFinished() or ExtensionEnableFlowAborted() will be
211 // called.
212 AddRef();
213 }
214
215 void EphemeralAppLauncher::MaybeLaunchApp() {
216 webstore_install::Result result = webstore_install::UNKNOWN_ERROR;
217 std::string error;
218
219 ExtensionRegistry* registry = ExtensionRegistry::Get(profile());
220 const Extension* extension =
221 registry->GetExtensionById(id(), ExtensionRegistry::EVERYTHING);
222 if (extension) {
223 // Although the installation was successful, the app may not be
224 // launchable.
225 if (registry->enabled_extensions().Contains(extension->id())) {
226 result = webstore_install::SUCCESS;
227 LaunchApp(extension);
228 } else {
229 error = kAppDisabledError;
230 // Determine why the app cannot be launched.
231 CanLaunchInstalledApp(extension, &result, &error);
232 }
233 } else {
234 // The extension must be present in the registry if installed.
235 NOTREACHED();
236 error = kMissingAppError;
237 }
238
239 InvokeCallback(result, error);
240 }
241
125 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const { 242 void EphemeralAppLauncher::LaunchApp(const Extension* extension) const {
126 DCHECK(extension); 243 DCHECK(extension && extension->is_app() &&
127 if (!extension->is_app()) { 244 ExtensionRegistry::Get(profile())
128 LOG(ERROR) << "Unable to launch extension " << extension->id() 245 ->GetExtensionById(extension->id(), ExtensionRegistry::ENABLED));
129 << ". It is not an app.";
130 return;
131 }
132 246
133 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB); 247 AppLaunchParams params(profile(), extension, NEW_FOREGROUND_TAB);
134 params.desktop_type = 248 params.desktop_type =
135 chrome::GetHostDesktopTypeForNativeWindow(parent_window_); 249 chrome::GetHostDesktopTypeForNativeWindow(parent_window_);
136 OpenApplication(params); 250 OpenApplication(params);
137 } 251 }
138 252
253 void EphemeralAppLauncher::InvokeCallback(webstore_install::Result result,
254 const std::string& error) {
255 if (!launch_callback_.is_null()) {
256 launch_callback_.Run(result, error);
257 launch_callback_.Reset();
258 }
259 }
260
261 void EphemeralAppLauncher::AbortLaunch(webstore_install::Result result,
262 const std::string& error) {
263 InvokeCallback(result, error);
264 WebstoreStandaloneInstaller::CompleteInstall(webstore_install::ABORTED,
265 std::string());
266 }
267
268 scoped_ptr<extensions::ExtensionInstallChecker>
269 EphemeralAppLauncher::CreateInstallChecker() {
270 return make_scoped_ptr(new ExtensionInstallChecker(profile()));
271 }
272
273 void EphemeralAppLauncher::CheckEphemeralInstallPermitted() {
274 scoped_refptr<const Extension> extension = GetLocalizedExtensionForDisplay();
275 DCHECK(extension.get()); // Checked in OnManifestParsed().
276
277 install_checker_ = CreateInstallChecker();
278 DCHECK(install_checker_.get());
279
280 install_checker_->set_extension(extension);
281 install_checker_->Start(ExtensionInstallChecker::CHECK_BLACKLIST |
282 ExtensionInstallChecker::CHECK_REQUIREMENTS,
283 true,
284 base::Bind(&EphemeralAppLauncher::OnInstallChecked,
285 base::Unretained(this)));
286 }
287
288 void EphemeralAppLauncher::OnInstallChecked(int check_failures) {
289 if (!CheckRequestorAlive()) {
290 AbortLaunch(webstore_install::UNKNOWN_ERROR, std::string());
291 return;
292 }
293
294 if (install_checker_->blacklist_state() == extensions::BLACKLISTED_MALWARE) {
295 AbortLaunch(webstore_install::BLACKLISTED, kBlacklistedError);
296 return;
297 }
298
299 if (!install_checker_->requirement_errors().empty()) {
300 AbortLaunch(webstore_install::REQUIREMENT_VIOLATIONS,
301 install_checker_->requirement_errors().front());
302 return;
303 }
304
305 // Proceed with the normal install flow.
306 ProceedWithInstallPrompt();
307 }
308
139 bool EphemeralAppLauncher::CheckRequestorAlive() const { 309 bool EphemeralAppLauncher::CheckRequestorAlive() const {
140 return dummy_web_contents_.get() != NULL || web_contents() != NULL; 310 return dummy_web_contents_.get() != NULL || web_contents() != NULL;
141 } 311 }
142 312
143 const GURL& EphemeralAppLauncher::GetRequestorURL() const { 313 const GURL& EphemeralAppLauncher::GetRequestorURL() const {
144 return GURL::EmptyGURL(); 314 return GURL::EmptyGURL();
145 } 315 }
146 316
147 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const { 317 bool EphemeralAppLauncher::ShouldShowPostInstallUI() const {
148 return false; 318 return false;
149 } 319 }
150 320
151 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const { 321 bool EphemeralAppLauncher::ShouldShowAppInstalledBubble() const {
152 return false; 322 return false;
153 } 323 }
154 324
155 WebContents* EphemeralAppLauncher::GetWebContents() const { 325 WebContents* EphemeralAppLauncher::GetWebContents() const {
156 return web_contents() ? web_contents() : dummy_web_contents_.get(); 326 return web_contents() ? web_contents() : dummy_web_contents_.get();
157 } 327 }
158 328
159 scoped_refptr<ExtensionInstallPrompt::Prompt> 329 scoped_refptr<ExtensionInstallPrompt::Prompt>
160 EphemeralAppLauncher::CreateInstallPrompt() const { 330 EphemeralAppLauncher::CreateInstallPrompt() const {
161 DCHECK(extension_.get() != NULL); 331 const Extension* extension = localized_extension_for_display();
332 DCHECK(extension); // Checked in OnManifestParsed().
162 333
163 // Skip the prompt by returning null if the app does not need to display 334 // Skip the prompt by returning null if the app does not need to display
164 // permission warnings. 335 // permission warnings.
165 extensions::PermissionMessages permissions = 336 extensions::PermissionMessages permissions =
166 extension_->permissions_data()->GetPermissionMessages(); 337 extension->permissions_data()->GetPermissionMessages();
167 if (permissions.empty()) 338 if (permissions.empty())
168 return NULL; 339 return NULL;
169 340
170 return make_scoped_refptr(new ExtensionInstallPrompt::Prompt( 341 return make_scoped_refptr(new ExtensionInstallPrompt::Prompt(
171 ExtensionInstallPrompt::LAUNCH_PROMPT)); 342 ExtensionInstallPrompt::LAUNCH_PROMPT));
172 } 343 }
173 344
174 bool EphemeralAppLauncher::CheckInlineInstallPermitted( 345 bool EphemeralAppLauncher::CheckInlineInstallPermitted(
175 const base::DictionaryValue& webstore_data, 346 const base::DictionaryValue& webstore_data,
176 std::string* error) const { 347 std::string* error) const {
177 *error = ""; 348 *error = "";
178 return true; 349 return true;
179 } 350 }
180 351
181 bool EphemeralAppLauncher::CheckRequestorPermitted( 352 bool EphemeralAppLauncher::CheckRequestorPermitted(
182 const base::DictionaryValue& webstore_data, 353 const base::DictionaryValue& webstore_data,
183 std::string* error) const { 354 std::string* error) const {
184 *error = ""; 355 *error = "";
185 return true; 356 return true;
186 } 357 }
187 358
188 bool EphemeralAppLauncher::CheckInstallValid( 359 void EphemeralAppLauncher::OnManifestParsed() {
189 const base::DictionaryValue& manifest, 360 const Extension* extension = GetLocalizedExtensionForDisplay();
190 std::string* error) { 361 if (!extension) {
191 extension_ = Extension::Create( 362 AbortLaunch(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
192 base::FilePath(), 363 return;
193 extensions::Manifest::INTERNAL,
194 manifest,
195 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
196 id(),
197 error);
198 if (!extension_.get()) {
199 *error = kInvalidManifestError;
200 return false;
201 } 364 }
202 365
203 if (!extension_->is_app()) { 366 webstore_install::Result result = webstore_install::UNKNOWN_ERROR;
204 *error = kExtensionTypeError; 367 std::string error;
205 return false; 368 if (!CheckCommonLaunchCriteria(profile(), extension, &result, &error)) {
369 AbortLaunch(result, error);
370 return;
206 } 371 }
207 372
208 return true; 373 CheckEphemeralInstallPermitted();
209 } 374 }
210 375
211 scoped_ptr<ExtensionInstallPrompt> 376 scoped_ptr<ExtensionInstallPrompt> EphemeralAppLauncher::CreateInstallUI() {
212 EphemeralAppLauncher::CreateInstallUI() {
213 if (web_contents()) 377 if (web_contents())
214 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents())); 378 return make_scoped_ptr(new ExtensionInstallPrompt(web_contents()));
215 379
216 return make_scoped_ptr( 380 return make_scoped_ptr(
217 new ExtensionInstallPrompt(profile(), parent_window_, NULL)); 381 new ExtensionInstallPrompt(profile(), parent_window_, NULL));
218 } 382 }
219 383
220 scoped_ptr<WebstoreInstaller::Approval> 384 scoped_ptr<WebstoreInstaller::Approval>
221 EphemeralAppLauncher::CreateApproval() const { 385 EphemeralAppLauncher::CreateApproval() const {
222 scoped_ptr<WebstoreInstaller::Approval> approval = 386 scoped_ptr<WebstoreInstaller::Approval> approval =
223 WebstoreStandaloneInstaller::CreateApproval(); 387 WebstoreStandaloneInstaller::CreateApproval();
224 approval->is_ephemeral = true; 388 approval->is_ephemeral = true;
225 return approval.Pass(); 389 return approval.Pass();
226 } 390 }
227 391
228 void EphemeralAppLauncher::CompleteInstall(const std::string& error) { 392 void EphemeralAppLauncher::CompleteInstall(webstore_install::Result result,
229 if (error.empty()) { 393 const std::string& error) {
230 const Extension* extension = 394 if (result == webstore_install::SUCCESS)
231 ExtensionRegistry::Get(profile()) 395 MaybeLaunchApp();
232 ->GetExtensionById(id(), ExtensionRegistry::ENABLED); 396 else if (!launch_callback_.is_null())
233 if (extension) 397 InvokeCallback(result, error);
234 LaunchApp(extension);
235 }
236 398
237 WebstoreStandaloneInstaller::CompleteInstall(error); 399 WebstoreStandaloneInstaller::CompleteInstall(result, error);
238 } 400 }
239 401
240 void EphemeralAppLauncher::WebContentsDestroyed() { 402 void EphemeralAppLauncher::WebContentsDestroyed() {
403 launch_callback_.Reset();
241 AbortInstall(); 404 AbortInstall();
242 } 405 }
243 406
244 void EphemeralAppLauncher::ExtensionEnableFlowFinished() { 407 void EphemeralAppLauncher::ExtensionEnableFlowFinished() {
245 CompleteInstall(std::string()); 408 MaybeLaunchApp();
409 Release(); // Matches the AddRef in EnableInstalledApp().
246 } 410 }
247 411
248 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) { 412 void EphemeralAppLauncher::ExtensionEnableFlowAborted(bool user_initiated) {
249 CompleteInstall(kLaunchAbortedError); 413 InvokeCallback(webstore_install::USER_CANCELLED, kUserCancelledError);
414 Release(); // Matches the AddRef in EnableInstalledApp().
250 } 415 }
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