blob: 2f7ef04c07b789fc22ccbba954e805496feacbfe [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
rdevlin.cronin41593052016-01-08 01:40:129#include "base/callback_helpers.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) {
56 return type == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT ||
57 type == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT ||
58 type == ExtensionInstallPrompt::REPAIR_PROMPT;
59}
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:
182 case INLINE_INSTALL_PROMPT:
183 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:
237 case INLINE_INSTALL_PROMPT:
238 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:
298 case INLINE_INSTALL_PROMPT:
299 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:
326 case INLINE_INSTALL_PROMPT:
327 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
asargent0a1c15f2015-12-09 02:25:21364bool ExtensionInstallPrompt::Prompt::ShouldUseTabModalDialog() const {
365 // For inline install, we want the install prompt to be tab modal so that the
366 // dialog is always clearly associated with the page that made the inline
367 // install request.
368 return type_ == INLINE_INSTALL_PROMPT;
369}
370
[email protected]c82da8c42012-06-08 19:49:11371void ExtensionInstallPrompt::Prompt::AppendRatingStars(
372 StarAppender appender, void* data) const {
373 CHECK(appender);
rdevlin.cronin5f6b69d2014-09-20 01:23:35374 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11375 int rating_integer = floor(average_rating_);
376 double rating_fractional = average_rating_ - rating_integer;
377
378 if (rating_fractional > 0.66) {
379 rating_integer++;
380 }
381
382 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
383 rating_fractional = 0;
384 }
385
Lei Zhang7640d542017-10-03 16:26:49386 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
[email protected]c82da8c42012-06-08 19:49:11387 int i;
388 for (i = 0; i < rating_integer; i++) {
389 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
390 }
391 if (rating_fractional) {
392 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
393 i++;
394 }
395 for (; i < kMaxExtensionRating; i++) {
396 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
397 }
398}
399
[email protected]6a72a632013-12-12 22:22:00400base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35401 CHECK(AllowWebstoreData(type_));
[email protected]6725048e2013-10-24 21:47:14402 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
403 base::IntToString16(rating_count_));
[email protected]c82da8c42012-06-08 19:49:11404}
405
[email protected]6a72a632013-12-12 22:22:00406base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35407 CHECK(AllowWebstoreData(type_));
[email protected]dcde34b32013-07-31 02:28:45408
409 if (show_user_count_) {
[email protected]6725048e2013-10-24 21:47:14410 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
411 base::UTF8ToUTF16(localized_user_count_));
[email protected]dcde34b32013-07-31 02:28:45412 }
[email protected]6725048e2013-10-24 21:47:14413 return base::string16();
[email protected]c82da8c42012-06-08 19:49:11414}
415
Devlin Cronind6e136a2018-05-15 23:39:32416size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
417 return prompt_permissions_.permissions.size();
[email protected]c82da8c42012-06-08 19:49:11418}
419
Devlin Cronind6e136a2018-05-15 23:39:32420size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
421 return prompt_permissions_.details.size();
[email protected]8ab7e6c2013-07-11 21:15:03422}
423
gpdavis.chromium0fbac4d2014-09-19 20:57:54424base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
Devlin Cronind6e136a2018-05-15 23:39:32425 size_t index) const {
426 CHECK_LT(index, prompt_permissions_.permissions.size());
427 return prompt_permissions_.permissions[index];
[email protected]c82da8c42012-06-08 19:49:11428}
429
[email protected]6a72a632013-12-12 22:22:00430base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
Devlin Cronind6e136a2018-05-15 23:39:32431 size_t index) const {
432 CHECK_LT(index, prompt_permissions_.details.size());
433 return prompt_permissions_.details[index];
[email protected]8ab7e6c2013-07-11 21:15:03434}
435
[email protected]79a6f99a2013-08-29 00:32:58436bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
437 DetailsType type, size_t index) const {
438 switch (type) {
439 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54440 CHECK_LT(index, prompt_permissions_.is_showing_details.size());
441 return prompt_permissions_.is_showing_details[index];
[email protected]79a6f99a2013-08-29 00:32:58442 case RETAINED_FILES_DETAILS:
443 return is_showing_details_for_retained_files_;
reillygc64d3dd2014-09-29 21:10:11444 case RETAINED_DEVICES_DETAILS:
445 return is_showing_details_for_retained_devices_;
[email protected]79a6f99a2013-08-29 00:32:58446 }
447 return false;
448}
449
[email protected]a2886e8b2013-06-08 05:15:02450size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
451 return retained_files_.size();
452}
453
[email protected]6a72a632013-12-12 22:22:00454base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
455 const {
[email protected]a2886e8b2013-06-08 05:15:02456 CHECK_LT(index, retained_files_.size());
[email protected]6725048e2013-10-24 21:47:14457 return retained_files_[index].AsUTF16Unsafe();
[email protected]a2886e8b2013-06-08 05:15:02458}
459
reillygc64d3dd2014-09-29 21:10:11460size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
461 return retained_device_messages_.size();
462}
463
464base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
465 size_t index) const {
466 CHECK_LT(index, retained_device_messages_.size());
467 return retained_device_messages_[index];
468}
469
470bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
471 return !retained_files_.empty() || !retained_device_messages_.empty();
472}
473
[email protected]a2886e8b2013-06-08 05:15:02474bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
475 return !retained_files_.empty();
476}
477
[email protected]c82da8c42012-06-08 19:49:11478// static
rdevlin.croninf84cab72015-12-12 03:45:23479ExtensionInstallPrompt::PromptType
480ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
481 content::BrowserContext* context,
482 const extensions::Extension* extension) {
483 bool is_remote_install =
484 context &&
485 extensions::ExtensionPrefs::Get(context)->HasDisableReason(
Minh X. Nguyen45479012017-08-18 21:35:36486 extension->id(), extensions::disable_reason::DISABLE_REMOTE_INSTALL);
rdevlin.croninf84cab72015-12-12 03:45:23487
488 return is_remote_install ? REMOTE_INSTALL_PROMPT : RE_ENABLE_PROMPT;
489}
490
491// static
[email protected]c82da8c42012-06-08 19:49:11492scoped_refptr<Extension>
493 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
[email protected]023b3d12013-12-23 18:46:49494 const base::DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13495 int flags,
[email protected]c82da8c42012-06-08 19:49:11496 const std::string& id,
497 const std::string& localized_name,
498 const std::string& localized_description,
499 std::string* error) {
dchengc963c7142016-04-08 03:55:22500 std::unique_ptr<base::DictionaryValue> localized_manifest;
[email protected]c82da8c42012-06-08 19:49:11501 if (!localized_name.empty() || !localized_description.empty()) {
502 localized_manifest.reset(manifest->DeepCopy());
503 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27504 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11505 localized_name);
506 }
507 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27508 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11509 localized_description);
510 }
511 }
512
513 return Extension::Create(
rdevlin.cronin165732a42016-07-18 22:25:08514 base::FilePath(), Manifest::INTERNAL,
515 localized_manifest.get() ? *localized_manifest : *manifest, flags, id,
[email protected]c82da8c42012-06-08 19:49:11516 error);
517}
518
[email protected]7f165342014-02-12 09:22:22519ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
Oscar Johanssondf0dd8f2018-06-18 06:51:07520 : profile_(contents
521 ? Profile::FromBrowserContext(contents->GetBrowserContext())
522 : nullptr),
[email protected]c82da8c42012-06-08 19:49:11523 extension_(NULL),
Oscar Johanssondf0dd8f2018-06-18 06:51:07524 install_ui_(extensions::CreateExtensionInstallUI(profile_)),
pkotwicz2f181782014-10-29 17:33:45525 show_params_(new ExtensionInstallPromptShowParams(contents)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40526 did_call_show_dialog_(false),
Oscar Johanssondf0dd8f2018-06-18 06:51:07527 weak_factory_(this) {}
[email protected]5db2e882012-12-20 10:17:26528
pkotwicz2175c622014-10-22 19:56:28529ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
530 gfx::NativeWindow native_window)
pkotwicza57a1f322014-10-21 00:24:30531 : profile_(profile),
[email protected]5db2e882012-12-20 10:17:26532 extension_(NULL),
pkotwicza57a1f322014-10-21 00:24:30533 install_ui_(extensions::CreateExtensionInstallUI(profile)),
pkotwicz2f181782014-10-29 17:33:45534 show_params_(
535 new ExtensionInstallPromptShowParams(profile, native_window)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40536 did_call_show_dialog_(false),
537 weak_factory_(this) {
[email protected]d382baa2014-06-17 18:50:01538}
[email protected]c82da8c42012-06-08 19:49:11539
540ExtensionInstallPrompt::~ExtensionInstallPrompt() {
541}
542
rdevlin.croninf84cab72015-12-12 03:45:23543void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12544 const DoneCallback& done_callback,
[email protected]c82da8c42012-06-08 19:49:11545 const Extension* extension,
rdevlin.croninf84cab72015-12-12 03:45:23546 const SkBitmap* icon,
547 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12548 ShowDialog(done_callback, extension, icon,
Jinho Bangb5216cec2018-01-17 19:43:11549 std::make_unique<Prompt>(INSTALL_PROMPT), show_dialog_callback);
rdevlin.cronin2e252692015-12-15 21:47:02550}
551
552void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12553 const DoneCallback& done_callback,
rdevlin.cronin2e252692015-12-15 21:47:02554 const Extension* extension,
555 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22556 std::unique_ptr<Prompt> prompt,
rdevlin.cronin2e252692015-12-15 21:47:02557 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12558 ShowDialog(done_callback, extension, icon, std::move(prompt), nullptr,
rdevlin.croninf84cab72015-12-12 03:45:23559 show_dialog_callback);
560}
561
562void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12563 const DoneCallback& done_callback,
rdevlin.croninf84cab72015-12-12 03:45:23564 const Extension* extension,
565 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22566 std::unique_ptr<Prompt> prompt,
567 std::unique_ptr<const PermissionSet> custom_permissions,
rdevlin.croninf84cab72015-12-12 03:45:23568 const ShowDialogCallback& show_dialog_callback) {
fdorayc16c6f82016-06-29 15:27:32569 DCHECK(ui_thread_checker_.CalledOnValidThread());
rdevlin.cronin2e252692015-12-15 21:47:02570 DCHECK(prompt);
[email protected]c82da8c42012-06-08 19:49:11571 extension_ = extension;
rdevlin.cronin41593052016-01-08 01:40:12572 done_callback_ = done_callback;
rdevlin.croninfc1499f2015-12-21 18:22:00573 if (icon && !icon->empty())
574 SetIcon(icon);
dcheng1fc00f12015-12-26 22:18:03575 prompt_ = std::move(prompt);
576 custom_permissions_ = std::move(custom_permissions);
[email protected]af6efb22012-10-12 02:23:05577 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11578
579 // We special-case themes to not show any confirm UI. Instead they are
580 // immediately installed, and then we show an infobar (see OnInstallSuccess)
581 // to allow the user to revert if they don't like it.
rdevlin.cronin10f34542017-02-06 18:22:47582 if (extension->is_theme() && extension->from_webstore()) {
583 base::ResetAndReturn(&done_callback_).Run(Result::ACCEPTED);
584 return;
[email protected]c82da8c42012-06-08 19:49:11585 }
586
587 LoadImageIfNeeded();
588}
589
[email protected]c82da8c42012-06-08 19:49:11590void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
591 SkBitmap* icon) {
592 extension_ = extension;
593 SetIcon(icon);
594
595 install_ui_->OnInstallSuccess(extension, &icon_);
596}
597
[email protected]bf3d9df2012-07-24 23:20:27598void ExtensionInstallPrompt::OnInstallFailure(
ginkage47e603e2015-02-27 08:42:41599 const extensions::CrxInstallError& error) {
[email protected]c82da8c42012-06-08 19:49:11600 install_ui_->OnInstallFailure(error);
601}
602
603void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
604 if (image)
605 icon_ = *image;
606 else
607 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50608 if (icon_.empty()) {
609 // Let's set default icon bitmap whose size is equal to the default icon's
610 // pixel size under maximal supported scale factor. If the bitmap is larger
611 // than the one we need, it will be scaled down by the ui code.
treibd9e1d9d2015-04-24 11:17:04612 icon_ = GetDefaultIconBitmapForMaxScaleFactor(
613 extension_ ? extension_->is_app() : false);
[email protected]dd46a4ce2012-09-15 10:50:50614 }
[email protected]c82da8c42012-06-08 19:49:11615}
616
[email protected]ec7de0c5a2012-11-16 07:40:47617void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
[email protected]c82da8c42012-06-08 19:49:11618 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
[email protected]4e494872013-09-06 09:13:54619 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11620}
621
622void ExtensionInstallPrompt::LoadImageIfNeeded() {
treib3d41d9f2016-04-12 08:53:01623 // Don't override an icon that was passed in. Also, |profile_| can be null in
624 // unit tests.
625 if (!icon_.empty() || !profile_) {
[email protected]4e494872013-09-06 09:13:54626 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11627 return;
628 }
629
[email protected]993da5e2013-03-23 21:25:16630 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
[email protected]702d8b42013-02-27 20:55:50631 extension_,
632 extension_misc::EXTENSION_ICON_LARGE,
633 ExtensionIconSet::MATCH_BIGGER);
[email protected]0d0ba182014-06-03 12:40:43634
635 // Load the image asynchronously. The response will be sent to OnImageLoaded.
pkotwicza57a1f322014-10-21 00:24:30636 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile_);
[email protected]0d0ba182014-06-03 12:40:43637
638 std::vector<extensions::ImageLoader::ImageRepresentation> images_list;
639 images_list.push_back(extensions::ImageLoader::ImageRepresentation(
640 image,
641 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
642 gfx::Size(),
643 ui::SCALE_FACTOR_100P));
644 loader->LoadImagesAsync(
645 extension_,
646 images_list,
rdevlin.cronin3fe4bd32016-01-12 18:45:40647 base::Bind(&ExtensionInstallPrompt::OnImageLoaded,
648 weak_factory_.GetWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11649}
650
651void ExtensionInstallPrompt::ShowConfirmation() {
dchengc963c7142016-04-08 03:55:22652 std::unique_ptr<const PermissionSet> permissions_wrapper;
rdevlin.cronine2d0fd02015-09-24 22:35:49653 const PermissionSet* permissions_to_display = nullptr;
gpdavis.chromium0fbac4d2014-09-19 20:57:54654 if (custom_permissions_.get()) {
rdevlin.cronine2d0fd02015-09-24 22:35:49655 permissions_to_display = custom_permissions_.get();
gpdavis.chromium0fbac4d2014-09-19 20:57:54656 } else if (extension_) {
657 // Initialize permissions if they have not already been set so that
Devlin Cronind6e136a2018-05-15 23:39:32658 // any transformations are correctly reflected in the install prompt.
gpdavis.chromium0fbac4d2014-09-19 20:57:54659 extensions::PermissionsUpdater(
pkotwicza57a1f322014-10-21 00:24:30660 profile_, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT)
gpdavis.chromium0fbac4d2014-09-19 20:57:54661 .InitializePermissions(extension_);
662 permissions_to_display =
rdevlin.cronind630c302015-09-30 20:19:33663 &extension_->permissions_data()->active_permissions();
treib7496f63c2015-03-04 12:18:53664 // For delegated installs, all optional permissions are pre-approved by the
665 // person who triggers the install, so add them to the list.
treib3d41d9f2016-04-12 08:53:01666 if (prompt_->type() == DELEGATED_PERMISSIONS_PROMPT) {
rdevlin.cronind630c302015-09-30 20:19:33667 const PermissionSet& optional_permissions =
treib7496f63c2015-03-04 12:18:53668 extensions::PermissionsParser::GetOptionalPermissions(extension_);
rdevlin.cronine2d0fd02015-09-24 22:35:49669 permissions_wrapper = PermissionSet::CreateUnion(*permissions_to_display,
rdevlin.cronind630c302015-09-30 20:19:33670 optional_permissions);
rdevlin.cronine2d0fd02015-09-24 22:35:49671 permissions_to_display = permissions_wrapper.get();
treib7496f63c2015-03-04 12:18:53672 }
gpdavis.chromium0fbac4d2014-09-19 20:57:54673 }
674
Devlin Cronin007e4fd52018-06-08 22:06:00675 if (permissions_to_display) {
[email protected]a397ec02014-08-08 15:48:13676 Manifest::Type type =
677 extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN;
678 const extensions::PermissionMessageProvider* message_provider =
679 extensions::PermissionMessageProvider::Get();
treib7b45a34b2015-04-16 11:48:02680
isandrk98a3e4a12017-05-26 21:14:50681 prompt_->AddPermissions(message_provider->GetPermissionMessages(
Devlin Cronind6e136a2018-05-15 23:39:32682 message_provider->GetAllPermissionIDs(*permissions_to_display, type)));
[email protected]bebe1d02012-08-02 20:17:09683 }
[email protected]c82da8c42012-06-08 19:49:11684
rdevlin.croninc7f8d132017-02-22 14:29:02685 prompt_->set_extension(extension_);
treibd9e1d9d2015-04-24 11:17:04686 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]af6efb22012-10-12 02:23:05687
pkotwicz2f181782014-10-29 17:33:45688 if (show_params_->WasParentDestroyed()) {
rdevlin.cronin41593052016-01-08 01:40:12689 base::ResetAndReturn(&done_callback_).Run(Result::ABORTED);
pkotwicz2f181782014-10-29 17:33:45690 return;
691 }
692
rdevlin.croninca5bf2da2015-12-17 21:21:08693 g_last_prompt_type_for_tests = prompt_->type();
694 did_call_show_dialog_ = true;
695
rdevlin.cronin41593052016-01-08 01:40:12696 if (AutoConfirmPrompt(&done_callback_))
rdevlin.croninca5bf2da2015-12-17 21:21:08697 return;
698
[email protected]5db2e882012-12-20 10:17:26699 if (show_dialog_callback_.is_null())
rdevlin.cronin41593052016-01-08 01:40:12700 show_dialog_callback_ = GetDefaultShowDialogCallback();
701 base::ResetAndReturn(&show_dialog_callback_)
702 .Run(show_params_.get(), base::ResetAndReturn(&done_callback_),
703 std::move(prompt_));
[email protected]c82da8c42012-06-08 19:49:11704}