blob: 11405b7af4401b79c99a12584ff8022ca506312c [file] [log] [blame]
[email protected]e3c0bc22012-02-24 01:34:151// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]502e39612011-03-26 01:36:282// 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/extensions/extension_uninstall_dialog.h"
6
[email protected]0d9a1da82013-03-14 21:52:077#include "base/bind.h"
[email protected]502e39612011-03-26 01:36:288#include "base/logging.h"
[email protected]b19fe572013-07-18 04:54:269#include "base/message_loop/message_loop.h"
rdevlin.cronin4f01c5f12015-05-07 17:52:0010#include "base/metrics/histogram_macros.h"
[email protected]226d79ad2014-03-14 21:34:5711#include "base/strings/utf_string_conversions.h"
khmel0df822022017-06-23 02:02:5612#include "chrome/browser/extensions/chrome_app_icon_service.h"
rdevlin.croninac8ce8c2015-05-27 23:00:0213#include "chrome/browser/extensions/extension_service.h"
[email protected]7eb20e32014-04-30 08:50:5614#include "chrome/browser/extensions/extension_util.h"
[email protected]472522b2013-10-25 00:41:2815#include "chrome/browser/profiles/profile.h"
rdevlin.croninf3af70d52015-03-20 03:55:0416#include "chrome/browser/ui/browser_navigator.h"
thestige80821242015-09-30 23:46:0817#include "chrome/browser/ui/browser_navigator_params.h"
khmel0df822022017-06-23 02:02:5618#include "chrome/browser/ui/native_window_tracker.h"
[email protected]af39f002014-08-22 10:18:1819#include "chrome/grit/generated_resources.h"
rdevlin.cronin66209492015-06-10 20:44:0520#include "extensions/browser/extension_dialog_auto_confirm.h"
[email protected]e0ebb8d2014-05-21 20:26:2721#include "extensions/browser/extension_registry.h"
rdevlin.croninac8ce8c2015-05-27 23:00:0222#include "extensions/browser/extension_system.h"
[email protected]326e6f02014-06-20 04:53:3723#include "extensions/browser/image_loader.h"
[email protected]cda103d2014-04-04 16:22:3924#include "extensions/common/constants.h"
[email protected]e4452d32013-11-15 23:07:4125#include "extensions/common/extension.h"
rdevlin.croninf3af70d52015-03-20 03:55:0426#include "extensions/common/extension_urls.h"
[email protected]0db486f2014-04-09 19:32:2227#include "extensions/common/manifest_handlers/icons_handler.h"
rdevlin.cronindbb8fa52015-05-06 00:16:5628#include "extensions/common/manifest_url_handlers.h"
[email protected]226d79ad2014-03-14 21:34:5729#include "ui/base/l10n/l10n_util.h"
khmel0df822022017-06-23 02:02:5630#include "ui/base/layout.h"
rdevlin.croninf3af70d52015-03-20 03:55:0431#include "ui/base/page_transition_types.h"
32#include "ui/base/window_open_disposition.h"
khmel0df822022017-06-23 02:02:5633#include "ui/display/display.h"
34#include "ui/display/screen.h"
[email protected]bdd6eec2012-03-03 19:58:0635#include "ui/gfx/image/image.h"
[email protected]50b66262013-09-24 03:25:4836#include "ui/gfx/image/image_skia.h"
[email protected]502e39612011-03-26 01:36:2837
[email protected]d4050672014-06-04 09:18:4038namespace extensions {
39
[email protected]dd46a4ce2012-09-15 10:50:5040namespace {
41
khmel0df822022017-06-23 02:02:5642constexpr int kIconSize = 64;
43
44constexpr char kExtensionRemovedError[] =
rdevlin.croninac8ce8c2015-05-27 23:00:0245 "Extension was removed before dialog closed.";
46
khmel0df822022017-06-23 02:02:5647constexpr char kReferrerId[] = "chrome-remove-extension-dialog";
rdevlin.croninde23cac2015-07-15 23:51:3948
khmel0df822022017-06-23 02:02:5649float GetScaleFactor(gfx::NativeWindow window) {
50 const display::Screen* screen = display::Screen::GetScreen();
51 if (!screen)
52 return 1.0; // Happens in unit_tests.
53 if (window)
54 return screen->GetDisplayNearestWindow(window).device_scale_factor();
55 return screen->GetPrimaryDisplay().device_scale_factor();
[email protected]dd46a4ce2012-09-15 10:50:5056}
57
58} // namespace
59
[email protected]6f03db062011-09-22 20:37:1460ExtensionUninstallDialog::ExtensionUninstallDialog(
[email protected]520414b12013-01-22 19:27:3661 Profile* profile,
khmel0df822022017-06-23 02:02:5662 gfx::NativeWindow parent,
[email protected]6f03db062011-09-22 20:37:1463 ExtensionUninstallDialog::Delegate* delegate)
khmel0df822022017-06-23 02:02:5664 : profile_(profile), parent_(parent), delegate_(delegate), observer_(this) {
65 if (parent)
66 parent_window_tracker_ = NativeWindowTracker::Create(parent);
[email protected]5f1ba412012-06-25 20:10:5267}
[email protected]502e39612011-03-26 01:36:2868
khmel0df822022017-06-23 02:02:5669ExtensionUninstallDialog::~ExtensionUninstallDialog() = default;
[email protected]502e39612011-03-26 01:36:2870
rdevlin.croninac8ce8c2015-05-27 23:00:0271void ExtensionUninstallDialog::ConfirmUninstallByExtension(
72 const scoped_refptr<const Extension>& extension,
73 const scoped_refptr<const Extension>& triggering_extension,
rdevlin.cronine18eb8cd2015-07-16 16:31:2874 UninstallReason reason,
75 UninstallSource source) {
[email protected]226d79ad2014-03-14 21:34:5776 triggering_extension_ = triggering_extension;
rdevlin.cronine18eb8cd2015-07-16 16:31:2877 ConfirmUninstall(extension, reason, source);
[email protected]226d79ad2014-03-14 21:34:5778}
79
rdevlin.croninac8ce8c2015-05-27 23:00:0280void ExtensionUninstallDialog::ConfirmUninstall(
81 const scoped_refptr<const Extension>& extension,
rdevlin.cronine18eb8cd2015-07-16 16:31:2882 UninstallReason reason,
83 UninstallSource source) {
rdevlin.croninac8ce8c2015-05-27 23:00:0284 DCHECK(thread_checker_.CalledOnValidThread());
rdevlin.cronine18eb8cd2015-07-16 16:31:2885
86 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallSource", source,
87 NUM_UNINSTALL_SOURCES);
88
[email protected]502e39612011-03-26 01:36:2889 extension_ = extension;
rdevlin.croninac8ce8c2015-05-27 23:00:0290 uninstall_reason_ = reason;
[email protected]eed85492013-01-14 20:37:4691
khmel0df822022017-06-23 02:02:5692 if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
93 OnDialogClosed(CLOSE_ACTION_CANCELED);
[email protected]e0ebb8d2014-05-21 20:26:2794 return;
95 }
96
khmel0df822022017-06-23 02:02:5697 // Track that extension uninstalled externally.
98 DCHECK(!observer_.IsObserving(ExtensionRegistry::Get(profile_)));
99 observer_.Add(ExtensionRegistry::Get(profile_));
100
101 // Dialog will be shown once icon is loaded.
102 DCHECK(!dialog_shown_);
103 icon_ = ChromeAppIconService::Get(profile_)->CreateIcon(this, extension->id(),
104 kIconSize);
105 icon_->image_skia().GetRepresentation(GetScaleFactor(parent_));
106}
107
108void ExtensionUninstallDialog::OnIconUpdated(ChromeAppIcon* icon) {
109 // Ignore initial update.
110 if (!icon_ || dialog_shown_)
111 return;
112
113 DCHECK_EQ(icon, icon_.get());
114
115 dialog_shown_ = true;
116
117 if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
118 OnDialogClosed(CLOSE_ACTION_CANCELED);
119 return;
120 }
rdevlin.croninac8ce8c2015-05-27 23:00:02121
rdevlin.cronin66209492015-06-10 20:44:05122 switch (ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
123 case ScopedTestDialogAutoConfirm::NONE:
rdevlin.croninac8ce8c2015-05-27 23:00:02124 Show();
125 break;
catmullingsff559d92017-05-20 01:43:36126 case ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
127 OnDialogClosed(CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE);
128 break;
rdevlin.cronin66209492015-06-10 20:44:05129 case ScopedTestDialogAutoConfirm::ACCEPT:
rdevlin.croninac8ce8c2015-05-27 23:00:02130 OnDialogClosed(CLOSE_ACTION_UNINSTALL);
131 break;
rdevlin.cronin66209492015-06-10 20:44:05132 case ScopedTestDialogAutoConfirm::CANCEL:
rdevlin.croninac8ce8c2015-05-27 23:00:02133 OnDialogClosed(CLOSE_ACTION_CANCELED);
134 break;
135 }
[email protected]5f1ba412012-06-25 20:10:52136}
[email protected]226d79ad2014-03-14 21:34:57137
khmel0df822022017-06-23 02:02:56138void ExtensionUninstallDialog::OnExtensionUninstalled(
139 content::BrowserContext* browser_context,
140 const Extension* extension,
141 UninstallReason reason) {
142 // Handle the case when extension was uninstalled externally and we have to
143 // close current dialog.
144 if (extension != extension_)
145 return;
146
147 delegate_->OnExtensionUninstallDialogClosed(
148 false, base::ASCIIToUTF16(kExtensionRemovedError));
149}
150
[email protected]226d79ad2014-03-14 21:34:57151std::string ExtensionUninstallDialog::GetHeadingText() {
152 if (triggering_extension_) {
153 return l10n_util::GetStringFUTF8(
154 IDS_EXTENSION_PROGRAMMATIC_UNINSTALL_PROMPT_HEADING,
155 base::UTF8ToUTF16(triggering_extension_->name()),
156 base::UTF8ToUTF16(extension_->name()));
157 }
158 return l10n_util::GetStringFUTF8(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
159 base::UTF8ToUTF16(extension_->name()));
160}
[email protected]d4050672014-06-04 09:18:40161
rdevlin.croninf3af70d52015-03-20 03:55:04162bool ExtensionUninstallDialog::ShouldShowReportAbuseCheckbox() const {
rdevlin.cronin903676442015-05-15 18:34:10163 return ManifestURL::UpdatesFromGallery(extension_.get());
rdevlin.croninf3af70d52015-03-20 03:55:04164}
165
rdevlin.cronin4f01c5f12015-05-07 17:52:00166void ExtensionUninstallDialog::OnDialogClosed(CloseAction action) {
167 // We don't want to artificially weight any of the options, so only record if
168 // reporting abuse was available.
169 if (ShouldShowReportAbuseCheckbox()) {
170 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallDialogAction",
171 action,
172 CLOSE_ACTION_LAST);
173 }
rdevlin.croninac8ce8c2015-05-27 23:00:02174
175 bool success = false;
176 base::string16 error;
177 switch (action) {
178 case CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE:
catmullingsff559d92017-05-20 01:43:36179 // If the extension specifies a custom uninstall page via
180 // chrome.runtime.setUninstallURL, then at uninstallation its uninstall
181 // page opens. To ensure that the CWS Report Abuse page is the active tab
182 // at uninstallation, HandleReportAbuse() is called after Uninstall().
183 success = Uninstall(&error);
rdevlin.croninac8ce8c2015-05-27 23:00:02184 HandleReportAbuse();
catmullingsff559d92017-05-20 01:43:36185 break;
rdevlin.croninac8ce8c2015-05-27 23:00:02186 case CLOSE_ACTION_UNINSTALL: {
catmullingsff559d92017-05-20 01:43:36187 success = Uninstall(&error);
rdevlin.croninac8ce8c2015-05-27 23:00:02188 break;
189 }
190 case CLOSE_ACTION_CANCELED:
191 error = base::ASCIIToUTF16("User canceled uninstall dialog");
192 break;
193 case CLOSE_ACTION_LAST:
194 NOTREACHED();
195 }
rdevlin.croninac8ce8c2015-05-27 23:00:02196 delegate_->OnExtensionUninstallDialogClosed(success, error);
rdevlin.cronin4f01c5f12015-05-07 17:52:00197}
198
catmullingsff559d92017-05-20 01:43:36199bool ExtensionUninstallDialog::Uninstall(base::string16* error) {
200 const Extension* current_extension =
201 ExtensionRegistry::Get(profile_)->GetExtensionById(
202 extension_->id(), ExtensionRegistry::EVERYTHING);
203 if (current_extension) {
khmel0df822022017-06-23 02:02:56204 // Prevent notifications triggered by our request.
205 observer_.RemoveAll();
catmullingsff559d92017-05-20 01:43:36206 return ExtensionSystem::Get(profile_)
207 ->extension_service()
208 ->UninstallExtension(extension_->id(), uninstall_reason_,
209 base::Bind(&base::DoNothing), error);
210 }
211 *error = base::ASCIIToUTF16(kExtensionRemovedError);
212 return false;
213}
214
rdevlin.croninf3af70d52015-03-20 03:55:04215void ExtensionUninstallDialog::HandleReportAbuse() {
216 chrome::NavigateParams params(
217 profile_,
rdevlin.croninde23cac2015-07-15 23:51:39218 extension_urls::GetWebstoreReportAbuseUrl(extension_->id(), kReferrerId),
rdevlin.croninf3af70d52015-03-20 03:55:04219 ui::PAGE_TRANSITION_LINK);
nick3b04f322016-08-31 19:29:19220 params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
rdevlin.croninf3af70d52015-03-20 03:55:04221 chrome::Navigate(&params);
222}
223
[email protected]d4050672014-06-04 09:18:40224} // namespace extensions