blob: 760cb00eb281ea0a003aa3204c8fe4e7cd83727c [file] [log] [blame]
[email protected]bdb74a22012-01-25 20:33:331// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]8915f342011-08-29 22:14:372// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]734bcec2012-10-08 20:29:055#include "chrome/browser/extensions/webstore_standalone_installer.h"
[email protected]8915f342011-08-29 22:14:376
[email protected]8915f342011-08-29 22:14:377#include "base/values.h"
[email protected]9c7b3092014-06-21 01:19:038#include "base/version.h"
[email protected]8915f342011-08-29 22:14:379#include "chrome/browser/extensions/crx_installer.h"
[email protected]d44ec7b2013-03-15 04:34:3410#include "chrome/browser/extensions/extension_install_prompt.h"
[email protected]453ac122014-07-28 03:32:1311#include "chrome/browser/extensions/extension_install_ui_util.h"
[email protected]655b2b1a2011-10-13 17:13:0612#include "chrome/browser/extensions/extension_service.h"
[email protected]773272b2014-07-18 05:48:3513#include "chrome/browser/extensions/install_tracker.h"
[email protected]7b3941052013-02-13 01:21:5914#include "chrome/browser/extensions/webstore_data_fetcher.h"
[email protected]8915f342011-08-29 22:14:3715#include "chrome/browser/profiles/profile.h"
[email protected]fdd28372014-08-21 02:27:2616#include "components/crx_file/id_util.h"
[email protected]10c2d692012-05-11 05:32:2317#include "content/public/browser/web_contents.h"
[email protected]489db0842014-01-22 18:20:0318#include "extensions/browser/extension_prefs.h"
[email protected]760f743b2014-05-28 13:52:0219#include "extensions/browser/extension_registry.h"
[email protected]59b0e602014-01-30 00:41:2420#include "extensions/browser/extension_system.h"
[email protected]411f8ae2014-05-22 11:12:2321#include "extensions/browser/extension_util.h"
[email protected]e4452d32013-11-15 23:07:4122#include "extensions/common/extension.h"
rockot90659852014-09-18 19:31:5223#include "extensions/common/extension_urls.h"
[email protected]a6483d22013-07-03 22:11:0024#include "url/gurl.h"
[email protected]8915f342011-08-29 22:14:3725
[email protected]26b5e322011-12-23 01:36:4726using content::WebContents;
[email protected]631bb742011-11-02 11:29:3927
[email protected]3d61a7f2012-07-12 19:11:2528namespace extensions {
29
[email protected]a221ef092011-09-07 01:34:1030const char kInvalidWebstoreItemId[] = "Invalid Chrome Web Store item ID";
31const char kWebstoreRequestError[] =
32 "Could not fetch data from the Chrome Web Store";
33const char kInvalidWebstoreResponseError[] = "Invalid Chrome Web Store reponse";
[email protected]8915f342011-08-29 22:14:3734const char kInvalidManifestError[] = "Invalid manifest";
35const char kUserCancelledError[] = "User cancelled install";
[email protected]f6b88d92014-01-09 19:45:3436const char kExtensionIsBlacklisted[] = "Extension is blacklisted";
[email protected]773272b2014-07-18 05:48:3537const char kInstallInProgressError[] = "An install is already in progress";
[email protected]8915f342011-08-29 22:14:3738
[email protected]734bcec2012-10-08 20:29:0539WebstoreStandaloneInstaller::WebstoreStandaloneInstaller(
[email protected]d44ec7b2013-03-15 04:34:3440 const std::string& webstore_item_id,
[email protected]c1b2d042013-02-23 00:31:0441 Profile* profile,
[email protected]c1b2d042013-02-23 00:31:0442 const Callback& callback)
[email protected]d44ec7b2013-03-15 04:34:3443 : id_(webstore_item_id),
[email protected]d2a639e2012-09-17 07:41:2144 callback_(callback),
[email protected]d44ec7b2013-03-15 04:34:3445 profile_(profile),
[email protected]084e35482013-09-25 02:46:1946 install_source_(WebstoreInstaller::INSTALL_SOURCE_INLINE),
[email protected]dcde34b32013-07-31 02:28:4547 show_user_count_(true),
[email protected]c7bf7452011-09-12 21:31:5048 average_rating_(0.0),
[email protected]5f2a4752012-04-27 22:18:5849 rating_count_(0) {
[email protected]c1b2d042013-02-23 00:31:0450}
51
[email protected]734bcec2012-10-08 20:29:0552void WebstoreStandaloneInstaller::BeginInstall() {
[email protected]3d6d676522013-10-14 20:44:5553 // Add a ref to keep this alive for WebstoreDataFetcher.
54 // All code paths from here eventually lead to either CompleteInstall or
55 // AbortInstall, which both release this ref.
56 AddRef();
[email protected]8915f342011-08-29 22:14:3757
[email protected]fdd28372014-08-21 02:27:2658 if (!crx_file::id_util::IdIsValid(id_)) {
[email protected]9c7b3092014-06-21 01:19:0359 CompleteInstall(webstore_install::INVALID_ID, kInvalidWebstoreItemId);
[email protected]8915f342011-08-29 22:14:3760 return;
61 }
62
[email protected]ced522c2014-07-23 20:23:5963 webstore_install::Result result = webstore_install::OTHER_ERROR;
[email protected]773272b2014-07-18 05:48:3564 std::string error;
65 if (!EnsureUniqueInstall(&result, &error)) {
66 CompleteInstall(result, error);
67 return;
68 }
69
[email protected]b4574c02011-11-17 06:19:1370 // Use the requesting page as the referrer both since that is more correct
71 // (it is the page that caused this request to happen) and so that we can
72 // track top sites that trigger inline install requests.
[email protected]7b3941052013-02-13 01:21:5973 webstore_data_fetcher_.reset(new WebstoreDataFetcher(
74 this,
[email protected]c1b2d042013-02-23 00:31:0475 profile_->GetRequestContext(),
[email protected]d44ec7b2013-03-15 04:34:3476 GetRequestorURL(),
[email protected]7b3941052013-02-13 01:21:5977 id_));
78 webstore_data_fetcher_->Start();
[email protected]8915f342011-08-29 22:14:3779}
80
[email protected]9c7b3092014-06-21 01:19:0381//
82// Private interface implementation.
83//
84
85WebstoreStandaloneInstaller::~WebstoreStandaloneInstaller() {
86}
87
rdevlin.cronin5f6b69d2014-09-20 01:23:3588void WebstoreStandaloneInstaller::RunCallback(bool success,
89 const std::string& error,
90 webstore_install::Result result) {
91 callback_.Run(success, error, result);
92}
93
[email protected]9c7b3092014-06-21 01:19:0394void WebstoreStandaloneInstaller::AbortInstall() {
95 callback_.Reset();
96 // Abort any in-progress fetches.
97 if (webstore_data_fetcher_) {
98 webstore_data_fetcher_.reset();
[email protected]773272b2014-07-18 05:48:3599 scoped_active_install_.reset();
[email protected]9c7b3092014-06-21 01:19:03100 Release(); // Matches the AddRef in BeginInstall.
101 }
102}
103
[email protected]773272b2014-07-18 05:48:35104bool WebstoreStandaloneInstaller::EnsureUniqueInstall(
105 webstore_install::Result* reason,
106 std::string* error) {
107 InstallTracker* tracker = InstallTracker::Get(profile_);
108 DCHECK(tracker);
109
110 const ActiveInstallData* existing_install_data =
111 tracker->GetActiveInstall(id_);
112 if (existing_install_data) {
benwellscb4422c12015-10-13 23:38:46113 *reason = webstore_install::INSTALL_IN_PROGRESS;
114 *error = kInstallInProgressError;
[email protected]773272b2014-07-18 05:48:35115 return false;
116 }
117
118 ActiveInstallData install_data(id_);
119 InitInstallData(&install_data);
120 scoped_active_install_.reset(new ScopedActiveInstall(tracker, install_data));
121 return true;
122}
123
[email protected]9c7b3092014-06-21 01:19:03124void WebstoreStandaloneInstaller::CompleteInstall(
125 webstore_install::Result result,
126 const std::string& error) {
[email protected]773272b2014-07-18 05:48:35127 scoped_active_install_.reset();
[email protected]9c7b3092014-06-21 01:19:03128 if (!callback_.is_null())
[email protected]ced522c2014-07-23 20:23:59129 callback_.Run(result == webstore_install::SUCCESS, error, result);
[email protected]9c7b3092014-06-21 01:19:03130 Release(); // Matches the AddRef in BeginInstall.
131}
132
133void WebstoreStandaloneInstaller::ProceedWithInstallPrompt() {
134 install_prompt_ = CreateInstallPrompt();
dchengc7047942014-08-26 05:05:31135 if (install_prompt_.get()) {
[email protected]9c7b3092014-06-21 01:19:03136 ShowInstallUI();
137 // Control flow finishes up in InstallUIProceed or InstallUIAbort.
138 } else {
139 InstallUIProceed();
140 }
141}
142
143scoped_refptr<const Extension>
144WebstoreStandaloneInstaller::GetLocalizedExtensionForDisplay() {
145 if (!localized_extension_for_display_.get()) {
146 DCHECK(manifest_.get());
147 if (!manifest_.get())
148 return NULL;
149
150 std::string error;
151 localized_extension_for_display_ =
152 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
153 manifest_.get(),
154 Extension::REQUIRE_KEY | Extension::FROM_WEBSTORE,
155 id_,
156 localized_name_,
157 localized_description_,
158 &error);
159 }
160 return localized_extension_for_display_.get();
161}
162
[email protected]773272b2014-07-18 05:48:35163void WebstoreStandaloneInstaller::InitInstallData(
164 ActiveInstallData* install_data) const {
165 // Default implementation sets no properties.
166}
167
[email protected]9c7b3092014-06-21 01:19:03168void WebstoreStandaloneInstaller::OnManifestParsed() {
169 ProceedWithInstallPrompt();
[email protected]ce35418b2013-11-25 01:22:33170}
171
[email protected]f8b23b42013-06-27 20:12:14172scoped_ptr<ExtensionInstallPrompt>
173WebstoreStandaloneInstaller::CreateInstallUI() {
174 return make_scoped_ptr(new ExtensionInstallPrompt(GetWebContents()));
175}
176
[email protected]ce35418b2013-11-25 01:22:33177scoped_ptr<WebstoreInstaller::Approval>
178WebstoreStandaloneInstaller::CreateApproval() const {
179 scoped_ptr<WebstoreInstaller::Approval> approval(
180 WebstoreInstaller::Approval::CreateWithNoInstallPrompt(
181 profile_,
182 id_,
183 scoped_ptr<base::DictionaryValue>(manifest_.get()->DeepCopy()),
184 true));
185 approval->skip_post_install_ui = !ShouldShowPostInstallUI();
186 approval->use_app_installed_bubble = ShouldShowAppInstalledBubble();
187 approval->installing_icon = gfx::ImageSkia::CreateFrom1xBitmap(icon_);
188 return approval.Pass();
189}
190
rdevlin.cronin5f6b69d2014-09-20 01:23:35191void WebstoreStandaloneInstaller::InstallUIProceed() {
192 if (!CheckRequestorAlive()) {
193 CompleteInstall(webstore_install::ABORTED, std::string());
194 return;
195 }
196
197 scoped_ptr<WebstoreInstaller::Approval> approval = CreateApproval();
198
199 ExtensionService* extension_service =
200 ExtensionSystem::Get(profile_)->extension_service();
201 const Extension* installed_extension =
202 extension_service->GetExtensionById(id_, true /* include disabled */);
203 if (installed_extension) {
204 std::string install_message;
205 webstore_install::Result install_result = webstore_install::SUCCESS;
206 bool done = true;
207
208 if (ExtensionPrefs::Get(profile_)->IsExtensionBlacklisted(id_)) {
209 // Don't install a blacklisted extension.
210 install_result = webstore_install::BLACKLISTED;
211 install_message = kExtensionIsBlacklisted;
benwellscb4422c12015-10-13 23:38:46212 } else if (util::IsEphemeralApp(installed_extension->id(), profile_)) {
rdevlin.cronin5f6b69d2014-09-20 01:23:35213 // If the target extension has already been installed ephemerally and is
214 // up to date, it can be promoted to a regular installed extension and
215 // downloading from the Web Store is not necessary.
216 scoped_refptr<const Extension> extension_to_install =
217 GetLocalizedExtensionForDisplay();
218 if (!extension_to_install.get()) {
219 CompleteInstall(webstore_install::INVALID_MANIFEST,
220 kInvalidManifestError);
221 return;
222 }
223
224 if (installed_extension->version()->CompareTo(
225 *extension_to_install->version()) < 0) {
226 // If the existing extension is out of date, proceed with the install
227 // to update the extension.
228 done = false;
229 } else {
230 install_ui::ShowPostInstallUIForApproval(
231 profile_, *approval, installed_extension);
232 extension_service->PromoteEphemeralApp(installed_extension, false);
233 }
234 } else if (!extension_service->IsExtensionEnabled(id_)) {
235 // If the extension is installed but disabled, and not blacklisted,
236 // enable it.
237 extension_service->EnableExtension(id_);
238 } // else extension is installed and enabled; no work to be done.
239
240 if (done) {
241 CompleteInstall(install_result, install_message);
242 return;
243 }
244 }
245
246 scoped_refptr<WebstoreInstaller> installer = new WebstoreInstaller(
247 profile_,
248 this,
249 GetWebContents(),
250 id_,
251 approval.Pass(),
252 install_source_);
253 installer->Start();
254}
255
256void WebstoreStandaloneInstaller::InstallUIAbort(bool user_initiated) {
257 CompleteInstall(webstore_install::USER_CANCELLED, kUserCancelledError);
258}
259
[email protected]7b3941052013-02-13 01:21:59260void WebstoreStandaloneInstaller::OnWebstoreRequestFailure() {
[email protected]e263a6b62014-06-05 23:19:49261 OnWebStoreDataFetcherDone();
[email protected]9c7b3092014-06-21 01:19:03262 CompleteInstall(webstore_install::WEBSTORE_REQUEST_ERROR,
263 kWebstoreRequestError);
[email protected]8915f342011-08-29 22:14:37264}
265
[email protected]734bcec2012-10-08 20:29:05266void WebstoreStandaloneInstaller::OnWebstoreResponseParseSuccess(
[email protected]cb1078de2013-12-23 20:04:22267 scoped_ptr<base::DictionaryValue> webstore_data) {
[email protected]e263a6b62014-06-05 23:19:49268 OnWebStoreDataFetcherDone();
269
[email protected]d44ec7b2013-03-15 04:34:34270 if (!CheckRequestorAlive()) {
[email protected]9c7b3092014-06-21 01:19:03271 CompleteInstall(webstore_install::ABORTED, std::string());
[email protected]4693243e2011-09-09 23:52:37272 return;
273 }
274
[email protected]d44ec7b2013-03-15 04:34:34275 std::string error;
[email protected]8915f342011-08-29 22:14:37276
[email protected]d44ec7b2013-03-15 04:34:34277 if (!CheckInlineInstallPermitted(*webstore_data, &error)) {
[email protected]9c7b3092014-06-21 01:19:03278 CompleteInstall(webstore_install::NOT_PERMITTED, error);
[email protected]8915f342011-08-29 22:14:37279 return;
280 }
281
[email protected]d44ec7b2013-03-15 04:34:34282 if (!CheckRequestorPermitted(*webstore_data, &error)) {
[email protected]9c7b3092014-06-21 01:19:03283 CompleteInstall(webstore_install::NOT_PERMITTED, error);
[email protected]dd5161a2011-09-14 17:40:17284 return;
285 }
286
287 // Manifest, number of users, average rating and rating count are required.
288 std::string manifest;
289 if (!webstore_data->GetString(kManifestKey, &manifest) ||
290 !webstore_data->GetString(kUsersKey, &localized_user_count_) ||
[email protected]5fdbd6f2011-09-01 17:33:04291 !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) ||
292 !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) {
[email protected]9c7b3092014-06-21 01:19:03293 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
294 kInvalidWebstoreResponseError);
[email protected]5fdbd6f2011-09-01 17:33:04295 return;
296 }
297
[email protected]dcde34b32013-07-31 02:28:45298 // Optional.
299 show_user_count_ = true;
300 webstore_data->GetBoolean(kShowUserCountKey, &show_user_count_);
301
[email protected]c82da8c42012-06-08 19:49:11302 if (average_rating_ < ExtensionInstallPrompt::kMinExtensionRating ||
303 average_rating_ > ExtensionInstallPrompt::kMaxExtensionRating) {
[email protected]9c7b3092014-06-21 01:19:03304 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
305 kInvalidWebstoreResponseError);
[email protected]5fdbd6f2011-09-01 17:33:04306 return;
307 }
308
309 // Localized name and description are optional.
310 if ((webstore_data->HasKey(kLocalizedNameKey) &&
311 !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) ||
312 (webstore_data->HasKey(kLocalizedDescriptionKey) &&
313 !webstore_data->GetString(
314 kLocalizedDescriptionKey, &localized_description_))) {
[email protected]9c7b3092014-06-21 01:19:03315 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
316 kInvalidWebstoreResponseError);
[email protected]8915f342011-08-29 22:14:37317 return;
318 }
319
320 // Icon URL is optional.
321 GURL icon_url;
322 if (webstore_data->HasKey(kIconUrlKey)) {
323 std::string icon_url_string;
324 if (!webstore_data->GetString(kIconUrlKey, &icon_url_string)) {
[email protected]9c7b3092014-06-21 01:19:03325 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
326 kInvalidWebstoreResponseError);
[email protected]8915f342011-08-29 22:14:37327 return;
328 }
329 icon_url = GURL(extension_urls::GetWebstoreLaunchURL()).Resolve(
330 icon_url_string);
331 if (!icon_url.is_valid()) {
[email protected]9c7b3092014-06-21 01:19:03332 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE,
333 kInvalidWebstoreResponseError);
[email protected]8915f342011-08-29 22:14:37334 return;
335 }
336 }
337
[email protected]d44ec7b2013-03-15 04:34:34338 // Assume ownership of webstore_data.
[email protected]b3188ec2013-11-06 10:26:13339 webstore_data_ = webstore_data.Pass();
[email protected]a221ef092011-09-07 01:34:10340
[email protected]007b3f82013-04-09 08:46:45341 scoped_refptr<WebstoreInstallHelper> helper =
342 new WebstoreInstallHelper(this,
343 id_,
344 manifest,
[email protected]007b3f82013-04-09 08:46:45345 icon_url,
346 profile_->GetRequestContext());
[email protected]8915f342011-08-29 22:14:37347 // The helper will call us back via OnWebstoreParseSucces or
348 // OnWebstoreParseFailure.
349 helper->Start();
350}
351
[email protected]734bcec2012-10-08 20:29:05352void WebstoreStandaloneInstaller::OnWebstoreResponseParseFailure(
[email protected]8915f342011-08-29 22:14:37353 const std::string& error) {
[email protected]e263a6b62014-06-05 23:19:49354 OnWebStoreDataFetcherDone();
[email protected]9c7b3092014-06-21 01:19:03355 CompleteInstall(webstore_install::INVALID_WEBSTORE_RESPONSE, error);
[email protected]8915f342011-08-29 22:14:37356}
357
[email protected]734bcec2012-10-08 20:29:05358void WebstoreStandaloneInstaller::OnWebstoreParseSuccess(
[email protected]98e4e522011-10-25 13:00:16359 const std::string& id,
[email protected]8915f342011-08-29 22:14:37360 const SkBitmap& icon,
361 base::DictionaryValue* manifest) {
[email protected]d44ec7b2013-03-15 04:34:34362 CHECK_EQ(id_, id);
363
364 if (!CheckRequestorAlive()) {
[email protected]9c7b3092014-06-21 01:19:03365 CompleteInstall(webstore_install::ABORTED, std::string());
[email protected]2fd920fb2011-09-08 23:33:00366 return;
367 }
368
[email protected]8915f342011-08-29 22:14:37369 manifest_.reset(manifest);
370 icon_ = icon;
371
[email protected]9c7b3092014-06-21 01:19:03372 OnManifestParsed();
[email protected]8915f342011-08-29 22:14:37373}
374
[email protected]734bcec2012-10-08 20:29:05375void WebstoreStandaloneInstaller::OnWebstoreParseFailure(
[email protected]98e4e522011-10-25 13:00:16376 const std::string& id,
[email protected]8915f342011-08-29 22:14:37377 InstallHelperResultCode result_code,
378 const std::string& error_message) {
[email protected]ced522c2014-07-23 20:23:59379 webstore_install::Result install_result = webstore_install::OTHER_ERROR;
[email protected]9c7b3092014-06-21 01:19:03380 switch (result_code) {
381 case WebstoreInstallHelper::Delegate::MANIFEST_ERROR:
382 install_result = webstore_install::INVALID_MANIFEST;
383 break;
384 case WebstoreInstallHelper::Delegate::ICON_ERROR:
385 install_result = webstore_install::ICON_ERROR;
386 break;
387 default:
388 break;
389 }
390
391 CompleteInstall(install_result, error_message);
[email protected]8915f342011-08-29 22:14:37392}
393
[email protected]734bcec2012-10-08 20:29:05394void WebstoreStandaloneInstaller::OnExtensionInstallSuccess(
395 const std::string& id) {
[email protected]cb08ba22011-10-19 21:41:40396 CHECK_EQ(id_, id);
[email protected]9c7b3092014-06-21 01:19:03397 CompleteInstall(webstore_install::SUCCESS, std::string());
[email protected]cb08ba22011-10-19 21:41:40398}
399
[email protected]734bcec2012-10-08 20:29:05400void WebstoreStandaloneInstaller::OnExtensionInstallFailure(
[email protected]bcd1eaf72012-10-03 05:42:29401 const std::string& id,
402 const std::string& error,
[email protected]9c7b3092014-06-21 01:19:03403 WebstoreInstaller::FailureReason reason) {
[email protected]cb08ba22011-10-19 21:41:40404 CHECK_EQ(id_, id);
[email protected]cb08ba22011-10-19 21:41:40405
[email protected]ced522c2014-07-23 20:23:59406 webstore_install::Result install_result = webstore_install::OTHER_ERROR;
[email protected]9c7b3092014-06-21 01:19:03407 switch (reason) {
408 case WebstoreInstaller::FAILURE_REASON_CANCELLED:
409 install_result = webstore_install::USER_CANCELLED;
410 break;
411 case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_FOUND:
412 case WebstoreInstaller::FAILURE_REASON_DEPENDENCY_NOT_SHARED_MODULE:
413 install_result = webstore_install::MISSING_DEPENDENCIES;
414 break;
415 default:
416 break;
[email protected]d44ec7b2013-03-15 04:34:34417 }
[email protected]d44ec7b2013-03-15 04:34:34418
[email protected]9c7b3092014-06-21 01:19:03419 CompleteInstall(install_result, error);
[email protected]8915f342011-08-29 22:14:37420}
[email protected]5f2a4752012-04-27 22:18:58421
[email protected]e263a6b62014-06-05 23:19:49422void WebstoreStandaloneInstaller::ShowInstallUI() {
dchengc7047942014-08-26 05:05:31423 scoped_refptr<const Extension> localized_extension =
424 GetLocalizedExtensionForDisplay();
dcheng40eae712014-08-27 16:56:10425 if (!localized_extension.get()) {
[email protected]9c7b3092014-06-21 01:19:03426 CompleteInstall(webstore_install::INVALID_MANIFEST, kInvalidManifestError);
[email protected]d44ec7b2013-03-15 04:34:34427 return;
[email protected]5f2a4752012-04-27 22:18:58428 }
[email protected]5f2a4752012-04-27 22:18:58429
[email protected]f8b23b42013-06-27 20:12:14430 install_ui_ = CreateInstallUI();
[email protected]dc24976f2013-06-02 21:15:09431 install_ui_->ConfirmStandaloneInstall(
dcheng40eae712014-08-27 16:56:10432 this, localized_extension.get(), &icon_, install_prompt_);
[email protected]5f2a4752012-04-27 22:18:58433}
[email protected]3d61a7f2012-07-12 19:11:25434
[email protected]e263a6b62014-06-05 23:19:49435void WebstoreStandaloneInstaller::OnWebStoreDataFetcherDone() {
436 // An instance of this class is passed in as a delegate for the
437 // WebstoreInstallHelper, ExtensionInstallPrompt and WebstoreInstaller, and
438 // therefore needs to remain alive until they are done. Clear the webstore
439 // data fetcher to avoid calling Release in AbortInstall while any of these
440 // operations are in progress.
441 webstore_data_fetcher_.reset();
442}
443
[email protected]3d61a7f2012-07-12 19:11:25444} // namespace extensions