blob: 1ccb799c1ef36ee0f0a9f21155c6ea149317d022 [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
Bettina Dea7264a04b62018-04-06 04:27:4558ExtensionUninstallDialog::OnWillShowCallback* g_on_will_show_callback = nullptr;
[email protected]dd46a4ce2012-09-15 10:50:5059} // namespace
60
Bettina Dea7264a04b62018-04-06 04:27:4561void ExtensionUninstallDialog::SetOnShownCallbackForTesting(
62 ExtensionUninstallDialog::OnWillShowCallback* callback) {
63 g_on_will_show_callback = callback;
64}
65
[email protected]6f03db062011-09-22 20:37:1466ExtensionUninstallDialog::ExtensionUninstallDialog(
[email protected]520414b12013-01-22 19:27:3667 Profile* profile,
khmel0df822022017-06-23 02:02:5668 gfx::NativeWindow parent,
[email protected]6f03db062011-09-22 20:37:1469 ExtensionUninstallDialog::Delegate* delegate)
khmel0df822022017-06-23 02:02:5670 : profile_(profile), parent_(parent), delegate_(delegate), observer_(this) {
71 if (parent)
72 parent_window_tracker_ = NativeWindowTracker::Create(parent);
[email protected]5f1ba412012-06-25 20:10:5273}
[email protected]502e39612011-03-26 01:36:2874
khmel0df822022017-06-23 02:02:5675ExtensionUninstallDialog::~ExtensionUninstallDialog() = default;
[email protected]502e39612011-03-26 01:36:2876
rdevlin.croninac8ce8c2015-05-27 23:00:0277void ExtensionUninstallDialog::ConfirmUninstallByExtension(
78 const scoped_refptr<const Extension>& extension,
79 const scoped_refptr<const Extension>& triggering_extension,
rdevlin.cronine18eb8cd2015-07-16 16:31:2880 UninstallReason reason,
81 UninstallSource source) {
[email protected]226d79ad2014-03-14 21:34:5782 triggering_extension_ = triggering_extension;
rdevlin.cronine18eb8cd2015-07-16 16:31:2883 ConfirmUninstall(extension, reason, source);
[email protected]226d79ad2014-03-14 21:34:5784}
85
rdevlin.croninac8ce8c2015-05-27 23:00:0286void ExtensionUninstallDialog::ConfirmUninstall(
87 const scoped_refptr<const Extension>& extension,
rdevlin.cronine18eb8cd2015-07-16 16:31:2888 UninstallReason reason,
89 UninstallSource source) {
rdevlin.croninac8ce8c2015-05-27 23:00:0290 DCHECK(thread_checker_.CalledOnValidThread());
rdevlin.cronine18eb8cd2015-07-16 16:31:2891
92 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallSource", source,
93 NUM_UNINSTALL_SOURCES);
94
[email protected]502e39612011-03-26 01:36:2895 extension_ = extension;
rdevlin.croninac8ce8c2015-05-27 23:00:0296 uninstall_reason_ = reason;
[email protected]eed85492013-01-14 20:37:4697
khmel0df822022017-06-23 02:02:5698 if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
99 OnDialogClosed(CLOSE_ACTION_CANCELED);
[email protected]e0ebb8d2014-05-21 20:26:27100 return;
101 }
102
khmel0df822022017-06-23 02:02:56103 // Track that extension uninstalled externally.
104 DCHECK(!observer_.IsObserving(ExtensionRegistry::Get(profile_)));
105 observer_.Add(ExtensionRegistry::Get(profile_));
106
107 // Dialog will be shown once icon is loaded.
108 DCHECK(!dialog_shown_);
109 icon_ = ChromeAppIconService::Get(profile_)->CreateIcon(this, extension->id(),
110 kIconSize);
111 icon_->image_skia().GetRepresentation(GetScaleFactor(parent_));
112}
113
114void ExtensionUninstallDialog::OnIconUpdated(ChromeAppIcon* icon) {
115 // Ignore initial update.
116 if (!icon_ || dialog_shown_)
117 return;
khmel0df822022017-06-23 02:02:56118 DCHECK_EQ(icon, icon_.get());
119
120 dialog_shown_ = true;
121
122 if (parent() && parent_window_tracker_->WasNativeWindowClosed()) {
123 OnDialogClosed(CLOSE_ACTION_CANCELED);
124 return;
125 }
rdevlin.croninac8ce8c2015-05-27 23:00:02126
Bettina Dea7264a04b62018-04-06 04:27:45127 if (g_on_will_show_callback != nullptr)
128 g_on_will_show_callback->Run(this);
129
rdevlin.cronin66209492015-06-10 20:44:05130 switch (ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
131 case ScopedTestDialogAutoConfirm::NONE:
rdevlin.croninac8ce8c2015-05-27 23:00:02132 Show();
133 break;
catmullingsff559d92017-05-20 01:43:36134 case ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
135 OnDialogClosed(CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE);
136 break;
rdevlin.cronin66209492015-06-10 20:44:05137 case ScopedTestDialogAutoConfirm::ACCEPT:
rdevlin.croninac8ce8c2015-05-27 23:00:02138 OnDialogClosed(CLOSE_ACTION_UNINSTALL);
139 break;
rdevlin.cronin66209492015-06-10 20:44:05140 case ScopedTestDialogAutoConfirm::CANCEL:
rdevlin.croninac8ce8c2015-05-27 23:00:02141 OnDialogClosed(CLOSE_ACTION_CANCELED);
142 break;
143 }
[email protected]5f1ba412012-06-25 20:10:52144}
[email protected]226d79ad2014-03-14 21:34:57145
khmel0df822022017-06-23 02:02:56146void ExtensionUninstallDialog::OnExtensionUninstalled(
147 content::BrowserContext* browser_context,
148 const Extension* extension,
149 UninstallReason reason) {
150 // Handle the case when extension was uninstalled externally and we have to
151 // close current dialog.
152 if (extension != extension_)
153 return;
154
155 delegate_->OnExtensionUninstallDialogClosed(
156 false, base::ASCIIToUTF16(kExtensionRemovedError));
157}
158
[email protected]226d79ad2014-03-14 21:34:57159std::string ExtensionUninstallDialog::GetHeadingText() {
160 if (triggering_extension_) {
161 return l10n_util::GetStringFUTF8(
162 IDS_EXTENSION_PROGRAMMATIC_UNINSTALL_PROMPT_HEADING,
163 base::UTF8ToUTF16(triggering_extension_->name()),
164 base::UTF8ToUTF16(extension_->name()));
165 }
166 return l10n_util::GetStringFUTF8(IDS_EXTENSION_UNINSTALL_PROMPT_HEADING,
167 base::UTF8ToUTF16(extension_->name()));
168}
[email protected]d4050672014-06-04 09:18:40169
rdevlin.croninf3af70d52015-03-20 03:55:04170bool ExtensionUninstallDialog::ShouldShowReportAbuseCheckbox() const {
rdevlin.cronin903676442015-05-15 18:34:10171 return ManifestURL::UpdatesFromGallery(extension_.get());
rdevlin.croninf3af70d52015-03-20 03:55:04172}
173
rdevlin.cronin4f01c5f12015-05-07 17:52:00174void ExtensionUninstallDialog::OnDialogClosed(CloseAction action) {
175 // We don't want to artificially weight any of the options, so only record if
176 // reporting abuse was available.
177 if (ShouldShowReportAbuseCheckbox()) {
178 UMA_HISTOGRAM_ENUMERATION("Extensions.UninstallDialogAction",
179 action,
180 CLOSE_ACTION_LAST);
181 }
rdevlin.croninac8ce8c2015-05-27 23:00:02182
183 bool success = false;
184 base::string16 error;
185 switch (action) {
186 case CLOSE_ACTION_UNINSTALL_AND_REPORT_ABUSE:
catmullingsff559d92017-05-20 01:43:36187 // If the extension specifies a custom uninstall page via
188 // chrome.runtime.setUninstallURL, then at uninstallation its uninstall
189 // page opens. To ensure that the CWS Report Abuse page is the active tab
190 // at uninstallation, HandleReportAbuse() is called after Uninstall().
191 success = Uninstall(&error);
rdevlin.croninac8ce8c2015-05-27 23:00:02192 HandleReportAbuse();
catmullingsff559d92017-05-20 01:43:36193 break;
rdevlin.croninac8ce8c2015-05-27 23:00:02194 case CLOSE_ACTION_UNINSTALL: {
catmullingsff559d92017-05-20 01:43:36195 success = Uninstall(&error);
rdevlin.croninac8ce8c2015-05-27 23:00:02196 break;
197 }
198 case CLOSE_ACTION_CANCELED:
199 error = base::ASCIIToUTF16("User canceled uninstall dialog");
200 break;
201 case CLOSE_ACTION_LAST:
202 NOTREACHED();
203 }
rdevlin.croninac8ce8c2015-05-27 23:00:02204 delegate_->OnExtensionUninstallDialogClosed(success, error);
rdevlin.cronin4f01c5f12015-05-07 17:52:00205}
206
catmullingsff559d92017-05-20 01:43:36207bool ExtensionUninstallDialog::Uninstall(base::string16* error) {
208 const Extension* current_extension =
209 ExtensionRegistry::Get(profile_)->GetExtensionById(
210 extension_->id(), ExtensionRegistry::EVERYTHING);
211 if (current_extension) {
khmel0df822022017-06-23 02:02:56212 // Prevent notifications triggered by our request.
213 observer_.RemoveAll();
catmullingsff559d92017-05-20 01:43:36214 return ExtensionSystem::Get(profile_)
215 ->extension_service()
Devlin Cronin218df7f2017-11-21 21:41:31216 ->UninstallExtension(extension_->id(), uninstall_reason_, error);
catmullingsff559d92017-05-20 01:43:36217 }
218 *error = base::ASCIIToUTF16(kExtensionRemovedError);
219 return false;
220}
221
rdevlin.croninf3af70d52015-03-20 03:55:04222void ExtensionUninstallDialog::HandleReportAbuse() {
cm.sanchi2522bc92017-12-04 08:04:13223 NavigateParams params(
rdevlin.croninf3af70d52015-03-20 03:55:04224 profile_,
rdevlin.croninde23cac2015-07-15 23:51:39225 extension_urls::GetWebstoreReportAbuseUrl(extension_->id(), kReferrerId),
rdevlin.croninf3af70d52015-03-20 03:55:04226 ui::PAGE_TRANSITION_LINK);
nick3b04f322016-08-31 19:29:19227 params.disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
cm.sanchi2522bc92017-12-04 08:04:13228 Navigate(&params);
rdevlin.croninf3af70d52015-03-20 03:55:04229}
230
[email protected]d4050672014-06-04 09:18:40231} // namespace extensions