blob: 5d8e6a30dcec34e35ccd6c66bc58c19012db75c8 [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"
treibf9dce312015-08-27 17:33:3539#include "extensions/common/permissions/permission_message_provider.h"
[email protected]5a55f3f2013-10-29 01:08:2940#include "extensions/common/permissions/permission_set.h"
[email protected]e4452d32013-11-15 23:07:4141#include "extensions/common/permissions/permissions_data.h"
[email protected]c82da8c42012-06-08 19:49:1142#include "ui/base/l10n/l10n_util.h"
43#include "ui/base/resource/resource_bundle.h"
pkotwicz7fd01192014-10-09 04:43:5044#include "ui/base/ui_base_types.h"
rdevlin.cronin3fe4bd32016-01-12 18:45:4045#include "ui/gfx/image/image_skia.h"
[email protected]c82da8c42012-06-08 19:49:1146
[email protected]c82da8c42012-06-08 19:49:1147using extensions::Extension;
[email protected]1d5e58b2013-01-31 08:41:4048using extensions::Manifest;
treibf9dce312015-08-27 17:33:3549using extensions::PermissionMessage;
50using extensions::PermissionMessages;
[email protected]c2e66e12012-06-27 06:27:0651using extensions::PermissionSet;
[email protected]c82da8c42012-06-08 19:49:1152
[email protected]612a1cb12012-10-17 13:18:0353namespace {
54
rdevlin.cronin5f6b69d2014-09-20 01:23:3555bool AllowWebstoreData(ExtensionInstallPrompt::PromptType type) {
Benjamin Ackermanc4fb02302018-10-03 19:55:4156 return type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT ||
57 type == ExtensionInstallPrompt::REPAIR_PROMPT ||
58 type == ExtensionInstallPrompt::WEBSTORE_WIDGET_PROMPT;
rdevlin.cronin5f6b69d2014-09-20 01:23:3559}
60
[email protected]dd46a4ce2012-09-15 10:50:5061// Returns bitmap for the default icon with size equal to the default icon's
62// pixel size under maximal supported scale factor.
63SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
[email protected]702d8b42013-02-27 20:55:5064 const gfx::ImageSkia& image = is_app ?
[email protected]7eb20e32014-04-30 08:50:5665 extensions::util::GetDefaultAppIcon() :
66 extensions::util::GetDefaultExtensionIcon();
Malay Keshave8c63be2018-10-02 00:12:2467 return image.GetRepresentation(gfx::ImageSkia::GetMaxSupportedScale())
68 .GetBitmap();
[email protected]dd46a4ce2012-09-15 10:50:5069}
70
[email protected]af6efb22012-10-12 02:23:0571// If auto confirm is enabled then posts a task to proceed with or cancel the
72// install and returns true. Otherwise returns false.
rdevlin.cronin41593052016-01-08 01:40:1273bool AutoConfirmPrompt(ExtensionInstallPrompt::DoneCallback* callback) {
rdevlin.cronin66209492015-06-10 20:44:0574 switch (extensions::ScopedTestDialogAutoConfirm::GetAutoConfirmValue()) {
75 case extensions::ScopedTestDialogAutoConfirm::NONE:
[email protected]2a74d6f62014-07-24 11:53:4776 return false;
rdevlin.cronin41593052016-01-08 01:40:1277 // We use PostTask instead of calling the callback directly here, because in
[email protected]2a74d6f62014-07-24 11:53:4778 // the real implementations it's highly likely the message loop will be
79 // pumping a few times before the user clicks accept or cancel.
rdevlin.cronin66209492015-06-10 20:44:0580 case extensions::ScopedTestDialogAutoConfirm::ACCEPT:
catmullingsff559d92017-05-20 01:43:3681 case extensions::ScopedTestDialogAutoConfirm::ACCEPT_AND_OPTION:
fdoray283082bd2016-06-02 20:18:4682 base::ThreadTaskRunnerHandle::Get()->PostTask(
tzik8d880ee2017-04-20 19:46:2483 FROM_HERE, base::BindOnce(base::ResetAndReturn(callback),
84 ExtensionInstallPrompt::Result::ACCEPTED));
[email protected]2a74d6f62014-07-24 11:53:4785 return true;
rdevlin.cronin66209492015-06-10 20:44:0586 case extensions::ScopedTestDialogAutoConfirm::CANCEL:
skyostilf221b7de2015-06-11 20:36:3287 base::ThreadTaskRunnerHandle::Get()->PostTask(
tzik8d880ee2017-04-20 19:46:2488 FROM_HERE,
89 base::BindOnce(base::ResetAndReturn(callback),
90 ExtensionInstallPrompt::Result::USER_CANCELED));
[email protected]2a74d6f62014-07-24 11:53:4791 return true;
[email protected]af6efb22012-10-12 02:23:0592 }
93
94 NOTREACHED();
95 return false;
96}
97
[email protected]c82da8c42012-06-08 19:49:1198} // namespace
99
gpdavis.chromium0fbac4d2014-09-19 20:57:54100ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
101 InstallPromptPermissions() {
102}
103ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
104 ~InstallPromptPermissions() {
105}
106
rdevlin.cronin092c6772014-11-20 23:52:40107ExtensionInstallPrompt::PromptType
108ExtensionInstallPrompt::g_last_prompt_type_for_tests =
109 ExtensionInstallPrompt::UNSET_PROMPT_TYPE;
110
[email protected]5db2e882012-12-20 10:17:26111ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
[email protected]c82da8c42012-06-08 19:49:11112 : type_(type),
[email protected]79a6f99a2013-08-29 00:32:58113 is_showing_details_for_retained_files_(false),
reillygc64d3dd2014-09-29 21:10:11114 is_showing_details_for_retained_devices_(false),
[email protected]c82da8c42012-06-08 19:49:11115 extension_(NULL),
[email protected]c82da8c42012-06-08 19:49:11116 average_rating_(0.0),
[email protected]dcde34b32013-07-31 02:28:45117 rating_count_(0),
[email protected]34b5f7f2014-01-29 02:48:11118 show_user_count_(false),
119 has_webstore_data_(false) {
rdevlin.croninc7f8d132017-02-22 14:29:02120 DCHECK_NE(type_, UNSET_PROMPT_TYPE);
121 DCHECK_NE(type_, NUM_PROMPT_TYPES);
[email protected]c82da8c42012-06-08 19:49:11122}
123
124ExtensionInstallPrompt::Prompt::~Prompt() {
125}
126
isandrk98a3e4a12017-05-26 21:14:50127void ExtensionInstallPrompt::Prompt::AddPermissions(
Devlin Cronind6e136a2018-05-15 23:39:32128 const PermissionMessages& permissions) {
treibf9dce312015-08-27 17:33:35129 for (const PermissionMessage& msg : permissions) {
Devlin Cronind6e136a2018-05-15 23:39:32130 prompt_permissions_.permissions.push_back(msg.message());
treib7b45a34b2015-04-16 11:48:02131 // Add a dash to the front of each permission detail.
132 base::string16 details;
treib8d2de482015-08-04 16:36:48133 if (!msg.submessages().empty()) {
sashabeabd5df2014-11-06 02:38:25134 std::vector<base::string16> detail_lines_with_bullets;
treib8d2de482015-08-04 16:36:48135 for (const auto& detail_line : msg.submessages()) {
sashabeabd5df2014-11-06 02:38:25136 detail_lines_with_bullets.push_back(base::ASCIIToUTF16("- ") +
137 detail_line);
treib7b45a34b2015-04-16 11:48:02138 }
sashabeabd5df2014-11-06 02:38:25139
brettwd94a22142015-07-15 05:19:26140 details = base::JoinString(detail_lines_with_bullets,
141 base::ASCIIToUTF16("\n"));
sashabeabd5df2014-11-06 02:38:25142 }
Devlin Cronind6e136a2018-05-15 23:39:32143 prompt_permissions_.details.push_back(details);
144 prompt_permissions_.is_showing_details.push_back(false);
sashabeabd5df2014-11-06 02:38:25145 }
[email protected]79a6f99a2013-08-29 00:32:58146}
147
148void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
149 DetailsType type,
150 size_t index,
151 bool is_showing_details) {
152 switch (type) {
153 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54154 prompt_permissions_.is_showing_details[index] = is_showing_details;
155 break;
[email protected]79a6f99a2013-08-29 00:32:58156 case RETAINED_FILES_DETAILS:
157 is_showing_details_for_retained_files_ = is_showing_details;
158 break;
reillygc64d3dd2014-09-29 21:10:11159 case RETAINED_DEVICES_DETAILS:
160 is_showing_details_for_retained_devices_ = is_showing_details;
161 break;
[email protected]79a6f99a2013-08-29 00:32:58162 }
[email protected]8ab7e6c2013-07-11 21:15:03163}
164
[email protected]34b5f7f2014-01-29 02:48:11165void ExtensionInstallPrompt::Prompt::SetWebstoreData(
[email protected]c82da8c42012-06-08 19:49:11166 const std::string& localized_user_count,
[email protected]dcde34b32013-07-31 02:28:45167 bool show_user_count,
[email protected]c82da8c42012-06-08 19:49:11168 double average_rating,
169 int rating_count) {
rdevlin.cronin5f6b69d2014-09-20 01:23:35170 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11171 localized_user_count_ = localized_user_count;
[email protected]dcde34b32013-07-31 02:28:45172 show_user_count_ = show_user_count;
[email protected]c82da8c42012-06-08 19:49:11173 average_rating_ = average_rating;
174 rating_count_ = rating_count;
[email protected]34b5f7f2014-01-29 02:48:11175 has_webstore_data_ = true;
[email protected]c82da8c42012-06-08 19:49:11176}
177
[email protected]6a72a632013-12-12 22:22:00178base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
rdevlin.croninc7f8d132017-02-22 14:29:02179 int id = -1;
180 switch (type_) {
181 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41182 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02183 id = IDS_EXTENSION_INSTALL_PROMPT_TITLE;
184 break;
185 case RE_ENABLE_PROMPT:
186 id = IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE;
187 break;
188 case PERMISSIONS_PROMPT:
189 id = IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE;
190 break;
191 case EXTERNAL_INSTALL_PROMPT:
192 if (extension_->is_app())
193 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP;
194 else if (extension_->is_theme())
195 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME;
196 else
197 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION;
198 break;
199 case POST_INSTALL_PERMISSIONS_PROMPT:
200 id = IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE;
201 break;
202 case REMOTE_INSTALL_PROMPT:
203 id = IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE;
204 break;
205 case REPAIR_PROMPT:
206 id = IDS_EXTENSION_REPAIR_PROMPT_TITLE;
207 break;
208 case DELEGATED_PERMISSIONS_PROMPT:
209 // Special case: need to include the delegated username.
210 return l10n_util::GetStringFUTF16(
211 IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE,
212 base::UTF8ToUTF16(extension_->name()),
213 base::UTF8ToUTF16(delegated_username_));
214 case UNSET_PROMPT_TYPE:
215 case NUM_PROMPT_TYPES:
216 NOTREACHED();
treibd35862252015-06-15 12:50:51217 }
rdevlin.croninc7f8d132017-02-22 14:29:02218
treibd35862252015-06-15 12:50:51219 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11220}
221
[email protected]15d267b42013-02-14 23:43:32222int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
rdevlin.croninc7f8d132017-02-22 14:29:02223 // The "OK" button in the post install permissions dialog allows revoking
224 // file/device access, and is only shown if such permissions exist; see
225 // ShouldDisplayRevokeButton().
226 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
227 !ShouldDisplayRevokeButton()) {
228 return ui::DIALOG_BUTTON_CANCEL;
[email protected]a2886e8b2013-06-08 05:15:02229 }
rdevlin.croninc7f8d132017-02-22 14:29:02230 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
[email protected]15d267b42013-02-14 23:43:32231}
232
[email protected]6a72a632013-12-12 22:22:00233base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02234 int id = -1;
235 switch (type_) {
236 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41237 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02238 if (extension_->is_app())
239 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
240 else if (extension_->is_theme())
241 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
242 else
243 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
244 break;
245 case RE_ENABLE_PROMPT:
246 id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON;
247 break;
248 case PERMISSIONS_PROMPT:
249 id = IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON;
250 break;
251 case EXTERNAL_INSTALL_PROMPT:
252 if (extension_->is_app())
253 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
254 else if (extension_->is_theme())
255 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
256 else
257 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
258 break;
259 case POST_INSTALL_PERMISSIONS_PROMPT:
260 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
261 id =
262 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON;
263 } else if (GetRetainedFileCount()) {
264 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON;
265 } else if (GetRetainedDeviceCount()) {
266 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON;
267 }
268 // If there are neither retained files nor devices, leave id -1 so there
269 // will be no "accept" button.
270 break;
271 case REMOTE_INSTALL_PROMPT:
272 if (extension_->is_app())
273 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_APP;
274 else
275 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_EXTENSION;
276 break;
277 case REPAIR_PROMPT:
278 if (extension_->is_app())
279 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_APP;
280 else
281 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_EXTENSION;
282 break;
283 case DELEGATED_PERMISSIONS_PROMPT:
284 id = IDS_EXTENSION_PROMPT_INSTALL_BUTTON;
285 break;
286 case UNSET_PROMPT_TYPE:
287 case NUM_PROMPT_TYPES:
288 NOTREACHED();
[email protected]846606012012-10-19 18:42:25289 }
rdevlin.croninc7f8d132017-02-22 14:29:02290
291 return id != -1 ? l10n_util::GetStringUTF16(id) : base::string16();
[email protected]c82da8c42012-06-08 19:49:11292}
293
[email protected]6a72a632013-12-12 22:22:00294base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02295 int id = -1;
296 switch (type_) {
297 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41298 case WEBSTORE_WIDGET_PROMPT:
rdevlin.croninc7f8d132017-02-22 14:29:02299 case RE_ENABLE_PROMPT:
300 case REMOTE_INSTALL_PROMPT:
301 case REPAIR_PROMPT:
302 case DELEGATED_PERMISSIONS_PROMPT:
303 id = IDS_CANCEL;
304 break;
305 case PERMISSIONS_PROMPT:
306 id = IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON;
307 break;
308 case EXTERNAL_INSTALL_PROMPT:
309 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON;
310 break;
311 case POST_INSTALL_PERMISSIONS_PROMPT:
312 id = IDS_CLOSE;
313 break;
314 case UNSET_PROMPT_TYPE:
315 case NUM_PROMPT_TYPES:
316 NOTREACHED();
317 }
318
319 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11320}
321
Devlin Cronind6e136a2018-05-15 23:39:32322base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
323 int id = -1;
324 switch (type_) {
325 case INSTALL_PROMPT:
Benjamin Ackermanc4fb02302018-10-03 19:55:41326 case WEBSTORE_WIDGET_PROMPT:
Devlin Cronind6e136a2018-05-15 23:39:32327 case EXTERNAL_INSTALL_PROMPT:
328 case REMOTE_INSTALL_PROMPT:
329 case DELEGATED_PERMISSIONS_PROMPT:
330 id = IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO;
331 break;
332 case RE_ENABLE_PROMPT:
333 id = IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO;
334 break;
335 case PERMISSIONS_PROMPT:
336 id = IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO;
337 break;
338 case POST_INSTALL_PERMISSIONS_PROMPT:
339 case REPAIR_PROMPT:
340 id = IDS_EXTENSION_PROMPT_CAN_ACCESS;
341 break;
342 case UNSET_PROMPT_TYPE:
343 case NUM_PROMPT_TYPES:
gpdavis.chromium0fbac4d2014-09-19 20:57:54344 NOTREACHED();
gpdavis.chromium0fbac4d2014-09-19 20:57:54345 }
Devlin Cronind6e136a2018-05-15 23:39:32346 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11347}
348
[email protected]6a72a632013-12-12 22:22:00349base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
jshin3a6c36bb2015-04-09 21:58:08350 return l10n_util::GetPluralStringFUTF16(
351 IDS_EXTENSION_PROMPT_RETAINED_FILES, GetRetainedFileCount());
[email protected]8ab7e6c2013-07-11 21:15:03352}
353
reillygc64d3dd2014-09-29 21:10:11354base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
355 const {
jshin3a6c36bb2015-04-09 21:58:08356 return l10n_util::GetPluralStringFUTF16(
357 IDS_EXTENSION_PROMPT_RETAINED_DEVICES, GetRetainedDeviceCount());
reillygc64d3dd2014-09-29 21:10:11358}
359
[email protected]f2cd8992013-06-11 17:30:18360bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
Devlin Cronind6e136a2018-05-15 23:39:32361 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
[email protected]f2cd8992013-06-11 17:30:18362}
363
[email protected]c82da8c42012-06-08 19:49:11364void ExtensionInstallPrompt::Prompt::AppendRatingStars(
365 StarAppender appender, void* data) const {
366 CHECK(appender);
rdevlin.cronin5f6b69d2014-09-20 01:23:35367 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11368 int rating_integer = floor(average_rating_);
369 double rating_fractional = average_rating_ - rating_integer;
370
371 if (rating_fractional > 0.66) {
372 rating_integer++;
373 }
374
375 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
376 rating_fractional = 0;
377 }
378
Lei Zhang7640d542017-10-03 16:26:49379 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
[email protected]c82da8c42012-06-08 19:49:11380 int i;
381 for (i = 0; i < rating_integer; i++) {
382 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
383 }
384 if (rating_fractional) {
385 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
386 i++;
387 }
388 for (; i < kMaxExtensionRating; i++) {
389 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
390 }
391}
392
[email protected]6a72a632013-12-12 22:22:00393base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35394 CHECK(AllowWebstoreData(type_));
[email protected]6725048e2013-10-24 21:47:14395 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
Raul Tambrefff51b752019-02-04 13:09:47396 base::NumberToString16(rating_count_));
[email protected]c82da8c42012-06-08 19:49:11397}
398
[email protected]6a72a632013-12-12 22:22:00399base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35400 CHECK(AllowWebstoreData(type_));
[email protected]dcde34b32013-07-31 02:28:45401
402 if (show_user_count_) {
[email protected]6725048e2013-10-24 21:47:14403 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
404 base::UTF8ToUTF16(localized_user_count_));
[email protected]dcde34b32013-07-31 02:28:45405 }
[email protected]6725048e2013-10-24 21:47:14406 return base::string16();
[email protected]c82da8c42012-06-08 19:49:11407}
408
Devlin Cronind6e136a2018-05-15 23:39:32409size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
410 return prompt_permissions_.permissions.size();
[email protected]c82da8c42012-06-08 19:49:11411}
412
Devlin Cronind6e136a2018-05-15 23:39:32413size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
414 return prompt_permissions_.details.size();
[email protected]8ab7e6c2013-07-11 21:15:03415}
416
gpdavis.chromium0fbac4d2014-09-19 20:57:54417base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
Devlin Cronind6e136a2018-05-15 23:39:32418 size_t index) const {
419 CHECK_LT(index, prompt_permissions_.permissions.size());
420 return prompt_permissions_.permissions[index];
[email protected]c82da8c42012-06-08 19:49:11421}
422
[email protected]6a72a632013-12-12 22:22:00423base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
Devlin Cronind6e136a2018-05-15 23:39:32424 size_t index) const {
425 CHECK_LT(index, prompt_permissions_.details.size());
426 return prompt_permissions_.details[index];
[email protected]8ab7e6c2013-07-11 21:15:03427}
428
[email protected]79a6f99a2013-08-29 00:32:58429bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
430 DetailsType type, size_t index) const {
431 switch (type) {
432 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54433 CHECK_LT(index, prompt_permissions_.is_showing_details.size());
434 return prompt_permissions_.is_showing_details[index];
[email protected]79a6f99a2013-08-29 00:32:58435 case RETAINED_FILES_DETAILS:
436 return is_showing_details_for_retained_files_;
reillygc64d3dd2014-09-29 21:10:11437 case RETAINED_DEVICES_DETAILS:
438 return is_showing_details_for_retained_devices_;
[email protected]79a6f99a2013-08-29 00:32:58439 }
440 return false;
441}
442
[email protected]a2886e8b2013-06-08 05:15:02443size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
444 return retained_files_.size();
445}
446
[email protected]6a72a632013-12-12 22:22:00447base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
448 const {
[email protected]a2886e8b2013-06-08 05:15:02449 CHECK_LT(index, retained_files_.size());
[email protected]6725048e2013-10-24 21:47:14450 return retained_files_[index].AsUTF16Unsafe();
[email protected]a2886e8b2013-06-08 05:15:02451}
452
reillygc64d3dd2014-09-29 21:10:11453size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
454 return retained_device_messages_.size();
455}
456
457base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
458 size_t index) const {
459 CHECK_LT(index, retained_device_messages_.size());
460 return retained_device_messages_[index];
461}
462
463bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
464 return !retained_files_.empty() || !retained_device_messages_.empty();
465}
466
[email protected]a2886e8b2013-06-08 05:15:02467bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
468 return !retained_files_.empty();
469}
470
[email protected]c82da8c42012-06-08 19:49:11471// static
rdevlin.croninf84cab72015-12-12 03:45:23472ExtensionInstallPrompt::PromptType
473ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
474 content::BrowserContext* context,
475 const extensions::Extension* extension) {
476 bool is_remote_install =
477 context &&
478 extensions::ExtensionPrefs::Get(context)->HasDisableReason(
Minh X. Nguyen45479012017-08-18 21:35:36479 extension->id(), extensions::disable_reason::DISABLE_REMOTE_INSTALL);
rdevlin.croninf84cab72015-12-12 03:45:23480
481 return is_remote_install ? REMOTE_INSTALL_PROMPT : RE_ENABLE_PROMPT;
482}
483
484// static
[email protected]c82da8c42012-06-08 19:49:11485scoped_refptr<Extension>
486 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
[email protected]023b3d12013-12-23 18:46:49487 const base::DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13488 int flags,
[email protected]c82da8c42012-06-08 19:49:11489 const std::string& id,
490 const std::string& localized_name,
491 const std::string& localized_description,
492 std::string* error) {
dchengc963c7142016-04-08 03:55:22493 std::unique_ptr<base::DictionaryValue> localized_manifest;
[email protected]c82da8c42012-06-08 19:49:11494 if (!localized_name.empty() || !localized_description.empty()) {
495 localized_manifest.reset(manifest->DeepCopy());
496 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27497 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11498 localized_name);
499 }
500 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27501 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11502 localized_description);
503 }
504 }
505
506 return Extension::Create(
rdevlin.cronin165732a42016-07-18 22:25:08507 base::FilePath(), Manifest::INTERNAL,
508 localized_manifest.get() ? *localized_manifest : *manifest, flags, id,
[email protected]c82da8c42012-06-08 19:49:11509 error);
510}
511
[email protected]7f165342014-02-12 09:22:22512ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
Oscar Johanssondf0dd8f2018-06-18 06:51:07513 : profile_(contents
514 ? Profile::FromBrowserContext(contents->GetBrowserContext())
515 : nullptr),
[email protected]c82da8c42012-06-08 19:49:11516 extension_(NULL),
Oscar Johanssondf0dd8f2018-06-18 06:51:07517 install_ui_(extensions::CreateExtensionInstallUI(profile_)),
pkotwicz2f181782014-10-29 17:33:45518 show_params_(new ExtensionInstallPromptShowParams(contents)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40519 did_call_show_dialog_(false),
Oscar Johanssondf0dd8f2018-06-18 06:51:07520 weak_factory_(this) {}
[email protected]5db2e882012-12-20 10:17:26521
pkotwicz2175c622014-10-22 19:56:28522ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
523 gfx::NativeWindow native_window)
pkotwicza57a1f322014-10-21 00:24:30524 : profile_(profile),
[email protected]5db2e882012-12-20 10:17:26525 extension_(NULL),
pkotwicza57a1f322014-10-21 00:24:30526 install_ui_(extensions::CreateExtensionInstallUI(profile)),
pkotwicz2f181782014-10-29 17:33:45527 show_params_(
528 new ExtensionInstallPromptShowParams(profile, native_window)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40529 did_call_show_dialog_(false),
530 weak_factory_(this) {
[email protected]d382baa2014-06-17 18:50:01531}
[email protected]c82da8c42012-06-08 19:49:11532
533ExtensionInstallPrompt::~ExtensionInstallPrompt() {
534}
535
rdevlin.croninf84cab72015-12-12 03:45:23536void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12537 const DoneCallback& done_callback,
[email protected]c82da8c42012-06-08 19:49:11538 const Extension* extension,
rdevlin.croninf84cab72015-12-12 03:45:23539 const SkBitmap* icon,
540 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12541 ShowDialog(done_callback, extension, icon,
Jinho Bangb5216cec2018-01-17 19:43:11542 std::make_unique<Prompt>(INSTALL_PROMPT), show_dialog_callback);
rdevlin.cronin2e252692015-12-15 21:47:02543}
544
545void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12546 const DoneCallback& done_callback,
rdevlin.cronin2e252692015-12-15 21:47:02547 const Extension* extension,
548 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22549 std::unique_ptr<Prompt> prompt,
rdevlin.cronin2e252692015-12-15 21:47:02550 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12551 ShowDialog(done_callback, extension, icon, std::move(prompt), nullptr,
rdevlin.croninf84cab72015-12-12 03:45:23552 show_dialog_callback);
553}
554
555void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12556 const DoneCallback& done_callback,
rdevlin.croninf84cab72015-12-12 03:45:23557 const Extension* extension,
558 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22559 std::unique_ptr<Prompt> prompt,
560 std::unique_ptr<const PermissionSet> custom_permissions,
rdevlin.croninf84cab72015-12-12 03:45:23561 const ShowDialogCallback& show_dialog_callback) {
fdorayc16c6f82016-06-29 15:27:32562 DCHECK(ui_thread_checker_.CalledOnValidThread());
rdevlin.cronin2e252692015-12-15 21:47:02563 DCHECK(prompt);
[email protected]c82da8c42012-06-08 19:49:11564 extension_ = extension;
rdevlin.cronin41593052016-01-08 01:40:12565 done_callback_ = done_callback;
rdevlin.croninfc1499f2015-12-21 18:22:00566 if (icon && !icon->empty())
567 SetIcon(icon);
dcheng1fc00f12015-12-26 22:18:03568 prompt_ = std::move(prompt);
569 custom_permissions_ = std::move(custom_permissions);
[email protected]af6efb22012-10-12 02:23:05570 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11571
572 // We special-case themes to not show any confirm UI. Instead they are
573 // immediately installed, and then we show an infobar (see OnInstallSuccess)
574 // to allow the user to revert if they don't like it.
rdevlin.cronin10f34542017-02-06 18:22:47575 if (extension->is_theme() && extension->from_webstore()) {
Daniel Chengd78c0252019-04-26 17:22:11576 std::move(done_callback_).Run(Result::ACCEPTED);
rdevlin.cronin10f34542017-02-06 18:22:47577 return;
[email protected]c82da8c42012-06-08 19:49:11578 }
579
580 LoadImageIfNeeded();
581}
582
Dana Friedc3c30222018-12-11 23:34:56583void ExtensionInstallPrompt::OnInstallSuccess(
584 scoped_refptr<const Extension> extension,
585 SkBitmap* icon) {
[email protected]c82da8c42012-06-08 19:49:11586 extension_ = extension;
587 SetIcon(icon);
588
589 install_ui_->OnInstallSuccess(extension, &icon_);
590}
591
[email protected]bf3d9df2012-07-24 23:20:27592void ExtensionInstallPrompt::OnInstallFailure(
ginkage47e603e2015-02-27 08:42:41593 const extensions::CrxInstallError& error) {
[email protected]c82da8c42012-06-08 19:49:11594 install_ui_->OnInstallFailure(error);
595}
596
597void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
598 if (image)
599 icon_ = *image;
600 else
601 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50602 if (icon_.empty()) {
603 // Let's set default icon bitmap whose size is equal to the default icon's
604 // pixel size under maximal supported scale factor. If the bitmap is larger
605 // than the one we need, it will be scaled down by the ui code.
treibd9e1d9d2015-04-24 11:17:04606 icon_ = GetDefaultIconBitmapForMaxScaleFactor(
607 extension_ ? extension_->is_app() : false);
[email protected]dd46a4ce2012-09-15 10:50:50608 }
[email protected]c82da8c42012-06-08 19:49:11609}
610
[email protected]ec7de0c5a2012-11-16 07:40:47611void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
[email protected]c82da8c42012-06-08 19:49:11612 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
[email protected]4e494872013-09-06 09:13:54613 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11614}
615
616void ExtensionInstallPrompt::LoadImageIfNeeded() {
treib3d41d9f2016-04-12 08:53:01617 // Don't override an icon that was passed in. Also, |profile_| can be null in
618 // unit tests.
619 if (!icon_.empty() || !profile_) {
[email protected]4e494872013-09-06 09:13:54620 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11621 return;
622 }
623
[email protected]993da5e2013-03-23 21:25:16624 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
Dana Friedc3c30222018-12-11 23:34:56625 extension_.get(), extension_misc::EXTENSION_ICON_LARGE,
[email protected]702d8b42013-02-27 20:55:50626 ExtensionIconSet::MATCH_BIGGER);
[email protected]0d0ba182014-06-03 12:40:43627
628 // Load the image asynchronously. The response will be sent to OnImageLoaded.
pkotwicza57a1f322014-10-21 00:24:30629 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile_);
[email protected]0d0ba182014-06-03 12:40:43630
631 std::vector<extensions::ImageLoader::ImageRepresentation> images_list;
632 images_list.push_back(extensions::ImageLoader::ImageRepresentation(
633 image,
634 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
635 gfx::Size(),
636 ui::SCALE_FACTOR_100P));
Dana Friedc3c30222018-12-11 23:34:56637 loader->LoadImagesAsync(extension_.get(), images_list,
Nigel Taobd12215b2018-11-29 01:10:18638 base::BindOnce(&ExtensionInstallPrompt::OnImageLoaded,
639 weak_factory_.GetWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11640}
641
642void ExtensionInstallPrompt::ShowConfirmation() {
dchengc963c7142016-04-08 03:55:22643 std::unique_ptr<const PermissionSet> permissions_wrapper;
rdevlin.cronine2d0fd02015-09-24 22:35:49644 const PermissionSet* permissions_to_display = nullptr;
gpdavis.chromium0fbac4d2014-09-19 20:57:54645 if (custom_permissions_.get()) {
rdevlin.cronine2d0fd02015-09-24 22:35:49646 permissions_to_display = custom_permissions_.get();
gpdavis.chromium0fbac4d2014-09-19 20:57:54647 } else if (extension_) {
648 // Initialize permissions if they have not already been set so that
Devlin Cronind6e136a2018-05-15 23:39:32649 // any transformations are correctly reflected in the install prompt.
gpdavis.chromium0fbac4d2014-09-19 20:57:54650 extensions::PermissionsUpdater(
pkotwicza57a1f322014-10-21 00:24:30651 profile_, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT)
Dana Friedc3c30222018-12-11 23:34:56652 .InitializePermissions(extension_.get());
gpdavis.chromium0fbac4d2014-09-19 20:57:54653 permissions_to_display =
rdevlin.cronind630c302015-09-30 20:19:33654 &extension_->permissions_data()->active_permissions();
treib7496f63c2015-03-04 12:18:53655 // For delegated installs, all optional permissions are pre-approved by the
656 // person who triggers the install, so add them to the list.
treib3d41d9f2016-04-12 08:53:01657 if (prompt_->type() == DELEGATED_PERMISSIONS_PROMPT) {
rdevlin.cronind630c302015-09-30 20:19:33658 const PermissionSet& optional_permissions =
Dana Friedc3c30222018-12-11 23:34:56659 extensions::PermissionsParser::GetOptionalPermissions(
660 extension_.get());
rdevlin.cronine2d0fd02015-09-24 22:35:49661 permissions_wrapper = PermissionSet::CreateUnion(*permissions_to_display,
rdevlin.cronind630c302015-09-30 20:19:33662 optional_permissions);
rdevlin.cronine2d0fd02015-09-24 22:35:49663 permissions_to_display = permissions_wrapper.get();
treib7496f63c2015-03-04 12:18:53664 }
gpdavis.chromium0fbac4d2014-09-19 20:57:54665 }
666
Devlin Cronin007e4fd52018-06-08 22:06:00667 if (permissions_to_display) {
[email protected]a397ec02014-08-08 15:48:13668 Manifest::Type type =
669 extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN;
670 const extensions::PermissionMessageProvider* message_provider =
671 extensions::PermissionMessageProvider::Get();
treib7b45a34b2015-04-16 11:48:02672
isandrk98a3e4a12017-05-26 21:14:50673 prompt_->AddPermissions(message_provider->GetPermissionMessages(
Devlin Cronind6e136a2018-05-15 23:39:32674 message_provider->GetAllPermissionIDs(*permissions_to_display, type)));
[email protected]bebe1d02012-08-02 20:17:09675 }
[email protected]c82da8c42012-06-08 19:49:11676
Dana Friedc3c30222018-12-11 23:34:56677 prompt_->set_extension(extension_.get());
treibd9e1d9d2015-04-24 11:17:04678 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]af6efb22012-10-12 02:23:05679
pkotwicz2f181782014-10-29 17:33:45680 if (show_params_->WasParentDestroyed()) {
Daniel Chengd78c0252019-04-26 17:22:11681 std::move(done_callback_).Run(Result::ABORTED);
pkotwicz2f181782014-10-29 17:33:45682 return;
683 }
684
rdevlin.croninca5bf2da2015-12-17 21:21:08685 g_last_prompt_type_for_tests = prompt_->type();
686 did_call_show_dialog_ = true;
687
rdevlin.cronin41593052016-01-08 01:40:12688 if (AutoConfirmPrompt(&done_callback_))
rdevlin.croninca5bf2da2015-12-17 21:21:08689 return;
690
[email protected]5db2e882012-12-20 10:17:26691 if (show_dialog_callback_.is_null())
rdevlin.cronin41593052016-01-08 01:40:12692 show_dialog_callback_ = GetDefaultShowDialogCallback();
Daniel Chengd59fe5d2019-04-30 00:32:49693 // TODO(https://crbug.com/957713): Use OnceCallback and eliminate the need for
694 // a callback on the stack.
695 auto cb = std::move(done_callback_);
Daniel Chengd78c0252019-04-26 17:22:11696 std::move(show_dialog_callback_)
Daniel Chengd59fe5d2019-04-30 00:32:49697 .Run(show_params_.get(), cb, std::move(prompt_));
[email protected]c82da8c42012-06-08 19:49:11698}