blob: 465f5a45b55b4962b063441922be13138155e381 [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"
rdevlin.cronin41593052016-01-08 01:40:1210#include "base/callback_helpers.h"
skyostilf221b7de2015-06-11 20:36:3211#include "base/location.h"
fdoray283082bd2016-06-02 20:18:4612#include "base/single_thread_task_runner.h"
[email protected]3ea1b182013-02-08 22:38:4113#include "base/strings/string_number_conversions.h"
[email protected]00e7bef2013-06-10 20:35:1714#include "base/strings/string_util.h"
[email protected]112158af2013-06-07 23:46:1815#include "base/strings/utf_string_conversions.h"
gabb15e19072016-05-11 20:45:4116#include "base/threading/thread_task_runner_handle.h"
pkotwicz2f181782014-10-29 17:33:4517#include "chrome/browser/extensions/extension_install_prompt_show_params.h"
[email protected]7eb20e32014-04-30 08:50:5618#include "chrome/browser/extensions/extension_util.h"
gpdavis.chromium0fbac4d2014-09-19 20:57:5419#include "chrome/browser/extensions/permissions_updater.h"
[email protected]c82da8c42012-06-08 19:49:1120#include "chrome/browser/profiles/profile.h"
pkotwicza57a1f322014-10-21 00:24:3021#include "chrome/browser/ui/extensions/extension_install_ui_factory.h"
[email protected]af39f002014-08-22 10:18:1822#include "chrome/grit/generated_resources.h"
thestig4a2e88e2016-08-27 23:23:5123#include "chrome/grit/theme_resources.h"
24#include "components/strings/grit/components_strings.h"
[email protected]91e51d612012-10-21 23:03:0525#include "content/public/browser/web_contents.h"
Devlin Croninbffe949eb2018-01-12 03:03:4026#include "extensions/browser/disable_reason.h"
rdevlin.cronin66209492015-06-10 20:44:0527#include "extensions/browser/extension_dialog_auto_confirm.h"
[email protected]21db9ef2014-05-16 02:06:2728#include "extensions/browser/extension_prefs.h"
[email protected]411f8ae2014-05-22 11:12:2329#include "extensions/browser/extension_util.h"
[email protected]326e6f02014-06-20 04:53:3730#include "extensions/browser/image_loader.h"
pkotwicza57a1f322014-10-21 00:24:3031#include "extensions/browser/install/extension_install_ui.h"
[email protected]e4452d32013-11-15 23:07:4132#include "extensions/common/extension.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"
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]af6efb22012-10-12 02:23:0572// If auto confirm is enabled then posts a task to proceed with or cancel the
73// install and returns true. Otherwise returns false.
rdevlin.cronin41593052016-01-08 01:40:1274bool AutoConfirmPrompt(ExtensionInstallPrompt::DoneCallback* callback) {
rdevlin.cronin66209492015-06-10 20:44:0575 switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
76 case extensions::ScopedTestDialogAutoConfirm::NONE:
[email protected]2a74d6f62014-07-24 11:53:4777 return false;
rdevlin.cronin41593052016-01-08 01:40:1278 // We use PostTask instead of calling the callback directly here, because in
[email protected]2a74d6f62014-07-24 11:53:4779 // the real implementations it's highly likely the message loop will be
80 // pumping a few times before the user clicks accept or cancel.
rdevlin.cronin66209492015-06-10 20:44:0581 case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
catmullingsff559d92017-05-20 01:43:3682 case extensions::ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
fdoray283082bd2016-06-02 20:18:4683 base::ThreadTaskRunnerHandle::Get()->PostTask(
tzik8d880ee2017-04-20 19:46:2484 FROM_HERE, base::BindOnce(base::ResetAndReturn(callback),
85 ExtensionInstallPrompt::Result::ACCEPTED));
[email protected]2a74d6f62014-07-24 11:53:4786 return true;
rdevlin.cronin66209492015-06-10 20:44:0587 case extensions::ScopedTestDialogAutoConfirm::CANCEL:
skyostilf221b7de2015-06-11 20:36:3288 base::ThreadTaskRunnerHandle::Get()->PostTask(
tzik8d880ee2017-04-20 19:46:2489 FROM_HERE,
90 base::BindOnce(base::ResetAndReturn(callback),
91 ExtensionInstallPrompt::Result::USER_CANCELED));
[email protected]2a74d6f62014-07-24 11:53:4792 return true;
[email protected]af6efb22012-10-12 02:23:0593 }
94
95 NOTREACHED();
96 return false;
97}
98
[email protected]c82da8c42012-06-08 19:49:1199} // namespace
100
gpdavis.chromium0fbac4d2014-09-19 20:57:54101ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
102 InstallPromptPermissions() {
103}
104ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
105 ~InstallPromptPermissions() {
106}
107
rdevlin.cronin092c6772014-11-20 23:52:40108ExtensionInstallPrompt::PromptType
109ExtensionInstallPrompt::g_last_prompt_type_for_tests =
110 ExtensionInstallPrompt::UNSET_PROMPT_TYPE;
111
[email protected]5db2e882012-12-20 10:17:26112ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
[email protected]c82da8c42012-06-08 19:49:11113 : type_(type),
[email protected]79a6f99a2013-08-29 00:32:58114 is_showing_details_for_retained_files_(false),
reillygc64d3dd2014-09-29 21:10:11115 is_showing_details_for_retained_devices_(false),
[email protected]c82da8c42012-06-08 19:49:11116 extension_(NULL),
[email protected]c82da8c42012-06-08 19:49:11117 average_rating_(0.0),
[email protected]dcde34b32013-07-31 02:28:45118 rating_count_(0),
[email protected]34b5f7f2014-01-29 02:48:11119 show_user_count_(false),
120 has_webstore_data_(false) {
rdevlin.croninc7f8d132017-02-22 14:29:02121 DCHECK_NE(type_, UNSET_PROMPT_TYPE);
122 DCHECK_NE(type_, NUM_PROMPT_TYPES);
[email protected]c82da8c42012-06-08 19:49:11123}
124
125ExtensionInstallPrompt::Prompt::~Prompt() {
126}
127
isandrk98a3e4a12017-05-26 21:14:50128void ExtensionInstallPrompt::Prompt::AddPermissions(
Devlin Cronind6e136a2018-05-15 23:39:32129 const PermissionMessages& permissions) {
treibf9dce312015-08-27 17:33:35130 for (const PermissionMessage& msg : permissions) {
Devlin Cronind6e136a2018-05-15 23:39:32131 prompt_permissions_.permissions.push_back(msg.message());
treib7b45a34b2015-04-16 11:48:02132 // Add a dash to the front of each permission detail.
133 base::string16 details;
treib8d2de482015-08-04 16:36:48134 if (!msg.submessages().empty()) {
sashabeabd5df2014-11-06 02:38:25135 std::vector<base::string16> detail_lines_with_bullets;
treib8d2de482015-08-04 16:36:48136 for (const auto& detail_line : msg.submessages()) {
sashabeabd5df2014-11-06 02:38:25137 detail_lines_with_bullets.push_back(base::ASCIIToUTF16("- ") +
138 detail_line);
treib7b45a34b2015-04-16 11:48:02139 }
sashabeabd5df2014-11-06 02:38:25140
brettwd94a22142015-07-15 05:19:26141 details = base::JoinString(detail_lines_with_bullets,
142 base::ASCIIToUTF16("\n"));
sashabeabd5df2014-11-06 02:38:25143 }
Devlin Cronind6e136a2018-05-15 23:39:32144 prompt_permissions_.details.push_back(details);
145 prompt_permissions_.is_showing_details.push_back(false);
sashabeabd5df2014-11-06 02:38:25146 }
[email protected]79a6f99a2013-08-29 00:32:58147}
148
149void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
150 DetailsType type,
151 size_t index,
152 bool is_showing_details) {
153 switch (type) {
154 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54155 prompt_permissions_.is_showing_details[index] = is_showing_details;
156 break;
[email protected]79a6f99a2013-08-29 00:32:58157 case RETAINED_FILES_DETAILS:
158 is_showing_details_for_retained_files_ = is_showing_details;
159 break;
reillygc64d3dd2014-09-29 21:10:11160 case RETAINED_DEVICES_DETAILS:
161 is_showing_details_for_retained_devices_ = is_showing_details;
162 break;
[email protected]79a6f99a2013-08-29 00:32:58163 }
[email protected]8ab7e6c2013-07-11 21:15:03164}
165
[email protected]34b5f7f2014-01-29 02:48:11166void ExtensionInstallPrompt::Prompt::SetWebstoreData(
[email protected]c82da8c42012-06-08 19:49:11167 const std::string& localized_user_count,
[email protected]dcde34b32013-07-31 02:28:45168 bool show_user_count,
[email protected]c82da8c42012-06-08 19:49:11169 double average_rating,
170 int rating_count) {
rdevlin.cronin5f6b69d2014-09-20 01:23:35171 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11172 localized_user_count_ = localized_user_count;
[email protected]dcde34b32013-07-31 02:28:45173 show_user_count_ = show_user_count;
[email protected]c82da8c42012-06-08 19:49:11174 average_rating_ = average_rating;
175 rating_count_ = rating_count;
[email protected]34b5f7f2014-01-29 02:48:11176 has_webstore_data_ = true;
[email protected]c82da8c42012-06-08 19:49:11177}
178
[email protected]6a72a632013-12-12 22:22:00179base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
rdevlin.croninc7f8d132017-02-22 14:29:02180 int id = -1;
181 switch (type_) {
182 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41183 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02184 id = IDS_EXTENSION_INSTALL_PROMPT_TITLE;
185 break;
186 case RE_ENABLE_PROMPT:
187 id = IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE;
188 break;
189 case PERMISSIONS_PROMPT:
190 id = IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE;
191 break;
192 case EXTERNAL_INSTALL_PROMPT:
193 if (extension_->is_app())
194 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP;
195 else if (extension_->is_theme())
196 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME;
197 else
198 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION;
199 break;
200 case POST_INSTALL_PERMISSIONS_PROMPT:
201 id = IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE;
202 break;
203 case REMOTE_INSTALL_PROMPT:
204 id = IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE;
205 break;
206 case REPAIR_PROMPT:
207 id = IDS_EXTENSION_REPAIR_PROMPT_TITLE;
208 break;
209 case DELEGATED_PERMISSIONS_PROMPT:
210 // Special case: need to include the delegated username.
211 return l10n_util::GetStringFUTF16(
212 IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE,
213 base::UTF8ToUTF16(extension_->name()),
214 base::UTF8ToUTF16(delegated_username_));
215 case UNSET_PROMPT_TYPE:
216 case NUM_PROMPT_TYPES:
217 NOTREACHED();
treibd35862252015-06-15 12:50:51218 }
rdevlin.croninc7f8d132017-02-22 14:29:02219
treibd35862252015-06-15 12:50:51220 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11221}
222
[email protected]15d267b42013-02-14 23:43:32223int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
rdevlin.croninc7f8d132017-02-22 14:29:02224 // The "OK" button in the post install permissions dialog allows revoking
225 // file/device access, and is only shown if such permissions exist; see
226 // ShouldDisplayRevokeButton().
227 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
228 !ShouldDisplayRevokeButton()) {
229 return ui::DIALOG_BUTTON_CANCEL;
[email protected]a2886e8b2013-06-08 05:15:02230 }
rdevlin.croninc7f8d132017-02-22 14:29:02231 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
[email protected]15d267b42013-02-14 23:43:32232}
233
[email protected]6a72a632013-12-12 22:22:00234base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02235 int id = -1;
236 switch (type_) {
237 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41238 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02239 if (extension_->is_app())
240 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
241 else if (extension_->is_theme())
242 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
243 else
244 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
245 break;
246 case RE_ENABLE_PROMPT:
247 id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON;
248 break;
249 case PERMISSIONS_PROMPT:
250 id = IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON;
251 break;
252 case EXTERNAL_INSTALL_PROMPT:
253 if (extension_->is_app())
254 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
255 else if (extension_->is_theme())
256 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
257 else
258 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
259 break;
260 case POST_INSTALL_PERMISSIONS_PROMPT:
261 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
262 id =
263 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON;
264 } else if (GetRetainedFileCount()) {
265 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON;
266 } else if (GetRetainedDeviceCount()) {
267 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON;
268 }
269 // If there are neither retained files nor devices, leave id -1 so there
270 // will be no "accept" button.
271 break;
272 case REMOTE_INSTALL_PROMPT:
273 if (extension_->is_app())
274 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_APP;
275 else
276 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_EXTENSION;
277 break;
278 case REPAIR_PROMPT:
279 if (extension_->is_app())
280 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_APP;
281 else
282 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_EXTENSION;
283 break;
284 case DELEGATED_PERMISSIONS_PROMPT:
285 id = IDS_EXTENSION_PROMPT_INSTALL_BUTTON;
286 break;
287 case UNSET_PROMPT_TYPE:
288 case NUM_PROMPT_TYPES:
289 NOTREACHED();
[email protected]846606012012-10-19 18:42:25290 }
rdevlin.croninc7f8d132017-02-22 14:29:02291
292 return id != -1 ? l10n_util::GetStringUTF16(id) : base::string16();
[email protected]c82da8c42012-06-08 19:49:11293}
294
[email protected]6a72a632013-12-12 22:22:00295base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02296 int id = -1;
297 switch (type_) {
298 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41299 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02300 case RE_ENABLE_PROMPT:
301 case REMOTE_INSTALL_PROMPT:
302 case REPAIR_PROMPT:
303 case DELEGATED_PERMISSIONS_PROMPT:
304 id = IDS_CANCEL;
305 break;
306 case PERMISSIONS_PROMPT:
307 id = IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON;
308 break;
309 case EXTERNAL_INSTALL_PROMPT:
310 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON;
311 break;
312 case POST_INSTALL_PERMISSIONS_PROMPT:
313 id = IDS_CLOSE;
314 break;
315 case UNSET_PROMPT_TYPE:
316 case NUM_PROMPT_TYPES:
317 NOTREACHED();
318 }
319
320 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11321}
322
Devlin Cronind6e136a2018-05-15 23:39:32323base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
324 int id = -1;
325 switch (type_) {
326 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41327 case WEBSTORE_WIDGET_PROMPT:
Devlin Cronind6e136a2018-05-15 23:39:32328 case EXTERNAL_INSTALL_PROMPT:
329 case REMOTE_INSTALL_PROMPT:
330 case DELEGATED_PERMISSIONS_PROMPT:
331 id = IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO;
332 break;
333 case RE_ENABLE_PROMPT:
334 id = IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO;
335 break;
336 case PERMISSIONS_PROMPT:
337 id = IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO;
338 break;
339 case POST_INSTALL_PERMISSIONS_PROMPT:
340 case REPAIR_PROMPT:
341 id = IDS_EXTENSION_PROMPT_CAN_ACCESS;
342 break;
343 case UNSET_PROMPT_TYPE:
344 case NUM_PROMPT_TYPES:
gpdavis.chromium0fbac4d2014-09-19 20:57:54345 NOTREACHED();
gpdavis.chromium0fbac4d2014-09-19 20:57:54346 }
Devlin Cronind6e136a2018-05-15 23:39:32347 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11348}
349
[email protected]6a72a632013-12-12 22:22:00350base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
jshin3a6c36bb2015-04-09 21:58:08351 return l10n_util::GetPluralStringFUTF16(
352 IDS_EXTENSION_PROMPT_RETAINED_FILES, GetRetainedFileCount());
[email protected]8ab7e6c2013-07-11 21:15:03353}
354
reillygc64d3dd2014-09-29 21:10:11355base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
356 const {
jshin3a6c36bb2015-04-09 21:58:08357 return l10n_util::GetPluralStringFUTF16(
358 IDS_EXTENSION_PROMPT_RETAINED_DEVICES, GetRetainedDeviceCount());
reillygc64d3dd2014-09-29 21:10:11359}
360
[email protected]f2cd8992013-06-11 17:30:18361bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
Devlin Cronind6e136a2018-05-15 23:39:32362 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
[email protected]f2cd8992013-06-11 17:30:18363}
364
[email protected]c82da8c42012-06-08 19:49:11365void ExtensionInstallPrompt::Prompt::AppendRatingStars(
366 StarAppender appender, void* data) const {
367 CHECK(appender);
rdevlin.cronin5f6b69d2014-09-20 01:23:35368 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11369 int rating_integer = floor(average_rating_);
370 double rating_fractional = average_rating_ - rating_integer;
371
372 if (rating_fractional > 0.66) {
373 rating_integer++;
374 }
375
376 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
377 rating_fractional = 0;
378 }
379
Lei Zhang7640d542017-10-03 16:26:49380 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
[email protected]c82da8c42012-06-08 19:49:11381 int i;
382 for (i = 0; i < rating_integer; i++) {
383 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
384 }
385 if (rating_fractional) {
386 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
387 i++;
388 }
389 for (; i < kMaxExtensionRating; i++) {
390 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
391 }
392}
393
[email protected]6a72a632013-12-12 22:22:00394base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35395 CHECK(AllowWebstoreData(type_));
[email protected]6725048e2013-10-24 21:47:14396 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
397 base::IntToString16(rating_count_));
[email protected]c82da8c42012-06-08 19:49:11398}
399
[email protected]6a72a632013-12-12 22:22:00400base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35401 CHECK(AllowWebstoreData(type_));
[email protected]dcde34b32013-07-31 02:28:45402
403 if (show_user_count_) {
[email protected]6725048e2013-10-24 21:47:14404 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
405 base::UTF8ToUTF16(localized_user_count_));
[email protected]dcde34b32013-07-31 02:28:45406 }
[email protected]6725048e2013-10-24 21:47:14407 return base::string16();
[email protected]c82da8c42012-06-08 19:49:11408}
409
Devlin Cronind6e136a2018-05-15 23:39:32410size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
411 return prompt_permissions_.permissions.size();
[email protected]c82da8c42012-06-08 19:49:11412}
413
Devlin Cronind6e136a2018-05-15 23:39:32414size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
415 return prompt_permissions_.details.size();
[email protected]8ab7e6c2013-07-11 21:15:03416}
417
gpdavis.chromium0fbac4d2014-09-19 20:57:54418base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
Devlin Cronind6e136a2018-05-15 23:39:32419 size_t index) const {
420 CHECK_LT(index, prompt_permissions_.permissions.size());
421 return prompt_permissions_.permissions[index];
[email protected]c82da8c42012-06-08 19:49:11422}
423
[email protected]6a72a632013-12-12 22:22:00424base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
Devlin Cronind6e136a2018-05-15 23:39:32425 size_t index) const {
426 CHECK_LT(index, prompt_permissions_.details.size());
427 return prompt_permissions_.details[index];
[email protected]8ab7e6c2013-07-11 21:15:03428}
429
[email protected]79a6f99a2013-08-29 00:32:58430bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
431 DetailsType type, size_t index) const {
432 switch (type) {
433 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54434 CHECK_LT(index, prompt_permissions_.is_showing_details.size());
435 return prompt_permissions_.is_showing_details[index];
[email protected]79a6f99a2013-08-29 00:32:58436 case RETAINED_FILES_DETAILS:
437 return is_showing_details_for_retained_files_;
reillygc64d3dd2014-09-29 21:10:11438 case RETAINED_DEVICES_DETAILS:
439 return is_showing_details_for_retained_devices_;
[email protected]79a6f99a2013-08-29 00:32:58440 }
441 return false;
442}
443
[email protected]a2886e8b2013-06-08 05:15:02444size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
445 return retained_files_.size();
446}
447
[email protected]6a72a632013-12-12 22:22:00448base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
449 const {
[email protected]a2886e8b2013-06-08 05:15:02450 CHECK_LT(index, retained_files_.size());
[email protected]6725048e2013-10-24 21:47:14451 return retained_files_[index].AsUTF16Unsafe();
[email protected]a2886e8b2013-06-08 05:15:02452}
453
reillygc64d3dd2014-09-29 21:10:11454size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
455 return retained_device_messages_.size();
456}
457
458base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
459 size_t index) const {
460 CHECK_LT(index, retained_device_messages_.size());
461 return retained_device_messages_[index];
462}
463
464bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
465 return !retained_files_.empty() || !retained_device_messages_.empty();
466}
467
[email protected]a2886e8b2013-06-08 05:15:02468bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
469 return !retained_files_.empty();
470}
471
[email protected]c82da8c42012-06-08 19:49:11472// static
rdevlin.croninf84cab72015-12-12 03:45:23473ExtensionInstallPrompt::PromptType
474ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
475 content::BrowserContext* context,
476 const extensions::Extension* extension) {
477 bool is_remote_install =
478 context &&
479 extensions::ExtensionPrefs::Get(context)->HasDisableReason(
Minh X. Nguyen45479012017-08-18 21:35:36480 extension->id(), extensions::disable_reason::DISABLE_REMOTE_INSTALL);
rdevlin.croninf84cab72015-12-12 03:45:23481
482 return is_remote_install ? REMOTE_INSTALL_PROMPT : RE_ENABLE_PROMPT;
483}
484
485// static
[email protected]c82da8c42012-06-08 19:49:11486scoped_refptr<Extension>
487 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
[email protected]023b3d12013-12-23 18:46:49488 const base::DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13489 int flags,
[email protected]c82da8c42012-06-08 19:49:11490 const std::string& id,
491 const std::string& localized_name,
492 const std::string& localized_description,
493 std::string* error) {
dchengc963c7142016-04-08 03:55:22494 std::unique_ptr<base::DictionaryValue> localized_manifest;
[email protected]c82da8c42012-06-08 19:49:11495 if (!localized_name.empty() || !localized_description.empty()) {
496 localized_manifest.reset(manifest->DeepCopy());
497 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27498 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11499 localized_name);
500 }
501 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27502 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11503 localized_description);
504 }
505 }
506
507 return Extension::Create(
rdevlin.cronin165732a42016-07-18 22:25:08508 base::FilePath(), Manifest::INTERNAL,
509 localized_manifest.get() ? *localized_manifest : *manifest, flags, id,
[email protected]c82da8c42012-06-08 19:49:11510 error);
511}
512
[email protected]7f165342014-02-12 09:22:22513ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
Oscar Johanssondf0dd8f2018-06-18 06:51:07514 : profile_(contents
515 ? Profile::FromBrowserContext(contents->GetBrowserContext())
516 : nullptr),
[email protected]c82da8c42012-06-08 19:49:11517 extension_(NULL),
Oscar Johanssondf0dd8f2018-06-18 06:51:07518 install_ui_(extensions::CreateExtensionInstallUI(profile_)),
pkotwicz2f181782014-10-29 17:33:45519 show_params_(new ExtensionInstallPromptShowParams(contents)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40520 did_call_show_dialog_(false),
Oscar Johanssondf0dd8f2018-06-18 06:51:07521 weak_factory_(this) {}
[email protected]5db2e882012-12-20 10:17:26522
pkotwicz2175c622014-10-22 19:56:28523ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
524 gfx::NativeWindow native_window)
pkotwicza57a1f322014-10-21 00:24:30525 : profile_(profile),
[email protected]5db2e882012-12-20 10:17:26526 extension_(NULL),
pkotwicza57a1f322014-10-21 00:24:30527 install_ui_(extensions::CreateExtensionInstallUI(profile)),
pkotwicz2f181782014-10-29 17:33:45528 show_params_(
529 new ExtensionInstallPromptShowParams(profile, native_window)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40530 did_call_show_dialog_(false),
531 weak_factory_(this) {
[email protected]d382baa2014-06-17 18:50:01532}
[email protected]c82da8c42012-06-08 19:49:11533
534ExtensionInstallPrompt::~ExtensionInstallPrompt() {
535}
536
rdevlin.croninf84cab72015-12-12 03:45:23537void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12538 const DoneCallback& done_callback,
[email protected]c82da8c42012-06-08 19:49:11539 const Extension* extension,
rdevlin.croninf84cab72015-12-12 03:45:23540 const SkBitmap* icon,
541 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12542 ShowDialog(done_callback, extension, icon,
Jinho Bangb5216cec2018-01-17 19:43:11543 std::make_unique<Prompt>(INSTALL_PROMPT), show_dialog_callback);
rdevlin.cronin2e252692015-12-15 21:47:02544}
545
546void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12547 const DoneCallback& done_callback,
rdevlin.cronin2e252692015-12-15 21:47:02548 const Extension* extension,
549 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22550 std::unique_ptr<Prompt> prompt,
rdevlin.cronin2e252692015-12-15 21:47:02551 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12552 ShowDialog(done_callback, extension, icon, std::move(prompt), nullptr,
rdevlin.croninf84cab72015-12-12 03:45:23553 show_dialog_callback);
554}
555
556void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12557 const DoneCallback& done_callback,
rdevlin.croninf84cab72015-12-12 03:45:23558 const Extension* extension,
559 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22560 std::unique_ptr<Prompt> prompt,
561 std::unique_ptr<const PermissionSet> custom_permissions,
rdevlin.croninf84cab72015-12-12 03:45:23562 const ShowDialogCallback& show_dialog_callback) {
fdorayc16c6f82016-06-29 15:27:32563 DCHECK(ui_thread_checker_.CalledOnValidThread());
rdevlin.cronin2e252692015-12-15 21:47:02564 DCHECK(prompt);
[email protected]c82da8c42012-06-08 19:49:11565 extension_ = extension;
rdevlin.cronin41593052016-01-08 01:40:12566 done_callback_ = done_callback;
rdevlin.croninfc1499f2015-12-21 18:22:00567 if (icon && !icon->empty())
568 SetIcon(icon);
dcheng1fc00f12015-12-26 22:18:03569 prompt_ = std::move(prompt);
570 custom_permissions_ = std::move(custom_permissions);
[email protected]af6efb22012-10-12 02:23:05571 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11572
573 // We special-case themes to not show any confirm UI. Instead they are
574 // immediately installed, and then we show an infobar (see OnInstallSuccess)
575 // to allow the user to revert if they don't like it.
rdevlin.cronin10f34542017-02-06 18:22:47576 if (extension->is_theme() && extension->from_webstore()) {
577 base::ResetAndReturn(&done_callback_).Run(Result::ACCEPTED);
578 return;
[email protected]c82da8c42012-06-08 19:49:11579 }
580
581 LoadImageIfNeeded();
582}
583
Dana Friedc3c30222018-12-11 23:34:56584void ExtensionInstallPrompt::OnInstallSuccess(
585 scoped_refptr<const Extension> extension,
586 SkBitmap* icon) {
[email protected]c82da8c42012-06-08 19:49:11587 extension_ = extension;
588 SetIcon(icon);
589
590 install_ui_->OnInstallSuccess(extension, &icon_);
591}
592
[email protected]bf3d9df2012-07-24 23:20:27593void ExtensionInstallPrompt::OnInstallFailure(
ginkage47e603e2015-02-27 08:42:41594 const extensions::CrxInstallError& error) {
[email protected]c82da8c42012-06-08 19:49:11595 install_ui_->OnInstallFailure(error);
596}
597
598void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
599 if (image)
600 icon_ = *image;
601 else
602 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50603 if (icon_.empty()) {
604 // Let's set default icon bitmap whose size is equal to the default icon's
605 // pixel size under maximal supported scale factor. If the bitmap is larger
606 // than the one we need, it will be scaled down by the ui code.
treibd9e1d9d2015-04-24 11:17:04607 icon_ = GetDefaultIconBitmapForMaxScaleFactor(
608 extension_ ? extension_->is_app() : false);
[email protected]dd46a4ce2012-09-15 10:50:50609 }
[email protected]c82da8c42012-06-08 19:49:11610}
611
[email protected]ec7de0c5a2012-11-16 07:40:47612void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
[email protected]c82da8c42012-06-08 19:49:11613 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
[email protected]4e494872013-09-06 09:13:54614 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11615}
616
617void ExtensionInstallPrompt::LoadImageIfNeeded() {
treib3d41d9f2016-04-12 08:53:01618 // Don't override an icon that was passed in. Also, |profile_| can be null in
619 // unit tests.
620 if (!icon_.empty() || !profile_) {
[email protected]4e494872013-09-06 09:13:54621 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11622 return;
623 }
624
[email protected]993da5e2013-03-23 21:25:16625 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
Dana Friedc3c30222018-12-11 23:34:56626 extension_.get(), extension_misc::EXTENSION_ICON_LARGE,
[email protected]702d8b42013-02-27 20:55:50627 ExtensionIconSet::MATCH_BIGGER);
[email protected]0d0ba182014-06-03 12:40:43628
629 // Load the image asynchronously. The response will be sent to OnImageLoaded.
pkotwicza57a1f322014-10-21 00:24:30630 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile_);
[email protected]0d0ba182014-06-03 12:40:43631
632 std::vector<extensions::ImageLoader::ImageRepresentation> images_list;
633 images_list.push_back(extensions::ImageLoader::ImageRepresentation(
634 image,
635 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
636 gfx::Size(),
637 ui::SCALE_FACTOR_100P));
Dana Friedc3c30222018-12-11 23:34:56638 loader->LoadImagesAsync(extension_.get(), images_list,
Nigel Taobd12215b2018-11-29 01:10:18639 base::BindOnce(&ExtensionInstallPrompt::OnImageLoaded,
640 weak_factory_.GetWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11641}
642
643void ExtensionInstallPrompt::ShowConfirmation() {
dchengc963c7142016-04-08 03:55:22644 std::unique_ptr<const PermissionSet> permissions_wrapper;
rdevlin.cronine2d0fd02015-09-24 22:35:49645 const PermissionSet* permissions_to_display = nullptr;
gpdavis.chromium0fbac4d2014-09-19 20:57:54646 if (custom_permissions_.get()) {
rdevlin.cronine2d0fd02015-09-24 22:35:49647 permissions_to_display = custom_permissions_.get();
gpdavis.chromium0fbac4d2014-09-19 20:57:54648 } else if (extension_) {
649 // Initialize permissions if they have not already been set so that
Devlin Cronind6e136a2018-05-15 23:39:32650 // any transformations are correctly reflected in the install prompt.
gpdavis.chromium0fbac4d2014-09-19 20:57:54651 extensions::PermissionsUpdater(
pkotwicza57a1f322014-10-21 00:24:30652 profile_, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT)
Dana Friedc3c30222018-12-11 23:34:56653 .InitializePermissions(extension_.get());
gpdavis.chromium0fbac4d2014-09-19 20:57:54654 permissions_to_display =
rdevlin.cronind630c302015-09-30 20:19:33655 &extension_->permissions_data()->active_permissions();
treib7496f63c2015-03-04 12:18:53656 // For delegated installs, all optional permissions are pre-approved by the
657 // person who triggers the install, so add them to the list.
treib3d41d9f2016-04-12 08:53:01658 if (prompt_->type() == DELEGATED_PERMISSIONS_PROMPT) {
rdevlin.cronind630c302015-09-30 20:19:33659 const PermissionSet& optional_permissions =
Dana Friedc3c30222018-12-11 23:34:56660 extensions::PermissionsParser::GetOptionalPermissions(
661 extension_.get());
rdevlin.cronine2d0fd02015-09-24 22:35:49662 permissions_wrapper = PermissionSet::CreateUnion(*permissions_to_display,
rdevlin.cronind630c302015-09-30 20:19:33663 optional_permissions);
rdevlin.cronine2d0fd02015-09-24 22:35:49664 permissions_to_display = permissions_wrapper.get();
treib7496f63c2015-03-04 12:18:53665 }
gpdavis.chromium0fbac4d2014-09-19 20:57:54666 }
667
Devlin Cronin007e4fd52018-06-08 22:06:00668 if (permissions_to_display) {
[email protected]a397ec02014-08-08 15:48:13669 Manifest::Type type =
670 extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN;
671 const extensions::PermissionMessageProvider* message_provider =
672 extensions::PermissionMessageProvider::Get();
treib7b45a34b2015-04-16 11:48:02673
isandrk98a3e4a12017-05-26 21:14:50674 prompt_->AddPermissions(message_provider->GetPermissionMessages(
Devlin Cronind6e136a2018-05-15 23:39:32675 message_provider->GetAllPermissionIDs(*permissions_to_display, type)));
[email protected]bebe1d02012-08-02 20:17:09676 }
[email protected]c82da8c42012-06-08 19:49:11677
Dana Friedc3c30222018-12-11 23:34:56678 prompt_->set_extension(extension_.get());
treibd9e1d9d2015-04-24 11:17:04679 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]af6efb22012-10-12 02:23:05680
pkotwicz2f181782014-10-29 17:33:45681 if (show_params_->WasParentDestroyed()) {
rdevlin.cronin41593052016-01-08 01:40:12682 base::ResetAndReturn(&done_callback_).Run(Result::ABORTED);
pkotwicz2f181782014-10-29 17:33:45683 return;
684 }
685
rdevlin.croninca5bf2da2015-12-17 21:21:08686 g_last_prompt_type_for_tests = prompt_->type();
687 did_call_show_dialog_ = true;
688
rdevlin.cronin41593052016-01-08 01:40:12689 if (AutoConfirmPrompt(&done_callback_))
rdevlin.croninca5bf2da2015-12-17 21:21:08690 return;
691
[email protected]5db2e882012-12-20 10:17:26692 if (show_dialog_callback_.is_null())
rdevlin.cronin41593052016-01-08 01:40:12693 show_dialog_callback_ = GetDefaultShowDialogCallback();
694 base::ResetAndReturn(&show_dialog_callback_)
695 .Run(show_params_.get(), base::ResetAndReturn(&done_callback_),
696 std::move(prompt_));
[email protected]c82da8c42012-06-08 19:49:11697}