blob: 401ec9018e325d56d72a1935f43ec1b009d43428 [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"
[email protected]4b7908842014-04-07 23:50:2232#include "extensions/common/extension_icon_set.h"
[email protected]993da5e2013-03-23 21:25:1633#include "extensions/common/extension_resource.h"
[email protected]5ef835a2013-11-08 20:42:5734#include "extensions/common/feature_switch.h"
[email protected]d42c11152013-08-22 19:36:3235#include "extensions/common/manifest.h"
[email protected]6bf90612013-08-15 00:36:2736#include "extensions/common/manifest_constants.h"
[email protected]0db486f2014-04-09 19:32:2237#include "extensions/common/manifest_handlers/icons_handler.h"
treib7496f63c2015-03-04 12:18:5338#include "extensions/common/manifest_handlers/permissions_parser.h"
Danan S5f430882020-01-14 04:52:5539#include "extensions/common/permissions/permission_message.h"
treibf9dce312015-08-27 17:33:3540#include "extensions/common/permissions/permission_message_provider.h"
[email protected]5a55f3f2013-10-29 01:08:2941#include "extensions/common/permissions/permission_set.h"
[email protected]e4452d32013-11-15 23:07:4142#include "extensions/common/permissions/permissions_data.h"
[email protected]c82da8c42012-06-08 19:49:1143#include "ui/base/l10n/l10n_util.h"
44#include "ui/base/resource/resource_bundle.h"
pkotwicz7fd01192014-10-09 04:43:5045#include "ui/base/ui_base_types.h"
rdevlin.cronin3fe4bd32016-01-12 18:45:4046#include "ui/gfx/image/image_skia.h"
[email protected]c82da8c42012-06-08 19:49:1147
[email protected]c82da8c42012-06-08 19:49:1148using extensions::Extension;
[email protected]1d5e58b2013-01-31 08:41:4049using extensions::Manifest;
treibf9dce312015-08-27 17:33:3550using extensions::PermissionMessage;
51using extensions::PermissionMessages;
[email protected]c2e66e12012-06-27 06:27:0652using extensions::PermissionSet;
[email protected]c82da8c42012-06-08 19:49:1153
[email protected]612a1cb12012-10-17 13:18:0354namespace {
55
rdevlin.cronin5f6b69d2014-09-20 01:23:3556bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type) {
Benjamin Ackermanc4fb02302018-10-03 19:55:4157 return type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT ||
58 type == ExtensionInstallPrompt::REPAIR_PROMPT ||
59 type == ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT;
rdevlin.cronin5f6b69d2014-09-20 01:23:3560}
61
[email protected]dd46a4ce2012-09-15 10:50:5062// Returns bitmap for the default icon with size equal to the default icon's
63// pixel size under maximal supported scale factor.
64SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
[email protected]702d8b42013-02-27 20:55:5065 const gfx::ImageSkia& image = is_app ?
[email protected]7eb20e32014-04-30 08:50:5666 extensions::util::GetDefaultAppIcon() :
67 extensions::util::GetDefaultExtensionIcon();
Malay Keshave8c63be2018-10-02 00:12:2468 return image.GetRepresentation(gfx::ImageSkia::GetMaxSupportedScale())
69 .GetBitmap();
[email protected]dd46a4ce2012-09-15 10:50:5070}
71
[email protected]c82da8c42012-06-08 19:49:1172} // namespace
73
rdevlin.cronin092c6772014-11-20 23:52:4074ExtensionInstallPrompt::PromptType
75ExtensionInstallPrompt::g_last_prompt_type_for_tests =
76 ExtensionInstallPrompt::UNSET_PROMPT_TYPE;
77
[email protected]5db2e882012-12-20 10:17:2678ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
[email protected]c82da8c42012-06-08 19:49:1179 : type_(type),
[email protected]79a6f99a2013-08-29 00:32:5880 is_showing_details_for_retained_files_(false),
reillygc64d3dd2014-09-29 21:10:1181 is_showing_details_for_retained_devices_(false),
kylecharb59c3d72019-10-29 05:26:2682 extension_(nullptr),
[email protected]c82da8c42012-06-08 19:49:1183 average_rating_(0.0),
[email protected]dcde34b32013-07-31 02:28:4584 rating_count_(0),
[email protected]34b5f7f2014-01-29 02:48:1185 show_user_count_(false),
86 has_webstore_data_(false) {
rdevlin.croninc7f8d132017-02-22 14:29:0287 DCHECK_NE(type_, UNSET_PROMPT_TYPE);
88 DCHECK_NE(type_, NUM_PROMPT_TYPES);
[email protected]c82da8c42012-06-08 19:49:1189}
90
91ExtensionInstallPrompt::Prompt::~Prompt() {
92}
93
Danan S5f430882020-01-14 04:52:5594void ExtensionInstallPrompt::Prompt::AddPermissionSet(
95 const PermissionSet& permissions) {
96 Manifest::Type type =
97 extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN;
98 prompt_permissions_.LoadFromPermissionSet(&permissions, type);
99}
sashabeabd5df2014-11-06 02:38:25100
Danan S5f430882020-01-14 04:52:55101void ExtensionInstallPrompt::Prompt::AddPermissionMessages(
102 const PermissionMessages& permissions) {
103 prompt_permissions_.AddPermissionMessages(permissions);
[email protected]79a6f99a2013-08-29 00:32:58104}
105
106void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
107 DetailsType type,
108 size_t index,
109 bool is_showing_details) {
110 switch (type) {
111 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54112 prompt_permissions_.is_showing_details[index] = is_showing_details;
113 break;
[email protected]79a6f99a2013-08-29 00:32:58114 case RETAINED_FILES_DETAILS:
115 is_showing_details_for_retained_files_ = is_showing_details;
116 break;
reillygc64d3dd2014-09-29 21:10:11117 case RETAINED_DEVICES_DETAILS:
118 is_showing_details_for_retained_devices_ = is_showing_details;
119 break;
[email protected]79a6f99a2013-08-29 00:32:58120 }
[email protected]8ab7e6c2013-07-11 21:15:03121}
122
[email protected]34b5f7f2014-01-29 02:48:11123void ExtensionInstallPrompt::Prompt::SetWebstoreData(
[email protected]c82da8c42012-06-08 19:49:11124 const std::string& localized_user_count,
[email protected]dcde34b32013-07-31 02:28:45125 bool show_user_count,
[email protected]c82da8c42012-06-08 19:49:11126 double average_rating,
127 int rating_count) {
rdevlin.cronin5f6b69d2014-09-20 01:23:35128 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11129 localized_user_count_ = localized_user_count;
[email protected]dcde34b32013-07-31 02:28:45130 show_user_count_ = show_user_count;
[email protected]c82da8c42012-06-08 19:49:11131 average_rating_ = average_rating;
132 rating_count_ = rating_count;
[email protected]34b5f7f2014-01-29 02:48:11133 has_webstore_data_ = true;
[email protected]c82da8c42012-06-08 19:49:11134}
135
[email protected]6a72a632013-12-12 22:22:00136base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
rdevlin.croninc7f8d132017-02-22 14:29:02137 int id = -1;
138 switch (type_) {
139 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41140 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02141 id = IDS_EXTENSION_INSTALL_PROMPT_TITLE;
142 break;
143 case RE_ENABLE_PROMPT:
144 id = IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE;
145 break;
146 case PERMISSIONS_PROMPT:
147 id = IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE;
148 break;
149 case EXTERNAL_INSTALL_PROMPT:
150 if (extension_->is_app())
151 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP;
152 else if (extension_->is_theme())
153 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME;
154 else
155 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION;
156 break;
157 case POST_INSTALL_PERMISSIONS_PROMPT:
158 id = IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE;
159 break;
160 case REMOTE_INSTALL_PROMPT:
161 id = IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE;
162 break;
163 case REPAIR_PROMPT:
164 id = IDS_EXTENSION_REPAIR_PROMPT_TITLE;
165 break;
166 case DELEGATED_PERMISSIONS_PROMPT:
167 // Special case: need to include the delegated username.
168 return l10n_util::GetStringFUTF16(
169 IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE,
170 base::UTF8ToUTF16(extension_->name()),
171 base::UTF8ToUTF16(delegated_username_));
172 case UNSET_PROMPT_TYPE:
173 case NUM_PROMPT_TYPES:
174 NOTREACHED();
treibd35862252015-06-15 12:50:51175 }
rdevlin.croninc7f8d132017-02-22 14:29:02176
treibd35862252015-06-15 12:50:51177 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11178}
179
[email protected]15d267b42013-02-14 23:43:32180int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
rdevlin.croninc7f8d132017-02-22 14:29:02181 // The "OK" button in the post install permissions dialog allows revoking
182 // file/device access, and is only shown if such permissions exist; see
183 // ShouldDisplayRevokeButton().
184 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
185 !ShouldDisplayRevokeButton()) {
186 return ui::DIALOG_BUTTON_CANCEL;
[email protected]a2886e8b2013-06-08 05:15:02187 }
rdevlin.croninc7f8d132017-02-22 14:29:02188 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
[email protected]15d267b42013-02-14 23:43:32189}
190
[email protected]6a72a632013-12-12 22:22:00191base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02192 int id = -1;
193 switch (type_) {
194 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41195 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02196 if (extension_->is_app())
197 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
198 else if (extension_->is_theme())
199 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
200 else
201 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
202 break;
203 case RE_ENABLE_PROMPT:
204 id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON;
205 break;
206 case PERMISSIONS_PROMPT:
207 id = IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON;
208 break;
209 case EXTERNAL_INSTALL_PROMPT:
210 if (extension_->is_app())
211 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
212 else if (extension_->is_theme())
213 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
214 else
215 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
216 break;
217 case POST_INSTALL_PERMISSIONS_PROMPT:
218 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
219 id =
220 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON;
221 } else if (GetRetainedFileCount()) {
222 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON;
223 } else if (GetRetainedDeviceCount()) {
224 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON;
225 }
226 // If there are neither retained files nor devices, leave id -1 so there
227 // will be no "accept" button.
228 break;
229 case REMOTE_INSTALL_PROMPT:
230 if (extension_->is_app())
231 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_APP;
232 else
233 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_EXTENSION;
234 break;
235 case REPAIR_PROMPT:
236 if (extension_->is_app())
237 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_APP;
238 else
239 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_EXTENSION;
240 break;
241 case DELEGATED_PERMISSIONS_PROMPT:
242 id = IDS_EXTENSION_PROMPT_INSTALL_BUTTON;
243 break;
244 case UNSET_PROMPT_TYPE:
245 case NUM_PROMPT_TYPES:
246 NOTREACHED();
[email protected]846606012012-10-19 18:42:25247 }
rdevlin.croninc7f8d132017-02-22 14:29:02248
249 return id != -1 ? l10n_util::GetStringUTF16(id) : base::string16();
[email protected]c82da8c42012-06-08 19:49:11250}
251
[email protected]6a72a632013-12-12 22:22:00252base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02253 int id = -1;
254 switch (type_) {
255 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41256 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02257 case RE_ENABLE_PROMPT:
258 case REMOTE_INSTALL_PROMPT:
259 case REPAIR_PROMPT:
260 case DELEGATED_PERMISSIONS_PROMPT:
261 id = IDS_CANCEL;
262 break;
263 case PERMISSIONS_PROMPT:
264 id = IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON;
265 break;
266 case EXTERNAL_INSTALL_PROMPT:
267 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON;
268 break;
269 case POST_INSTALL_PERMISSIONS_PROMPT:
270 id = IDS_CLOSE;
271 break;
272 case UNSET_PROMPT_TYPE:
273 case NUM_PROMPT_TYPES:
274 NOTREACHED();
275 }
276
277 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11278}
279
Devlin Cronind6e136a2018-05-15 23:39:32280base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
281 int id = -1;
282 switch (type_) {
283 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41284 case WEBSTORE_WIDGET_PROMPT:
Devlin Cronind6e136a2018-05-15 23:39:32285 case EXTERNAL_INSTALL_PROMPT:
286 case REMOTE_INSTALL_PROMPT:
287 case DELEGATED_PERMISSIONS_PROMPT:
288 id = IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO;
289 break;
290 case RE_ENABLE_PROMPT:
291 id = IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO;
292 break;
293 case PERMISSIONS_PROMPT:
294 id = IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO;
295 break;
296 case POST_INSTALL_PERMISSIONS_PROMPT:
297 case REPAIR_PROMPT:
298 id = IDS_EXTENSION_PROMPT_CAN_ACCESS;
299 break;
300 case UNSET_PROMPT_TYPE:
301 case NUM_PROMPT_TYPES:
gpdavis.chromium0fbac4d2014-09-19 20:57:54302 NOTREACHED();
gpdavis.chromium0fbac4d2014-09-19 20:57:54303 }
Devlin Cronind6e136a2018-05-15 23:39:32304 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11305}
306
[email protected]6a72a632013-12-12 22:22:00307base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
jshin3a6c36bb2015-04-09 21:58:08308 return l10n_util::GetPluralStringFUTF16(
309 IDS_EXTENSION_PROMPT_RETAINED_FILES, GetRetainedFileCount());
[email protected]8ab7e6c2013-07-11 21:15:03310}
311
reillygc64d3dd2014-09-29 21:10:11312base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
313 const {
jshin3a6c36bb2015-04-09 21:58:08314 return l10n_util::GetPluralStringFUTF16(
315 IDS_EXTENSION_PROMPT_RETAINED_DEVICES, GetRetainedDeviceCount());
reillygc64d3dd2014-09-29 21:10:11316}
317
[email protected]f2cd8992013-06-11 17:30:18318bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
Devlin Cronind6e136a2018-05-15 23:39:32319 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
[email protected]f2cd8992013-06-11 17:30:18320}
321
[email protected]c82da8c42012-06-08 19:49:11322void ExtensionInstallPrompt::Prompt::AppendRatingStars(
323 StarAppender appender, void* data) const {
324 CHECK(appender);
rdevlin.cronin5f6b69d2014-09-20 01:23:35325 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11326 int rating_integer = floor(average_rating_);
327 double rating_fractional = average_rating_ - rating_integer;
328
329 if (rating_fractional > 0.66) {
330 rating_integer++;
331 }
332
333 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
334 rating_fractional = 0;
335 }
336
Lei Zhang7640d542017-10-03 16:26:49337 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
[email protected]c82da8c42012-06-08 19:49:11338 int i;
339 for (i = 0; i < rating_integer; i++) {
340 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
341 }
342 if (rating_fractional) {
343 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
344 i++;
345 }
346 for (; i < kMaxExtensionRating; i++) {
347 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
348 }
349}
350
[email protected]6a72a632013-12-12 22:22:00351base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35352 CHECK(AllowWebstoreData(type_));
[email protected]6725048e2013-10-24 21:47:14353 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
Raul Tambrefff51b752019-02-04 13:09:47354 base::NumberToString16(rating_count_));
[email protected]c82da8c42012-06-08 19:49:11355}
356
[email protected]6a72a632013-12-12 22:22:00357base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35358 CHECK(AllowWebstoreData(type_));
[email protected]dcde34b32013-07-31 02:28:45359
360 if (show_user_count_) {
[email protected]6725048e2013-10-24 21:47:14361 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
362 base::UTF8ToUTF16(localized_user_count_));
[email protected]dcde34b32013-07-31 02:28:45363 }
[email protected]6725048e2013-10-24 21:47:14364 return base::string16();
[email protected]c82da8c42012-06-08 19:49:11365}
366
Devlin Cronind6e136a2018-05-15 23:39:32367size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
368 return prompt_permissions_.permissions.size();
[email protected]c82da8c42012-06-08 19:49:11369}
370
Devlin Cronind6e136a2018-05-15 23:39:32371size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
372 return prompt_permissions_.details.size();
[email protected]8ab7e6c2013-07-11 21:15:03373}
374
gpdavis.chromium0fbac4d2014-09-19 20:57:54375base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
Devlin Cronind6e136a2018-05-15 23:39:32376 size_t index) const {
377 CHECK_LT(index, prompt_permissions_.permissions.size());
378 return prompt_permissions_.permissions[index];
[email protected]c82da8c42012-06-08 19:49:11379}
380
[email protected]6a72a632013-12-12 22:22:00381base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
Devlin Cronind6e136a2018-05-15 23:39:32382 size_t index) const {
383 CHECK_LT(index, prompt_permissions_.details.size());
384 return prompt_permissions_.details[index];
[email protected]8ab7e6c2013-07-11 21:15:03385}
386
[email protected]79a6f99a2013-08-29 00:32:58387bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
388 DetailsType type, size_t index) const {
389 switch (type) {
390 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54391 CHECK_LT(index, prompt_permissions_.is_showing_details.size());
392 return prompt_permissions_.is_showing_details[index];
[email protected]79a6f99a2013-08-29 00:32:58393 case RETAINED_FILES_DETAILS:
394 return is_showing_details_for_retained_files_;
reillygc64d3dd2014-09-29 21:10:11395 case RETAINED_DEVICES_DETAILS:
396 return is_showing_details_for_retained_devices_;
[email protected]79a6f99a2013-08-29 00:32:58397 }
398 return false;
399}
400
[email protected]a2886e8b2013-06-08 05:15:02401size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
402 return retained_files_.size();
403}
404
[email protected]6a72a632013-12-12 22:22:00405base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
406 const {
[email protected]a2886e8b2013-06-08 05:15:02407 CHECK_LT(index, retained_files_.size());
[email protected]6725048e2013-10-24 21:47:14408 return retained_files_[index].AsUTF16Unsafe();
[email protected]a2886e8b2013-06-08 05:15:02409}
410
reillygc64d3dd2014-09-29 21:10:11411size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
412 return retained_device_messages_.size();
413}
414
415base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
416 size_t index) const {
417 CHECK_LT(index, retained_device_messages_.size());
418 return retained_device_messages_[index];
419}
420
421bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
422 return !retained_files_.empty() || !retained_device_messages_.empty();
423}
424
[email protected]a2886e8b2013-06-08 05:15:02425bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
426 return !retained_files_.empty();
427}
428
[email protected]c82da8c42012-06-08 19:49:11429// static
rdevlin.croninf84cab72015-12-12 03:45:23430ExtensionInstallPrompt::PromptType
431ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
432 content::BrowserContext* context,
433 const extensions::Extension* extension) {
434 bool is_remote_install =
435 context &&
436 extensions::ExtensionPrefs::Get(context)->HasDisableReason(
Minh X. Nguyen45479012017-08-18 21:35:36437 extension->id(), extensions::disable_reason::DISABLE_REMOTE_INSTALL);
rdevlin.croninf84cab72015-12-12 03:45:23438
439 return is_remote_install ? REMOTE_INSTALL_PROMPT : RE_ENABLE_PROMPT;
440}
441
442// static
[email protected]c82da8c42012-06-08 19:49:11443scoped_refptr<Extension>
444 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
[email protected]023b3d12013-12-23 18:46:49445 const base::DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13446 int flags,
[email protected]c82da8c42012-06-08 19:49:11447 const std::string& id,
448 const std::string& localized_name,
449 const std::string& localized_description,
450 std::string* error) {
dchengc963c7142016-04-08 03:55:22451 std::unique_ptr<base::DictionaryValue> localized_manifest;
[email protected]c82da8c42012-06-08 19:49:11452 if (!localized_name.empty() || !localized_description.empty()) {
453 localized_manifest.reset(manifest->DeepCopy());
454 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27455 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11456 localized_name);
457 }
458 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27459 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11460 localized_description);
461 }
462 }
463
464 return Extension::Create(
rdevlin.cronin165732a42016-07-18 22:25:08465 base::FilePath(), Manifest::INTERNAL,
466 localized_manifest.get() ? *localized_manifest : *manifest, flags, id,
[email protected]c82da8c42012-06-08 19:49:11467 error);
468}
469
[email protected]7f165342014-02-12 09:22:22470ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
Oscar Johanssondf0dd8f2018-06-18 06:51:07471 : profile_(contents
472 ? Profile::FromBrowserContext(contents->GetBrowserContext())
473 : nullptr),
kylecharb59c3d72019-10-29 05:26:26474 extension_(nullptr),
Oscar Johanssondf0dd8f2018-06-18 06:51:07475 install_ui_(extensions::CreateExtensionInstallUI(profile_)),
pkotwicz2f181782014-10-29 17:33:45476 show_params_(new ExtensionInstallPromptShowParams(contents)),
Jeremy Roman495db682019-07-12 16:03:24477 did_call_show_dialog_(false) {}
[email protected]5db2e882012-12-20 10:17:26478
pkotwicz2175c622014-10-22 19:56:28479ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
480 gfx::NativeWindow native_window)
pkotwicza57a1f322014-10-21 00:24:30481 : profile_(profile),
kylecharb59c3d72019-10-29 05:26:26482 extension_(nullptr),
pkotwicza57a1f322014-10-21 00:24:30483 install_ui_(extensions::CreateExtensionInstallUI(profile)),
pkotwicz2f181782014-10-29 17:33:45484 show_params_(
485 new ExtensionInstallPromptShowParams(profile, native_window)),
Jeremy Roman495db682019-07-12 16:03:24486 did_call_show_dialog_(false) {}
[email protected]c82da8c42012-06-08 19:49:11487
488ExtensionInstallPrompt::~ExtensionInstallPrompt() {
489}
490
rdevlin.croninf84cab72015-12-12 03:45:23491void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12492 const DoneCallback& done_callback,
[email protected]c82da8c42012-06-08 19:49:11493 const Extension* extension,
rdevlin.croninf84cab72015-12-12 03:45:23494 const SkBitmap* icon,
495 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12496 ShowDialog(done_callback, extension, icon,
Jinho Bangb5216cec2018-01-17 19:43:11497 std::make_unique<Prompt>(INSTALL_PROMPT), show_dialog_callback);
rdevlin.cronin2e252692015-12-15 21:47:02498}
499
500void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12501 const DoneCallback& done_callback,
rdevlin.cronin2e252692015-12-15 21:47:02502 const Extension* extension,
503 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22504 std::unique_ptr<Prompt> prompt,
rdevlin.cronin2e252692015-12-15 21:47:02505 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12506 ShowDialog(done_callback, extension, icon, std::move(prompt), nullptr,
rdevlin.croninf84cab72015-12-12 03:45:23507 show_dialog_callback);
508}
509
510void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12511 const DoneCallback& done_callback,
rdevlin.croninf84cab72015-12-12 03:45:23512 const Extension* extension,
513 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22514 std::unique_ptr<Prompt> prompt,
515 std::unique_ptr<const PermissionSet> custom_permissions,
rdevlin.croninf84cab72015-12-12 03:45:23516 const ShowDialogCallback& show_dialog_callback) {
fdorayc16c6f82016-06-29 15:27:32517 DCHECK(ui_thread_checker_.CalledOnValidThread());
rdevlin.cronin2e252692015-12-15 21:47:02518 DCHECK(prompt);
[email protected]c82da8c42012-06-08 19:49:11519 extension_ = extension;
rdevlin.cronin41593052016-01-08 01:40:12520 done_callback_ = done_callback;
rdevlin.croninfc1499f2015-12-21 18:22:00521 if (icon && !icon->empty())
522 SetIcon(icon);
dcheng1fc00f12015-12-26 22:18:03523 prompt_ = std::move(prompt);
524 custom_permissions_ = std::move(custom_permissions);
[email protected]af6efb22012-10-12 02:23:05525 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11526
527 // We special-case themes to not show any confirm UI. Instead they are
528 // immediately installed, and then we show an infobar (see OnInstallSuccess)
529 // to allow the user to revert if they don't like it.
rdevlin.cronin10f34542017-02-06 18:22:47530 if (extension->is_theme() && extension->from_webstore()) {
Daniel Chengd78c0252019-04-26 17:22:11531 std::move(done_callback_).Run(Result::ACCEPTED);
rdevlin.cronin10f34542017-02-06 18:22:47532 return;
[email protected]c82da8c42012-06-08 19:49:11533 }
534
535 LoadImageIfNeeded();
536}
537
Dana Friedc3c30222018-12-11 23:34:56538void ExtensionInstallPrompt::OnInstallSuccess(
539 scoped_refptr<const Extension> extension,
540 SkBitmap* icon) {
[email protected]c82da8c42012-06-08 19:49:11541 extension_ = extension;
542 SetIcon(icon);
543
544 install_ui_->OnInstallSuccess(extension, &icon_);
545}
546
[email protected]bf3d9df2012-07-24 23:20:27547void ExtensionInstallPrompt::OnInstallFailure(
ginkage47e603e2015-02-27 08:42:41548 const extensions::CrxInstallError& error) {
[email protected]c82da8c42012-06-08 19:49:11549 install_ui_->OnInstallFailure(error);
550}
551
552void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
553 if (image)
554 icon_ = *image;
555 else
556 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50557 if (icon_.empty()) {
558 // Let's set default icon bitmap whose size is equal to the default icon's
559 // pixel size under maximal supported scale factor. If the bitmap is larger
560 // than the one we need, it will be scaled down by the ui code.
treibd9e1d9d2015-04-24 11:17:04561 icon_ = GetDefaultIconBitmapForMaxScaleFactor(
562 extension_ ? extension_->is_app() : false);
[email protected]dd46a4ce2012-09-15 10:50:50563 }
[email protected]c82da8c42012-06-08 19:49:11564}
565
[email protected]ec7de0c5a2012-11-16 07:40:47566void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
kylecharb59c3d72019-10-29 05:26:26567 SetIcon(image.IsEmpty() ? nullptr : image.ToSkBitmap());
[email protected]4e494872013-09-06 09:13:54568 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11569}
570
571void ExtensionInstallPrompt::LoadImageIfNeeded() {
treib3d41d9f2016-04-12 08:53:01572 // Don't override an icon that was passed in. Also, |profile_| can be null in
573 // unit tests.
574 if (!icon_.empty() || !profile_) {
[email protected]4e494872013-09-06 09:13:54575 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11576 return;
577 }
578
[email protected]993da5e2013-03-23 21:25:16579 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
Dana Friedc3c30222018-12-11 23:34:56580 extension_.get(), extension_misc::EXTENSION_ICON_LARGE,
[email protected]702d8b42013-02-27 20:55:50581 ExtensionIconSet::MATCH_BIGGER);
[email protected]0d0ba182014-06-03 12:40:43582
583 // Load the image asynchronously. The response will be sent to OnImageLoaded.
pkotwicza57a1f322014-10-21 00:24:30584 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile_);
[email protected]0d0ba182014-06-03 12:40:43585
586 std::vector<extensions::ImageLoader::ImageRepresentation> images_list;
587 images_list.push_back(extensions::ImageLoader::ImageRepresentation(
588 image,
589 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
590 gfx::Size(),
591 ui::SCALE_FACTOR_100P));
Dana Friedc3c30222018-12-11 23:34:56592 loader->LoadImagesAsync(extension_.get(), images_list,
Nigel Taobd12215b2018-11-29 01:10:18593 base::BindOnce(&ExtensionInstallPrompt::OnImageLoaded,
594 weak_factory_.GetWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11595}
596
597void ExtensionInstallPrompt::ShowConfirmation() {
Danan S5f430882020-01-14 04:52:55598 std::unique_ptr<const PermissionSet> permissions_to_display;
599
gpdavis.chromium0fbac4d2014-09-19 20:57:54600 if (custom_permissions_.get()) {
Danan S5f430882020-01-14 04:52:55601 permissions_to_display = custom_permissions_->Clone();
gpdavis.chromium0fbac4d2014-09-19 20:57:54602 } else if (extension_) {
treib7496f63c2015-03-04 12:18:53603 // For delegated installs, all optional permissions are pre-approved by the
604 // person who triggers the install, so add them to the list.
Danan S5f430882020-01-14 04:52:55605 bool include_optional_permissions =
606 prompt_->type() == DELEGATED_PERMISSIONS_PROMPT;
607 permissions_to_display =
608 extensions::util::GetInstallPromptPermissionSetForExtension(
609 extension_.get(), profile_, include_optional_permissions);
[email protected]bebe1d02012-08-02 20:17:09610 }
[email protected]c82da8c42012-06-08 19:49:11611
Dana Friedc3c30222018-12-11 23:34:56612 prompt_->set_extension(extension_.get());
Danan S5f430882020-01-14 04:52:55613 if (permissions_to_display) {
614 prompt_->AddPermissionSet(*permissions_to_display);
615 }
616
treibd9e1d9d2015-04-24 11:17:04617 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]af6efb22012-10-12 02:23:05618
pkotwicz2f181782014-10-29 17:33:45619 if (show_params_->WasParentDestroyed()) {
Daniel Chengd78c0252019-04-26 17:22:11620 std::move(done_callback_).Run(Result::ABORTED);
pkotwicz2f181782014-10-29 17:33:45621 return;
622 }
623
rdevlin.croninca5bf2da2015-12-17 21:21:08624 g_last_prompt_type_for_tests = prompt_->type();
625 did_call_show_dialog_ = true;
626
Daniel Cheng83563bc2019-04-30 23:50:24627 // If true, auto confirm is enabled and already handled the result.
628 if (AutoConfirmPromptIfEnabled())
rdevlin.croninca5bf2da2015-12-17 21:21:08629 return;
630
[email protected]5db2e882012-12-20 10:17:26631 if (show_dialog_callback_.is_null())
rdevlin.cronin41593052016-01-08 01:40:12632 show_dialog_callback_ = GetDefaultShowDialogCallback();
Daniel Chengd59fe5d2019-04-30 00:32:49633 // TODO(https://crbug.com/957713): Use OnceCallback and eliminate the need for
634 // a callback on the stack.
635 auto cb = std::move(done_callback_);
Daniel Chengd78c0252019-04-26 17:22:11636 std::move(show_dialog_callback_)
Daniel Chengd59fe5d2019-04-30 00:32:49637 .Run(show_params_.get(), cb, std::move(prompt_));
[email protected]c82da8c42012-06-08 19:49:11638}
Daniel Cheng83563bc2019-04-30 23:50:24639
640bool ExtensionInstallPrompt::AutoConfirmPromptIfEnabled() {
641 switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
642 case extensions::ScopedTestDialogAutoConfirm::NONE:
643 return false;
644 // We use PostTask instead of calling the callback directly here, because in
645 // the real implementations it's highly likely the message loop will be
646 // pumping a few times before the user clicks accept or cancel.
647 case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
648 case extensions::ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
649 base::ThreadTaskRunnerHandle::Get()->PostTask(
650 FROM_HERE, base::BindOnce(std::move(done_callback_),
651 ExtensionInstallPrompt::Result::ACCEPTED));
652 return true;
653 case extensions::ScopedTestDialogAutoConfirm::CANCEL:
654 base::ThreadTaskRunnerHandle::Get()->PostTask(
655 FROM_HERE,
656 base::BindOnce(std::move(done_callback_),
657 ExtensionInstallPrompt::Result::USER_CANCELED));
658 return true;
659 }
660
661 NOTREACHED();
662 return false;
663}