blob: b03355f04f54ffe007d94a3899194e5117ea718f [file] [log] [blame]
[email protected]c82da8c42012-06-08 19:49:111// Copyright (c) 2012 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/extensions/extension_install_prompt.h"
6
dcheng1fc00f12015-12-26 22:18:037#include <utility>
8
Sebastien Marchandf1349f52019-01-25 03:16:419#include "base/bind.h"
skyostilf221b7de2015-06-11 20:36:3210#include "base/location.h"
fdoray283082bd2016-06-02 20:18:4611#include "base/single_thread_task_runner.h"
[email protected]3ea1b182013-02-08 22:38:4112#include "base/strings/string_number_conversions.h"
[email protected]00e7bef2013-06-10 20:35:1713#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1814#include "base/strings/utf_string_conversions.h"
gabb15e19072016-05-11 20:45:4115#include "base/threading/thread_task_runner_handle.h"
pkotwicz2f181782014-10-29 17:33:4516#include "chrome/browser/extensions/extension_install_prompt_show_params.h"
[email protected]7eb20e32014-04-30 08:50:5617#include "chrome/browser/extensions/extension_util.h"
gpdavis.chromium0fbac4d2014-09-19 20:57:5418#include "chrome/browser/extensions/permissions_updater.h"
[email protected]c82da8c42012-06-08 19:49:1119#include "chrome/browser/profiles/profile.h"
pkotwicza57a1f322014-10-21 00:24:3020#include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
[email protected]af39f002014-08-22 10:18:1821#include "chrome/grit/generated_resources.h"
thestig4a2e88e2016-08-27 23:23:5122#include "chrome/grit/theme_resources.h"
23#include "components/strings/grit/components_strings.h"
[email protected]91e51d612012-10-21 23:03:0524#include "content/public/browser/web_contents.h"
Devlin Croninbffe949eb2018-01-12 03:03:4025#include "extensions/browser/disable_reason.h"
rdevlin.cronin66209492015-06-10 20:44:0526#include "extensions/browser/extension_dialog_auto_confirm.h"
[email protected]21db9ef2014-05-16 02:06:2727#include "extensions/browser/extension_prefs.h"
[email protected]411f8ae2014-05-22 11:12:2328#include "extensions/browser/extension_util.h"
[email protected]326e6f02014-06-20 04:53:3729#include "extensions/browser/image_loader.h"
pkotwicza57a1f322014-10-21 00:24:3030#include "extensions/browser/install/extension_install_ui.h"
[email protected]e4452d32013-11-15 23:07:4131#include "extensions/common/extension.h"
Tim Judkinse9221872020-01-25 01:37:5332#include "extensions/common/extension_features.h"
[email protected]4b7908842014-04-07 23:50:2233#include "extensions/common/extension_icon_set.h"
[email protected]993da5e2013-03-23 21:25:1634#include "extensions/common/extension_resource.h"
[email protected]5ef835a2013-11-08 20:42:5735#include "extensions/common/feature_switch.h"
[email protected]d42c11152013-08-22 19:36:3236#include "extensions/common/manifest.h"
[email protected]6bf90612013-08-15 00:36:2737#include "extensions/common/manifest_constants.h"
[email protected]0db486f2014-04-09 19:32:2238#include "extensions/common/manifest_handlers/icons_handler.h"
treib7496f63c2015-03-04 12:18:5339#include "extensions/common/manifest_handlers/permissions_parser.h"
Danan S5f430882020-01-14 04:52:5540#include "extensions/common/permissions/permission_message.h"
treibf9dce312015-08-27 17:33:3541#include "extensions/common/permissions/permission_message_provider.h"
[email protected]5a55f3f2013-10-29 01:08:2942#include "extensions/common/permissions/permission_set.h"
[email protected]e4452d32013-11-15 23:07:4143#include "extensions/common/permissions/permissions_data.h"
[email protected]c82da8c42012-06-08 19:49:1144#include "ui/base/l10n/l10n_util.h"
45#include "ui/base/resource/resource_bundle.h"
pkotwicz7fd01192014-10-09 04:43:5046#include "ui/base/ui_base_types.h"
rdevlin.cronin3fe4bd32016-01-12 18:45:4047#include "ui/gfx/image/image_skia.h"
[email protected]c82da8c42012-06-08 19:49:1148
[email protected]c82da8c42012-06-08 19:49:1149using extensions::Extension;
[email protected]1d5e58b2013-01-31 08:41:4050using extensions::Manifest;
treibf9dce312015-08-27 17:33:3551using extensions::PermissionMessage;
52using extensions::PermissionMessages;
[email protected]c2e66e12012-06-27 06:27:0653using extensions::PermissionSet;
[email protected]c82da8c42012-06-08 19:49:1154
[email protected]612a1cb12012-10-17 13:18:0355namespace {
56
rdevlin.cronin5f6b69d2014-09-20 01:23:3557bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type) {
Benjamin Ackermanc4fb02302018-10-03 19:55:4158 return type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT ||
59 type == ExtensionInstallPrompt::REPAIR_PROMPT ||
60 type == ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT;
rdevlin.cronin5f6b69d2014-09-20 01:23:3561}
62
[email protected]dd46a4ce2012-09-15 10:50:5063// Returns bitmap for the default icon with size equal to the default icon's
64// pixel size under maximal supported scale factor.
65SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
[email protected]702d8b42013-02-27 20:55:5066 const gfx::ImageSkia& image = is_app ?
[email protected]7eb20e32014-04-30 08:50:5667 extensions::util::GetDefaultAppIcon() :
68 extensions::util::GetDefaultExtensionIcon();
Malay Keshave8c63be2018-10-02 00:12:2469 return image.GetRepresentation(gfx::ImageSkia::GetMaxSupportedScale())
70 .GetBitmap();
[email protected]dd46a4ce2012-09-15 10:50:5071}
72
[email protected]c82da8c42012-06-08 19:49:1173} // namespace
74
rdevlin.cronin092c6772014-11-20 23:52:4075ExtensionInstallPrompt::PromptType
76ExtensionInstallPrompt::g_last_prompt_type_for_tests =
77 ExtensionInstallPrompt::UNSET_PROMPT_TYPE;
78
[email protected]5db2e882012-12-20 10:17:2679ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
[email protected]c82da8c42012-06-08 19:49:1180 : type_(type),
Tim Judkinse9221872020-01-25 01:37:5381 is_requesting_host_permissions_(false),
[email protected]79a6f99a2013-08-29 00:32:5882 is_showing_details_for_retained_files_(false),
reillygc64d3dd2014-09-29 21:10:1183 is_showing_details_for_retained_devices_(false),
kylecharb59c3d72019-10-29 05:26:2684 extension_(nullptr),
[email protected]c82da8c42012-06-08 19:49:1185 average_rating_(0.0),
[email protected]dcde34b32013-07-31 02:28:4586 rating_count_(0),
[email protected]34b5f7f2014-01-29 02:48:1187 show_user_count_(false),
88 has_webstore_data_(false) {
rdevlin.croninc7f8d132017-02-22 14:29:0289 DCHECK_NE(type_, UNSET_PROMPT_TYPE);
90 DCHECK_NE(type_, NUM_PROMPT_TYPES);
[email protected]c82da8c42012-06-08 19:49:1191}
92
93ExtensionInstallPrompt::Prompt::~Prompt() {
94}
95
Danan S5f430882020-01-14 04:52:5596void ExtensionInstallPrompt::Prompt::AddPermissionSet(
97 const PermissionSet& permissions) {
98 Manifest::Type type =
99 extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN;
100 prompt_permissions_.LoadFromPermissionSet(&permissions, type);
Tim Judkinse9221872020-01-25 01:37:53101 if (!permissions.effective_hosts().is_empty()) {
102 is_requesting_host_permissions_ = true;
103 }
Danan S5f430882020-01-14 04:52:55104}
sashabeabd5df2014-11-06 02:38:25105
Danan S5f430882020-01-14 04:52:55106void ExtensionInstallPrompt::Prompt::AddPermissionMessages(
107 const PermissionMessages& permissions) {
108 prompt_permissions_.AddPermissionMessages(permissions);
[email protected]79a6f99a2013-08-29 00:32:58109}
110
111void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
112 DetailsType type,
113 size_t index,
114 bool is_showing_details) {
115 switch (type) {
116 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54117 prompt_permissions_.is_showing_details[index] = is_showing_details;
118 break;
[email protected]79a6f99a2013-08-29 00:32:58119 case RETAINED_FILES_DETAILS:
120 is_showing_details_for_retained_files_ = is_showing_details;
121 break;
reillygc64d3dd2014-09-29 21:10:11122 case RETAINED_DEVICES_DETAILS:
123 is_showing_details_for_retained_devices_ = is_showing_details;
124 break;
[email protected]79a6f99a2013-08-29 00:32:58125 }
[email protected]8ab7e6c2013-07-11 21:15:03126}
127
[email protected]34b5f7f2014-01-29 02:48:11128void ExtensionInstallPrompt::Prompt::SetWebstoreData(
[email protected]c82da8c42012-06-08 19:49:11129 const std::string& localized_user_count,
[email protected]dcde34b32013-07-31 02:28:45130 bool show_user_count,
[email protected]c82da8c42012-06-08 19:49:11131 double average_rating,
132 int rating_count) {
rdevlin.cronin5f6b69d2014-09-20 01:23:35133 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11134 localized_user_count_ = localized_user_count;
[email protected]dcde34b32013-07-31 02:28:45135 show_user_count_ = show_user_count;
[email protected]c82da8c42012-06-08 19:49:11136 average_rating_ = average_rating;
137 rating_count_ = rating_count;
[email protected]34b5f7f2014-01-29 02:48:11138 has_webstore_data_ = true;
[email protected]c82da8c42012-06-08 19:49:11139}
140
[email protected]6a72a632013-12-12 22:22:00141base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
rdevlin.croninc7f8d132017-02-22 14:29:02142 int id = -1;
143 switch (type_) {
144 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41145 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02146 id = IDS_EXTENSION_INSTALL_PROMPT_TITLE;
147 break;
148 case RE_ENABLE_PROMPT:
149 id = IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE;
150 break;
151 case PERMISSIONS_PROMPT:
152 id = IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE;
153 break;
154 case EXTERNAL_INSTALL_PROMPT:
155 if (extension_->is_app())
156 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP;
157 else if (extension_->is_theme())
158 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME;
159 else
160 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION;
161 break;
162 case POST_INSTALL_PERMISSIONS_PROMPT:
163 id = IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE;
164 break;
165 case REMOTE_INSTALL_PROMPT:
166 id = IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE;
167 break;
168 case REPAIR_PROMPT:
169 id = IDS_EXTENSION_REPAIR_PROMPT_TITLE;
170 break;
171 case DELEGATED_PERMISSIONS_PROMPT:
172 // Special case: need to include the delegated username.
173 return l10n_util::GetStringFUTF16(
174 IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE,
175 base::UTF8ToUTF16(extension_->name()),
176 base::UTF8ToUTF16(delegated_username_));
177 case UNSET_PROMPT_TYPE:
178 case NUM_PROMPT_TYPES:
179 NOTREACHED();
treibd35862252015-06-15 12:50:51180 }
rdevlin.croninc7f8d132017-02-22 14:29:02181
treibd35862252015-06-15 12:50:51182 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11183}
184
[email protected]15d267b42013-02-14 23:43:32185int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
rdevlin.croninc7f8d132017-02-22 14:29:02186 // The "OK" button in the post install permissions dialog allows revoking
187 // file/device access, and is only shown if such permissions exist; see
188 // ShouldDisplayRevokeButton().
189 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
190 !ShouldDisplayRevokeButton()) {
191 return ui::DIALOG_BUTTON_CANCEL;
[email protected]a2886e8b2013-06-08 05:15:02192 }
rdevlin.croninc7f8d132017-02-22 14:29:02193 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
[email protected]15d267b42013-02-14 23:43:32194}
195
[email protected]6a72a632013-12-12 22:22:00196base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02197 int id = -1;
198 switch (type_) {
199 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41200 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02201 if (extension_->is_app())
202 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
203 else if (extension_->is_theme())
204 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
205 else
206 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
207 break;
208 case RE_ENABLE_PROMPT:
209 id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON;
210 break;
211 case PERMISSIONS_PROMPT:
212 id = IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON;
213 break;
214 case EXTERNAL_INSTALL_PROMPT:
215 if (extension_->is_app())
216 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
217 else if (extension_->is_theme())
218 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
219 else
220 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
221 break;
222 case POST_INSTALL_PERMISSIONS_PROMPT:
223 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
224 id =
225 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON;
226 } else if (GetRetainedFileCount()) {
227 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON;
228 } else if (GetRetainedDeviceCount()) {
229 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON;
230 }
231 // If there are neither retained files nor devices, leave id -1 so there
232 // will be no "accept" button.
233 break;
234 case REMOTE_INSTALL_PROMPT:
235 if (extension_->is_app())
236 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_APP;
237 else
238 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_EXTENSION;
239 break;
240 case REPAIR_PROMPT:
241 if (extension_->is_app())
242 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_APP;
243 else
244 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_EXTENSION;
245 break;
246 case DELEGATED_PERMISSIONS_PROMPT:
247 id = IDS_EXTENSION_PROMPT_INSTALL_BUTTON;
248 break;
249 case UNSET_PROMPT_TYPE:
250 case NUM_PROMPT_TYPES:
251 NOTREACHED();
[email protected]846606012012-10-19 18:42:25252 }
rdevlin.croninc7f8d132017-02-22 14:29:02253
254 return id != -1 ? l10n_util::GetStringUTF16(id) : base::string16();
[email protected]c82da8c42012-06-08 19:49:11255}
256
[email protected]6a72a632013-12-12 22:22:00257base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02258 int id = -1;
259 switch (type_) {
260 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41261 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02262 case RE_ENABLE_PROMPT:
263 case REMOTE_INSTALL_PROMPT:
264 case REPAIR_PROMPT:
265 case DELEGATED_PERMISSIONS_PROMPT:
266 id = IDS_CANCEL;
267 break;
268 case PERMISSIONS_PROMPT:
269 id = IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON;
270 break;
271 case EXTERNAL_INSTALL_PROMPT:
272 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON;
273 break;
274 case POST_INSTALL_PERMISSIONS_PROMPT:
275 id = IDS_CLOSE;
276 break;
277 case UNSET_PROMPT_TYPE:
278 case NUM_PROMPT_TYPES:
279 NOTREACHED();
280 }
281
282 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11283}
284
Devlin Cronind6e136a2018-05-15 23:39:32285base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
286 int id = -1;
287 switch (type_) {
288 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41289 case WEBSTORE_WIDGET_PROMPT:
Devlin Cronind6e136a2018-05-15 23:39:32290 case EXTERNAL_INSTALL_PROMPT:
291 case REMOTE_INSTALL_PROMPT:
292 case DELEGATED_PERMISSIONS_PROMPT:
293 id = IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO;
294 break;
295 case RE_ENABLE_PROMPT:
296 id = IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO;
297 break;
298 case PERMISSIONS_PROMPT:
299 id = IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO;
300 break;
301 case POST_INSTALL_PERMISSIONS_PROMPT:
302 case REPAIR_PROMPT:
303 id = IDS_EXTENSION_PROMPT_CAN_ACCESS;
304 break;
305 case UNSET_PROMPT_TYPE:
306 case NUM_PROMPT_TYPES:
gpdavis.chromium0fbac4d2014-09-19 20:57:54307 NOTREACHED();
gpdavis.chromium0fbac4d2014-09-19 20:57:54308 }
Devlin Cronind6e136a2018-05-15 23:39:32309 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11310}
311
[email protected]6a72a632013-12-12 22:22:00312base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
jshin3a6c36bb2015-04-09 21:58:08313 return l10n_util::GetPluralStringFUTF16(
314 IDS_EXTENSION_PROMPT_RETAINED_FILES, GetRetainedFileCount());
[email protected]8ab7e6c2013-07-11 21:15:03315}
316
reillygc64d3dd2014-09-29 21:10:11317base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
318 const {
jshin3a6c36bb2015-04-09 21:58:08319 return l10n_util::GetPluralStringFUTF16(
320 IDS_EXTENSION_PROMPT_RETAINED_DEVICES, GetRetainedDeviceCount());
reillygc64d3dd2014-09-29 21:10:11321}
322
[email protected]f2cd8992013-06-11 17:30:18323bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
Devlin Cronind6e136a2018-05-15 23:39:32324 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
[email protected]f2cd8992013-06-11 17:30:18325}
326
[email protected]c82da8c42012-06-08 19:49:11327void ExtensionInstallPrompt::Prompt::AppendRatingStars(
328 StarAppender appender, void* data) const {
329 CHECK(appender);
rdevlin.cronin5f6b69d2014-09-20 01:23:35330 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11331 int rating_integer = floor(average_rating_);
332 double rating_fractional = average_rating_ - rating_integer;
333
334 if (rating_fractional > 0.66) {
335 rating_integer++;
336 }
337
338 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
339 rating_fractional = 0;
340 }
341
Lei Zhang7640d542017-10-03 16:26:49342 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
[email protected]c82da8c42012-06-08 19:49:11343 int i;
344 for (i = 0; i < rating_integer; i++) {
345 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
346 }
347 if (rating_fractional) {
348 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
349 i++;
350 }
351 for (; i < kMaxExtensionRating; i++) {
352 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
353 }
354}
355
[email protected]6a72a632013-12-12 22:22:00356base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35357 CHECK(AllowWebstoreData(type_));
[email protected]6725048e2013-10-24 21:47:14358 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
Raul Tambrefff51b752019-02-04 13:09:47359 base::NumberToString16(rating_count_));
[email protected]c82da8c42012-06-08 19:49:11360}
361
[email protected]6a72a632013-12-12 22:22:00362base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35363 CHECK(AllowWebstoreData(type_));
[email protected]dcde34b32013-07-31 02:28:45364
365 if (show_user_count_) {
[email protected]6725048e2013-10-24 21:47:14366 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
367 base::UTF8ToUTF16(localized_user_count_));
[email protected]dcde34b32013-07-31 02:28:45368 }
[email protected]6725048e2013-10-24 21:47:14369 return base::string16();
[email protected]c82da8c42012-06-08 19:49:11370}
371
Devlin Cronind6e136a2018-05-15 23:39:32372size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
373 return prompt_permissions_.permissions.size();
[email protected]c82da8c42012-06-08 19:49:11374}
375
Devlin Cronind6e136a2018-05-15 23:39:32376size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
377 return prompt_permissions_.details.size();
[email protected]8ab7e6c2013-07-11 21:15:03378}
379
gpdavis.chromium0fbac4d2014-09-19 20:57:54380base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
Devlin Cronind6e136a2018-05-15 23:39:32381 size_t index) const {
382 CHECK_LT(index, prompt_permissions_.permissions.size());
383 return prompt_permissions_.permissions[index];
[email protected]c82da8c42012-06-08 19:49:11384}
385
[email protected]6a72a632013-12-12 22:22:00386base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
Devlin Cronind6e136a2018-05-15 23:39:32387 size_t index) const {
388 CHECK_LT(index, prompt_permissions_.details.size());
389 return prompt_permissions_.details[index];
[email protected]8ab7e6c2013-07-11 21:15:03390}
391
[email protected]79a6f99a2013-08-29 00:32:58392bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
393 DetailsType type, size_t index) const {
394 switch (type) {
395 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54396 CHECK_LT(index, prompt_permissions_.is_showing_details.size());
397 return prompt_permissions_.is_showing_details[index];
[email protected]79a6f99a2013-08-29 00:32:58398 case RETAINED_FILES_DETAILS:
399 return is_showing_details_for_retained_files_;
reillygc64d3dd2014-09-29 21:10:11400 case RETAINED_DEVICES_DETAILS:
401 return is_showing_details_for_retained_devices_;
[email protected]79a6f99a2013-08-29 00:32:58402 }
403 return false;
404}
405
[email protected]a2886e8b2013-06-08 05:15:02406size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
407 return retained_files_.size();
408}
409
[email protected]6a72a632013-12-12 22:22:00410base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
411 const {
[email protected]a2886e8b2013-06-08 05:15:02412 CHECK_LT(index, retained_files_.size());
[email protected]6725048e2013-10-24 21:47:14413 return retained_files_[index].AsUTF16Unsafe();
[email protected]a2886e8b2013-06-08 05:15:02414}
415
reillygc64d3dd2014-09-29 21:10:11416size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
417 return retained_device_messages_.size();
418}
419
420base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
421 size_t index) const {
422 CHECK_LT(index, retained_device_messages_.size());
423 return retained_device_messages_[index];
424}
425
426bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
427 return !retained_files_.empty() || !retained_device_messages_.empty();
428}
429
[email protected]a2886e8b2013-06-08 05:15:02430bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
431 return !retained_files_.empty();
432}
433
Tim Judkinse9221872020-01-25 01:37:53434bool ExtensionInstallPrompt::Prompt::ShouldDisplayWithholdingUI() const {
435 return base::FeatureList::IsEnabled(
436 extensions_features::
437 kAllowWithholdingExtensionPermissionsOnInstall) &&
438 extensions::util::CanWithholdPermissionsFromExtension(*extension_) &&
439 is_requesting_host_permissions_ && type_ == INSTALL_PROMPT;
440}
441
[email protected]c82da8c42012-06-08 19:49:11442// static
rdevlin.croninf84cab72015-12-12 03:45:23443ExtensionInstallPrompt::PromptType
444ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
445 content::BrowserContext* context,
446 const extensions::Extension* extension) {
447 bool is_remote_install =
448 context &&
449 extensions::ExtensionPrefs::Get(context)->HasDisableReason(
Minh X. Nguyen45479012017-08-18 21:35:36450 extension->id(), extensions::disable_reason::DISABLE_REMOTE_INSTALL);
rdevlin.croninf84cab72015-12-12 03:45:23451
452 return is_remote_install ? REMOTE_INSTALL_PROMPT : RE_ENABLE_PROMPT;
453}
454
455// static
[email protected]c82da8c42012-06-08 19:49:11456scoped_refptr<Extension>
457 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
[email protected]023b3d12013-12-23 18:46:49458 const base::DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13459 int flags,
[email protected]c82da8c42012-06-08 19:49:11460 const std::string& id,
461 const std::string& localized_name,
462 const std::string& localized_description,
463 std::string* error) {
dchengc963c7142016-04-08 03:55:22464 std::unique_ptr<base::DictionaryValue> localized_manifest;
[email protected]c82da8c42012-06-08 19:49:11465 if (!localized_name.empty() || !localized_description.empty()) {
466 localized_manifest.reset(manifest->DeepCopy());
467 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27468 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11469 localized_name);
470 }
471 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27472 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11473 localized_description);
474 }
475 }
476
477 return Extension::Create(
rdevlin.cronin165732a42016-07-18 22:25:08478 base::FilePath(), Manifest::INTERNAL,
479 localized_manifest.get() ? *localized_manifest : *manifest, flags, id,
[email protected]c82da8c42012-06-08 19:49:11480 error);
481}
482
[email protected]7f165342014-02-12 09:22:22483ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
Oscar Johanssondf0dd8f2018-06-18 06:51:07484 : profile_(contents
485 ? Profile::FromBrowserContext(contents->GetBrowserContext())
486 : nullptr),
kylecharb59c3d72019-10-29 05:26:26487 extension_(nullptr),
Oscar Johanssondf0dd8f2018-06-18 06:51:07488 install_ui_(extensions::CreateExtensionInstallUI(profile_)),
pkotwicz2f181782014-10-29 17:33:45489 show_params_(new ExtensionInstallPromptShowParams(contents)),
Jeremy Roman495db682019-07-12 16:03:24490 did_call_show_dialog_(false) {}
[email protected]5db2e882012-12-20 10:17:26491
pkotwicz2175c622014-10-22 19:56:28492ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
493 gfx::NativeWindow native_window)
pkotwicza57a1f322014-10-21 00:24:30494 : profile_(profile),
kylecharb59c3d72019-10-29 05:26:26495 extension_(nullptr),
pkotwicza57a1f322014-10-21 00:24:30496 install_ui_(extensions::CreateExtensionInstallUI(profile)),
pkotwicz2f181782014-10-29 17:33:45497 show_params_(
498 new ExtensionInstallPromptShowParams(profile, native_window)),
Jeremy Roman495db682019-07-12 16:03:24499 did_call_show_dialog_(false) {}
[email protected]c82da8c42012-06-08 19:49:11500
501ExtensionInstallPrompt::~ExtensionInstallPrompt() {
502}
503
rdevlin.croninf84cab72015-12-12 03:45:23504void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12505 const DoneCallback& done_callback,
[email protected]c82da8c42012-06-08 19:49:11506 const Extension* extension,
rdevlin.croninf84cab72015-12-12 03:45:23507 const SkBitmap* icon,
508 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12509 ShowDialog(done_callback, extension, icon,
Jinho Bangb5216cec2018-01-17 19:43:11510 std::make_unique<Prompt>(INSTALL_PROMPT), show_dialog_callback);
rdevlin.cronin2e252692015-12-15 21:47:02511}
512
513void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12514 const DoneCallback& done_callback,
rdevlin.cronin2e252692015-12-15 21:47:02515 const Extension* extension,
516 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22517 std::unique_ptr<Prompt> prompt,
rdevlin.cronin2e252692015-12-15 21:47:02518 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12519 ShowDialog(done_callback, extension, icon, std::move(prompt), nullptr,
rdevlin.croninf84cab72015-12-12 03:45:23520 show_dialog_callback);
521}
522
523void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12524 const DoneCallback& done_callback,
rdevlin.croninf84cab72015-12-12 03:45:23525 const Extension* extension,
526 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22527 std::unique_ptr<Prompt> prompt,
528 std::unique_ptr<const PermissionSet> custom_permissions,
rdevlin.croninf84cab72015-12-12 03:45:23529 const ShowDialogCallback& show_dialog_callback) {
fdorayc16c6f82016-06-29 15:27:32530 DCHECK(ui_thread_checker_.CalledOnValidThread());
rdevlin.cronin2e252692015-12-15 21:47:02531 DCHECK(prompt);
[email protected]c82da8c42012-06-08 19:49:11532 extension_ = extension;
rdevlin.cronin41593052016-01-08 01:40:12533 done_callback_ = done_callback;
rdevlin.croninfc1499f2015-12-21 18:22:00534 if (icon && !icon->empty())
535 SetIcon(icon);
dcheng1fc00f12015-12-26 22:18:03536 prompt_ = std::move(prompt);
537 custom_permissions_ = std::move(custom_permissions);
[email protected]af6efb22012-10-12 02:23:05538 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11539
540 // We special-case themes to not show any confirm UI. Instead they are
541 // immediately installed, and then we show an infobar (see OnInstallSuccess)
542 // to allow the user to revert if they don't like it.
rdevlin.cronin10f34542017-02-06 18:22:47543 if (extension->is_theme() && extension->from_webstore()) {
Daniel Chengd78c0252019-04-26 17:22:11544 std::move(done_callback_).Run(Result::ACCEPTED);
rdevlin.cronin10f34542017-02-06 18:22:47545 return;
[email protected]c82da8c42012-06-08 19:49:11546 }
547
548 LoadImageIfNeeded();
549}
550
Dana Friedc3c30222018-12-11 23:34:56551void ExtensionInstallPrompt::OnInstallSuccess(
552 scoped_refptr<const Extension> extension,
553 SkBitmap* icon) {
[email protected]c82da8c42012-06-08 19:49:11554 extension_ = extension;
555 SetIcon(icon);
556
557 install_ui_->OnInstallSuccess(extension, &icon_);
558}
559
[email protected]bf3d9df2012-07-24 23:20:27560void ExtensionInstallPrompt::OnInstallFailure(
ginkage47e603e2015-02-27 08:42:41561 const extensions::CrxInstallError& error) {
[email protected]c82da8c42012-06-08 19:49:11562 install_ui_->OnInstallFailure(error);
563}
564
565void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
566 if (image)
567 icon_ = *image;
568 else
569 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50570 if (icon_.empty()) {
571 // Let's set default icon bitmap whose size is equal to the default icon's
572 // pixel size under maximal supported scale factor. If the bitmap is larger
573 // than the one we need, it will be scaled down by the ui code.
treibd9e1d9d2015-04-24 11:17:04574 icon_ = GetDefaultIconBitmapForMaxScaleFactor(
575 extension_ ? extension_->is_app() : false);
[email protected]dd46a4ce2012-09-15 10:50:50576 }
[email protected]c82da8c42012-06-08 19:49:11577}
578
[email protected]ec7de0c5a2012-11-16 07:40:47579void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
kylecharb59c3d72019-10-29 05:26:26580 SetIcon(image.IsEmpty() ? nullptr : image.ToSkBitmap());
[email protected]4e494872013-09-06 09:13:54581 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11582}
583
584void ExtensionInstallPrompt::LoadImageIfNeeded() {
treib3d41d9f2016-04-12 08:53:01585 // Don't override an icon that was passed in. Also, |profile_| can be null in
586 // unit tests.
587 if (!icon_.empty() || !profile_) {
[email protected]4e494872013-09-06 09:13:54588 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11589 return;
590 }
591
[email protected]993da5e2013-03-23 21:25:16592 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
Dana Friedc3c30222018-12-11 23:34:56593 extension_.get(), extension_misc::EXTENSION_ICON_LARGE,
[email protected]702d8b42013-02-27 20:55:50594 ExtensionIconSet::MATCH_BIGGER);
[email protected]0d0ba182014-06-03 12:40:43595
596 // Load the image asynchronously. The response will be sent to OnImageLoaded.
pkotwicza57a1f322014-10-21 00:24:30597 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile_);
[email protected]0d0ba182014-06-03 12:40:43598
599 std::vector<extensions::ImageLoader::ImageRepresentation> images_list;
600 images_list.push_back(extensions::ImageLoader::ImageRepresentation(
601 image,
602 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
603 gfx::Size(),
604 ui::SCALE_FACTOR_100P));
Dana Friedc3c30222018-12-11 23:34:56605 loader->LoadImagesAsync(extension_.get(), images_list,
Nigel Taobd12215b2018-11-29 01:10:18606 base::BindOnce(&ExtensionInstallPrompt::OnImageLoaded,
607 weak_factory_.GetWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11608}
609
610void ExtensionInstallPrompt::ShowConfirmation() {
Danan S5f430882020-01-14 04:52:55611 std::unique_ptr<const PermissionSet> permissions_to_display;
612
gpdavis.chromium0fbac4d2014-09-19 20:57:54613 if (custom_permissions_.get()) {
Danan S5f430882020-01-14 04:52:55614 permissions_to_display = custom_permissions_->Clone();
gpdavis.chromium0fbac4d2014-09-19 20:57:54615 } else if (extension_) {
treib7496f63c2015-03-04 12:18:53616 // For delegated installs, all optional permissions are pre-approved by the
617 // person who triggers the install, so add them to the list.
Danan S5f430882020-01-14 04:52:55618 bool include_optional_permissions =
619 prompt_->type() == DELEGATED_PERMISSIONS_PROMPT;
620 permissions_to_display =
621 extensions::util::GetInstallPromptPermissionSetForExtension(
622 extension_.get(), profile_, include_optional_permissions);
[email protected]bebe1d02012-08-02 20:17:09623 }
[email protected]c82da8c42012-06-08 19:49:11624
Dana Friedc3c30222018-12-11 23:34:56625 prompt_->set_extension(extension_.get());
Danan S5f430882020-01-14 04:52:55626 if (permissions_to_display) {
627 prompt_->AddPermissionSet(*permissions_to_display);
628 }
629
treibd9e1d9d2015-04-24 11:17:04630 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]af6efb22012-10-12 02:23:05631
pkotwicz2f181782014-10-29 17:33:45632 if (show_params_->WasParentDestroyed()) {
Daniel Chengd78c0252019-04-26 17:22:11633 std::move(done_callback_).Run(Result::ABORTED);
pkotwicz2f181782014-10-29 17:33:45634 return;
635 }
636
rdevlin.croninca5bf2da2015-12-17 21:21:08637 g_last_prompt_type_for_tests = prompt_->type();
638 did_call_show_dialog_ = true;
639
Daniel Cheng83563bc2019-04-30 23:50:24640 // If true, auto confirm is enabled and already handled the result.
641 if (AutoConfirmPromptIfEnabled())
rdevlin.croninca5bf2da2015-12-17 21:21:08642 return;
643
[email protected]5db2e882012-12-20 10:17:26644 if (show_dialog_callback_.is_null())
rdevlin.cronin41593052016-01-08 01:40:12645 show_dialog_callback_ = GetDefaultShowDialogCallback();
Daniel Chengd59fe5d2019-04-30 00:32:49646 // TODO(https://crbug.com/957713): Use OnceCallback and eliminate the need for
647 // a callback on the stack.
648 auto cb = std::move(done_callback_);
Daniel Chengd78c0252019-04-26 17:22:11649 std::move(show_dialog_callback_)
Daniel Chengd59fe5d2019-04-30 00:32:49650 .Run(show_params_.get(), cb, std::move(prompt_));
[email protected]c82da8c42012-06-08 19:49:11651}
Daniel Cheng83563bc2019-04-30 23:50:24652
653bool ExtensionInstallPrompt::AutoConfirmPromptIfEnabled() {
654 switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
655 case extensions::ScopedTestDialogAutoConfirm::NONE:
656 return false;
657 // We use PostTask instead of calling the callback directly here, because in
658 // the real implementations it's highly likely the message loop will be
659 // pumping a few times before the user clicks accept or cancel.
660 case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
Daniel Cheng83563bc2019-04-30 23:50:24661 base::ThreadTaskRunnerHandle::Get()->PostTask(
662 FROM_HERE, base::BindOnce(std::move(done_callback_),
663 ExtensionInstallPrompt::Result::ACCEPTED));
664 return true;
Tim Judkinse9221872020-01-25 01:37:53665 case extensions::ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
666 base::ThreadTaskRunnerHandle::Get()->PostTask(
667 FROM_HERE,
668 base::BindOnce(
669 std::move(done_callback_),
670 ExtensionInstallPrompt::Result::ACCEPTED_AND_OPTION_CHECKED));
671 return true;
Daniel Cheng83563bc2019-04-30 23:50:24672 case extensions::ScopedTestDialogAutoConfirm::CANCEL:
673 base::ThreadTaskRunnerHandle::Get()->PostTask(
674 FROM_HERE,
675 base::BindOnce(std::move(done_callback_),
676 ExtensionInstallPrompt::Result::USER_CANCELED));
677 return true;
678 }
679
680 NOTREACHED();
681 return false;
682}