blob: ed7af21a107f34ad11f61c8d2a7adfbf3b524f15 [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();
[email protected]50b66262013-09-24 03:25:4867 return image.GetRepresentation(
68 gfx::ImageSkia::GetMaxSupportedScale()).sk_bitmap();
[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]91e51d612012-10-21 23:03:0598Profile* ProfileForWebContents(content::WebContents* web_contents) {
99 if (!web_contents)
100 return NULL;
101 return Profile::FromBrowserContext(web_contents->GetBrowserContext());
102}
103
[email protected]c82da8c42012-06-08 19:49:11104} // namespace
105
gpdavis.chromium0fbac4d2014-09-19 20:57:54106ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
107 InstallPromptPermissions() {
108}
109ExtensionInstallPrompt::Prompt::InstallPromptPermissions::
110 ~InstallPromptPermissions() {
111}
112
rdevlin.cronin092c6772014-11-20 23:52:40113ExtensionInstallPrompt::PromptType
114ExtensionInstallPrompt::g_last_prompt_type_for_tests =
115 ExtensionInstallPrompt::UNSET_PROMPT_TYPE;
116
[email protected]5db2e882012-12-20 10:17:26117ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
[email protected]c82da8c42012-06-08 19:49:11118 : type_(type),
[email protected]79a6f99a2013-08-29 00:32:58119 is_showing_details_for_retained_files_(false),
reillygc64d3dd2014-09-29 21:10:11120 is_showing_details_for_retained_devices_(false),
[email protected]c82da8c42012-06-08 19:49:11121 extension_(NULL),
[email protected]c82da8c42012-06-08 19:49:11122 average_rating_(0.0),
[email protected]dcde34b32013-07-31 02:28:45123 rating_count_(0),
[email protected]34b5f7f2014-01-29 02:48:11124 show_user_count_(false),
125 has_webstore_data_(false) {
rdevlin.croninc7f8d132017-02-22 14:29:02126 DCHECK_NE(type_, UNSET_PROMPT_TYPE);
127 DCHECK_NE(type_, NUM_PROMPT_TYPES);
[email protected]c82da8c42012-06-08 19:49:11128}
129
130ExtensionInstallPrompt::Prompt::~Prompt() {
131}
132
isandrk98a3e4a12017-05-26 21:14:50133void ExtensionInstallPrompt::Prompt::AddPermissions(
Devlin Cronind6e136a2018-05-15 23:39:32134 const PermissionMessages& permissions) {
treibf9dce312015-08-27 17:33:35135 for (const PermissionMessage& msg : permissions) {
Devlin Cronind6e136a2018-05-15 23:39:32136 prompt_permissions_.permissions.push_back(msg.message());
treib7b45a34b2015-04-16 11:48:02137 // Add a dash to the front of each permission detail.
138 base::string16 details;
treib8d2de482015-08-04 16:36:48139 if (!msg.submessages().empty()) {
sashabeabd5df2014-11-06 02:38:25140 std::vector<base::string16> detail_lines_with_bullets;
treib8d2de482015-08-04 16:36:48141 for (const auto& detail_line : msg.submessages()) {
sashabeabd5df2014-11-06 02:38:25142 detail_lines_with_bullets.push_back(base::ASCIIToUTF16("- ") +
143 detail_line);
treib7b45a34b2015-04-16 11:48:02144 }
sashabeabd5df2014-11-06 02:38:25145
brettwd94a22142015-07-15 05:19:26146 details = base::JoinString(detail_lines_with_bullets,
147 base::ASCIIToUTF16("\n"));
sashabeabd5df2014-11-06 02:38:25148 }
Devlin Cronind6e136a2018-05-15 23:39:32149 prompt_permissions_.details.push_back(details);
150 prompt_permissions_.is_showing_details.push_back(false);
sashabeabd5df2014-11-06 02:38:25151 }
[email protected]79a6f99a2013-08-29 00:32:58152}
153
154void ExtensionInstallPrompt::Prompt::SetIsShowingDetails(
155 DetailsType type,
156 size_t index,
157 bool is_showing_details) {
158 switch (type) {
159 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54160 prompt_permissions_.is_showing_details[index] = is_showing_details;
161 break;
[email protected]79a6f99a2013-08-29 00:32:58162 case RETAINED_FILES_DETAILS:
163 is_showing_details_for_retained_files_ = is_showing_details;
164 break;
reillygc64d3dd2014-09-29 21:10:11165 case RETAINED_DEVICES_DETAILS:
166 is_showing_details_for_retained_devices_ = is_showing_details;
167 break;
[email protected]79a6f99a2013-08-29 00:32:58168 }
[email protected]8ab7e6c2013-07-11 21:15:03169}
170
[email protected]34b5f7f2014-01-29 02:48:11171void ExtensionInstallPrompt::Prompt::SetWebstoreData(
[email protected]c82da8c42012-06-08 19:49:11172 const std::string& localized_user_count,
[email protected]dcde34b32013-07-31 02:28:45173 bool show_user_count,
[email protected]c82da8c42012-06-08 19:49:11174 double average_rating,
175 int rating_count) {
rdevlin.cronin5f6b69d2014-09-20 01:23:35176 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11177 localized_user_count_ = localized_user_count;
[email protected]dcde34b32013-07-31 02:28:45178 show_user_count_ = show_user_count;
[email protected]c82da8c42012-06-08 19:49:11179 average_rating_ = average_rating;
180 rating_count_ = rating_count;
[email protected]34b5f7f2014-01-29 02:48:11181 has_webstore_data_ = true;
[email protected]c82da8c42012-06-08 19:49:11182}
183
[email protected]6a72a632013-12-12 22:22:00184base::string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
rdevlin.croninc7f8d132017-02-22 14:29:02185 int id = -1;
186 switch (type_) {
187 case INSTALL_PROMPT:
188 case INLINE_INSTALL_PROMPT:
189 id = IDS_EXTENSION_INSTALL_PROMPT_TITLE;
190 break;
191 case RE_ENABLE_PROMPT:
192 id = IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE;
193 break;
194 case PERMISSIONS_PROMPT:
195 id = IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE;
196 break;
197 case EXTERNAL_INSTALL_PROMPT:
198 if (extension_->is_app())
199 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_APP;
200 else if (extension_->is_theme())
201 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_THEME;
202 else
203 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE_EXTENSION;
204 break;
205 case POST_INSTALL_PERMISSIONS_PROMPT:
206 id = IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE;
207 break;
208 case REMOTE_INSTALL_PROMPT:
209 id = IDS_EXTENSION_REMOTE_INSTALL_PROMPT_TITLE;
210 break;
211 case REPAIR_PROMPT:
212 id = IDS_EXTENSION_REPAIR_PROMPT_TITLE;
213 break;
214 case DELEGATED_PERMISSIONS_PROMPT:
215 // Special case: need to include the delegated username.
216 return l10n_util::GetStringFUTF16(
217 IDS_EXTENSION_DELEGATED_INSTALL_PROMPT_TITLE,
218 base::UTF8ToUTF16(extension_->name()),
219 base::UTF8ToUTF16(delegated_username_));
220 case UNSET_PROMPT_TYPE:
221 case NUM_PROMPT_TYPES:
222 NOTREACHED();
treibd35862252015-06-15 12:50:51223 }
rdevlin.croninc7f8d132017-02-22 14:29:02224
treibd35862252015-06-15 12:50:51225 return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11226}
227
[email protected]15d267b42013-02-14 23:43:32228int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
rdevlin.croninc7f8d132017-02-22 14:29:02229 // The "OK" button in the post install permissions dialog allows revoking
230 // file/device access, and is only shown if such permissions exist; see
231 // ShouldDisplayRevokeButton().
232 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
233 !ShouldDisplayRevokeButton()) {
234 return ui::DIALOG_BUTTON_CANCEL;
[email protected]a2886e8b2013-06-08 05:15:02235 }
rdevlin.croninc7f8d132017-02-22 14:29:02236 return ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL;
[email protected]15d267b42013-02-14 23:43:32237}
238
[email protected]6a72a632013-12-12 22:22:00239base::string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02240 int id = -1;
241 switch (type_) {
242 case INSTALL_PROMPT:
243 case INLINE_INSTALL_PROMPT:
244 if (extension_->is_app())
245 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
246 else if (extension_->is_theme())
247 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
248 else
249 id = IDS_EXTENSION_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
250 break;
251 case RE_ENABLE_PROMPT:
252 id = IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON;
253 break;
254 case PERMISSIONS_PROMPT:
255 id = IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON;
256 break;
257 case EXTERNAL_INSTALL_PROMPT:
258 if (extension_->is_app())
259 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
260 else if (extension_->is_theme())
261 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
262 else
263 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
264 break;
265 case POST_INSTALL_PERMISSIONS_PROMPT:
266 if (GetRetainedFileCount() && GetRetainedDeviceCount()) {
267 id =
268 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_AND_DEVICES_BUTTON;
269 } else if (GetRetainedFileCount()) {
270 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON;
271 } else if (GetRetainedDeviceCount()) {
272 id = IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_DEVICES_BUTTON;
273 }
274 // If there are neither retained files nor devices, leave id -1 so there
275 // will be no "accept" button.
276 break;
277 case REMOTE_INSTALL_PROMPT:
278 if (extension_->is_app())
279 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_APP;
280 else
281 id = IDS_EXTENSION_PROMPT_REMOTE_INSTALL_BUTTON_EXTENSION;
282 break;
283 case REPAIR_PROMPT:
284 if (extension_->is_app())
285 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_APP;
286 else
287 id = IDS_EXTENSION_PROMPT_REPAIR_BUTTON_EXTENSION;
288 break;
289 case DELEGATED_PERMISSIONS_PROMPT:
290 id = IDS_EXTENSION_PROMPT_INSTALL_BUTTON;
291 break;
292 case UNSET_PROMPT_TYPE:
293 case NUM_PROMPT_TYPES:
294 NOTREACHED();
[email protected]846606012012-10-19 18:42:25295 }
rdevlin.croninc7f8d132017-02-22 14:29:02296
297 return id != -1 ? l10n_util::GetStringUTF16(id) : base::string16();
[email protected]c82da8c42012-06-08 19:49:11298}
299
[email protected]6a72a632013-12-12 22:22:00300base::string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
rdevlin.croninc7f8d132017-02-22 14:29:02301 int id = -1;
302 switch (type_) {
303 case INSTALL_PROMPT:
304 case INLINE_INSTALL_PROMPT:
305 case RE_ENABLE_PROMPT:
306 case REMOTE_INSTALL_PROMPT:
307 case REPAIR_PROMPT:
308 case DELEGATED_PERMISSIONS_PROMPT:
309 id = IDS_CANCEL;
310 break;
311 case PERMISSIONS_PROMPT:
312 id = IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON;
313 break;
314 case EXTERNAL_INSTALL_PROMPT:
315 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON;
316 break;
317 case POST_INSTALL_PERMISSIONS_PROMPT:
318 id = IDS_CLOSE;
319 break;
320 case UNSET_PROMPT_TYPE:
321 case NUM_PROMPT_TYPES:
322 NOTREACHED();
323 }
324
325 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11326}
327
Devlin Cronind6e136a2018-05-15 23:39:32328base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
329 int id = -1;
330 switch (type_) {
331 case INSTALL_PROMPT:
332 case INLINE_INSTALL_PROMPT:
333 case EXTERNAL_INSTALL_PROMPT:
334 case REMOTE_INSTALL_PROMPT:
335 case DELEGATED_PERMISSIONS_PROMPT:
336 id = IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO;
337 break;
338 case RE_ENABLE_PROMPT:
339 id = IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO;
340 break;
341 case PERMISSIONS_PROMPT:
342 id = IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO;
343 break;
344 case POST_INSTALL_PERMISSIONS_PROMPT:
345 case REPAIR_PROMPT:
346 id = IDS_EXTENSION_PROMPT_CAN_ACCESS;
347 break;
348 case UNSET_PROMPT_TYPE:
349 case NUM_PROMPT_TYPES:
gpdavis.chromium0fbac4d2014-09-19 20:57:54350 NOTREACHED();
gpdavis.chromium0fbac4d2014-09-19 20:57:54351 }
Devlin Cronind6e136a2018-05-15 23:39:32352 return l10n_util::GetStringUTF16(id);
[email protected]c82da8c42012-06-08 19:49:11353}
354
[email protected]6a72a632013-12-12 22:22:00355base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
jshin3a6c36bb2015-04-09 21:58:08356 return l10n_util::GetPluralStringFUTF16(
357 IDS_EXTENSION_PROMPT_RETAINED_FILES, GetRetainedFileCount());
[email protected]8ab7e6c2013-07-11 21:15:03358}
359
reillygc64d3dd2014-09-29 21:10:11360base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDevicesHeading()
361 const {
jshin3a6c36bb2015-04-09 21:58:08362 return l10n_util::GetPluralStringFUTF16(
363 IDS_EXTENSION_PROMPT_RETAINED_DEVICES, GetRetainedDeviceCount());
reillygc64d3dd2014-09-29 21:10:11364}
365
[email protected]f2cd8992013-06-11 17:30:18366bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
Devlin Cronind6e136a2018-05-15 23:39:32367 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
[email protected]f2cd8992013-06-11 17:30:18368}
369
asargent0a1c15f2015-12-09 02:25:21370bool ExtensionInstallPrompt::Prompt::ShouldUseTabModalDialog() const {
371 // For inline install, we want the install prompt to be tab modal so that the
372 // dialog is always clearly associated with the page that made the inline
373 // install request.
374 return type_ == INLINE_INSTALL_PROMPT;
375}
376
[email protected]c82da8c42012-06-08 19:49:11377void ExtensionInstallPrompt::Prompt::AppendRatingStars(
378 StarAppender appender, void* data) const {
379 CHECK(appender);
rdevlin.cronin5f6b69d2014-09-20 01:23:35380 CHECK(AllowWebstoreData(type_));
[email protected]c82da8c42012-06-08 19:49:11381 int rating_integer = floor(average_rating_);
382 double rating_fractional = average_rating_ - rating_integer;
383
384 if (rating_fractional > 0.66) {
385 rating_integer++;
386 }
387
388 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
389 rating_fractional = 0;
390 }
391
Lei Zhang7640d542017-10-03 16:26:49392 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
[email protected]c82da8c42012-06-08 19:49:11393 int i;
394 for (i = 0; i < rating_integer; i++) {
395 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
396 }
397 if (rating_fractional) {
398 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
399 i++;
400 }
401 for (; i < kMaxExtensionRating; i++) {
402 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
403 }
404}
405
[email protected]6a72a632013-12-12 22:22:00406base::string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35407 CHECK(AllowWebstoreData(type_));
[email protected]6725048e2013-10-24 21:47:14408 return l10n_util::GetStringFUTF16(IDS_EXTENSION_RATING_COUNT,
409 base::IntToString16(rating_count_));
[email protected]c82da8c42012-06-08 19:49:11410}
411
[email protected]6a72a632013-12-12 22:22:00412base::string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
rdevlin.cronin5f6b69d2014-09-20 01:23:35413 CHECK(AllowWebstoreData(type_));
[email protected]dcde34b32013-07-31 02:28:45414
415 if (show_user_count_) {
[email protected]6725048e2013-10-24 21:47:14416 return l10n_util::GetStringFUTF16(IDS_EXTENSION_USER_COUNT,
417 base::UTF8ToUTF16(localized_user_count_));
[email protected]dcde34b32013-07-31 02:28:45418 }
[email protected]6725048e2013-10-24 21:47:14419 return base::string16();
[email protected]c82da8c42012-06-08 19:49:11420}
421
Devlin Cronind6e136a2018-05-15 23:39:32422size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
423 return prompt_permissions_.permissions.size();
[email protected]c82da8c42012-06-08 19:49:11424}
425
Devlin Cronind6e136a2018-05-15 23:39:32426size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
427 return prompt_permissions_.details.size();
[email protected]8ab7e6c2013-07-11 21:15:03428}
429
gpdavis.chromium0fbac4d2014-09-19 20:57:54430base::string16 ExtensionInstallPrompt::Prompt::GetPermission(
Devlin Cronind6e136a2018-05-15 23:39:32431 size_t index) const {
432 CHECK_LT(index, prompt_permissions_.permissions.size());
433 return prompt_permissions_.permissions[index];
[email protected]c82da8c42012-06-08 19:49:11434}
435
[email protected]6a72a632013-12-12 22:22:00436base::string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
Devlin Cronind6e136a2018-05-15 23:39:32437 size_t index) const {
438 CHECK_LT(index, prompt_permissions_.details.size());
439 return prompt_permissions_.details[index];
[email protected]8ab7e6c2013-07-11 21:15:03440}
441
[email protected]79a6f99a2013-08-29 00:32:58442bool ExtensionInstallPrompt::Prompt::GetIsShowingDetails(
443 DetailsType type, size_t index) const {
444 switch (type) {
445 case PERMISSIONS_DETAILS:
gpdavis.chromium0fbac4d2014-09-19 20:57:54446 CHECK_LT(index, prompt_permissions_.is_showing_details.size());
447 return prompt_permissions_.is_showing_details[index];
[email protected]79a6f99a2013-08-29 00:32:58448 case RETAINED_FILES_DETAILS:
449 return is_showing_details_for_retained_files_;
reillygc64d3dd2014-09-29 21:10:11450 case RETAINED_DEVICES_DETAILS:
451 return is_showing_details_for_retained_devices_;
[email protected]79a6f99a2013-08-29 00:32:58452 }
453 return false;
454}
455
[email protected]a2886e8b2013-06-08 05:15:02456size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
457 return retained_files_.size();
458}
459
[email protected]6a72a632013-12-12 22:22:00460base::string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index)
461 const {
[email protected]a2886e8b2013-06-08 05:15:02462 CHECK_LT(index, retained_files_.size());
[email protected]6725048e2013-10-24 21:47:14463 return retained_files_[index].AsUTF16Unsafe();
[email protected]a2886e8b2013-06-08 05:15:02464}
465
reillygc64d3dd2014-09-29 21:10:11466size_t ExtensionInstallPrompt::Prompt::GetRetainedDeviceCount() const {
467 return retained_device_messages_.size();
468}
469
470base::string16 ExtensionInstallPrompt::Prompt::GetRetainedDeviceMessageString(
471 size_t index) const {
472 CHECK_LT(index, retained_device_messages_.size());
473 return retained_device_messages_[index];
474}
475
476bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeButton() const {
477 return !retained_files_.empty() || !retained_device_messages_.empty();
478}
479
[email protected]a2886e8b2013-06-08 05:15:02480bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
481 return !retained_files_.empty();
482}
483
[email protected]c82da8c42012-06-08 19:49:11484// static
rdevlin.croninf84cab72015-12-12 03:45:23485ExtensionInstallPrompt::PromptType
486ExtensionInstallPrompt::GetReEnablePromptTypeForExtension(
487 content::BrowserContext* context,
488 const extensions::Extension* extension) {
489 bool is_remote_install =
490 context &&
491 extensions::ExtensionPrefs::Get(context)->HasDisableReason(
Minh X. Nguyen45479012017-08-18 21:35:36492 extension->id(), extensions::disable_reason::DISABLE_REMOTE_INSTALL);
rdevlin.croninf84cab72015-12-12 03:45:23493
494 return is_remote_install ? REMOTE_INSTALL_PROMPT : RE_ENABLE_PROMPT;
495}
496
497// static
[email protected]c82da8c42012-06-08 19:49:11498scoped_refptr<Extension>
499 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
[email protected]023b3d12013-12-23 18:46:49500 const base::DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13501 int flags,
[email protected]c82da8c42012-06-08 19:49:11502 const std::string& id,
503 const std::string& localized_name,
504 const std::string& localized_description,
505 std::string* error) {
dchengc963c7142016-04-08 03:55:22506 std::unique_ptr<base::DictionaryValue> localized_manifest;
[email protected]c82da8c42012-06-08 19:49:11507 if (!localized_name.empty() || !localized_description.empty()) {
508 localized_manifest.reset(manifest->DeepCopy());
509 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27510 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11511 localized_name);
512 }
513 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27514 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11515 localized_description);
516 }
517 }
518
519 return Extension::Create(
rdevlin.cronin165732a42016-07-18 22:25:08520 base::FilePath(), Manifest::INTERNAL,
521 localized_manifest.get() ? *localized_manifest : *manifest, flags, id,
[email protected]c82da8c42012-06-08 19:49:11522 error);
523}
524
[email protected]7f165342014-02-12 09:22:22525ExtensionInstallPrompt::ExtensionInstallPrompt(content::WebContents* contents)
pkotwicza57a1f322014-10-21 00:24:30526 : profile_(ProfileForWebContents(contents)),
[email protected]c82da8c42012-06-08 19:49:11527 extension_(NULL),
pkotwicza57a1f322014-10-21 00:24:30528 install_ui_(extensions::CreateExtensionInstallUI(
529 ProfileForWebContents(contents))),
pkotwicz2f181782014-10-29 17:33:45530 show_params_(new ExtensionInstallPromptShowParams(contents)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40531 did_call_show_dialog_(false),
532 weak_factory_(this) {
[email protected]d382baa2014-06-17 18:50:01533}
[email protected]5db2e882012-12-20 10:17:26534
pkotwicz2175c622014-10-22 19:56:28535ExtensionInstallPrompt::ExtensionInstallPrompt(Profile* profile,
536 gfx::NativeWindow native_window)
pkotwicza57a1f322014-10-21 00:24:30537 : profile_(profile),
[email protected]5db2e882012-12-20 10:17:26538 extension_(NULL),
pkotwicza57a1f322014-10-21 00:24:30539 install_ui_(extensions::CreateExtensionInstallUI(profile)),
pkotwicz2f181782014-10-29 17:33:45540 show_params_(
541 new ExtensionInstallPromptShowParams(profile, native_window)),
rdevlin.cronin3fe4bd32016-01-12 18:45:40542 did_call_show_dialog_(false),
543 weak_factory_(this) {
[email protected]d382baa2014-06-17 18:50:01544}
[email protected]c82da8c42012-06-08 19:49:11545
546ExtensionInstallPrompt::~ExtensionInstallPrompt() {
547}
548
rdevlin.croninf84cab72015-12-12 03:45:23549void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12550 const DoneCallback& done_callback,
[email protected]c82da8c42012-06-08 19:49:11551 const Extension* extension,
rdevlin.croninf84cab72015-12-12 03:45:23552 const SkBitmap* icon,
553 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12554 ShowDialog(done_callback, extension, icon,
Jinho Bangb5216cec2018-01-17 19:43:11555 std::make_unique<Prompt>(INSTALL_PROMPT), show_dialog_callback);
rdevlin.cronin2e252692015-12-15 21:47:02556}
557
558void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12559 const DoneCallback& done_callback,
rdevlin.cronin2e252692015-12-15 21:47:02560 const Extension* extension,
561 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22562 std::unique_ptr<Prompt> prompt,
rdevlin.cronin2e252692015-12-15 21:47:02563 const ShowDialogCallback& show_dialog_callback) {
rdevlin.cronin41593052016-01-08 01:40:12564 ShowDialog(done_callback, extension, icon, std::move(prompt), nullptr,
rdevlin.croninf84cab72015-12-12 03:45:23565 show_dialog_callback);
566}
567
568void ExtensionInstallPrompt::ShowDialog(
rdevlin.cronin41593052016-01-08 01:40:12569 const DoneCallback& done_callback,
rdevlin.croninf84cab72015-12-12 03:45:23570 const Extension* extension,
571 const SkBitmap* icon,
dchengc963c7142016-04-08 03:55:22572 std::unique_ptr<Prompt> prompt,
573 std::unique_ptr<const PermissionSet> custom_permissions,
rdevlin.croninf84cab72015-12-12 03:45:23574 const ShowDialogCallback& show_dialog_callback) {
fdorayc16c6f82016-06-29 15:27:32575 DCHECK(ui_thread_checker_.CalledOnValidThread());
rdevlin.cronin2e252692015-12-15 21:47:02576 DCHECK(prompt);
[email protected]c82da8c42012-06-08 19:49:11577 extension_ = extension;
rdevlin.cronin41593052016-01-08 01:40:12578 done_callback_ = done_callback;
rdevlin.croninfc1499f2015-12-21 18:22:00579 if (icon && !icon->empty())
580 SetIcon(icon);
dcheng1fc00f12015-12-26 22:18:03581 prompt_ = std::move(prompt);
582 custom_permissions_ = std::move(custom_permissions);
[email protected]af6efb22012-10-12 02:23:05583 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11584
585 // We special-case themes to not show any confirm UI. Instead they are
586 // immediately installed, and then we show an infobar (see OnInstallSuccess)
587 // to allow the user to revert if they don't like it.
rdevlin.cronin10f34542017-02-06 18:22:47588 if (extension->is_theme() && extension->from_webstore()) {
589 base::ResetAndReturn(&done_callback_).Run(Result::ACCEPTED);
590 return;
[email protected]c82da8c42012-06-08 19:49:11591 }
592
593 LoadImageIfNeeded();
594}
595
[email protected]c82da8c42012-06-08 19:49:11596void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
597 SkBitmap* icon) {
598 extension_ = extension;
599 SetIcon(icon);
600
601 install_ui_->OnInstallSuccess(extension, &icon_);
602}
603
[email protected]bf3d9df2012-07-24 23:20:27604void ExtensionInstallPrompt::OnInstallFailure(
ginkage47e603e2015-02-27 08:42:41605 const extensions::CrxInstallError& error) {
[email protected]c82da8c42012-06-08 19:49:11606 install_ui_->OnInstallFailure(error);
607}
608
609void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
610 if (image)
611 icon_ = *image;
612 else
613 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50614 if (icon_.empty()) {
615 // Let's set default icon bitmap whose size is equal to the default icon's
616 // pixel size under maximal supported scale factor. If the bitmap is larger
617 // than the one we need, it will be scaled down by the ui code.
treibd9e1d9d2015-04-24 11:17:04618 icon_ = GetDefaultIconBitmapForMaxScaleFactor(
619 extension_ ? extension_->is_app() : false);
[email protected]dd46a4ce2012-09-15 10:50:50620 }
[email protected]c82da8c42012-06-08 19:49:11621}
622
[email protected]ec7de0c5a2012-11-16 07:40:47623void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
[email protected]c82da8c42012-06-08 19:49:11624 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
[email protected]4e494872013-09-06 09:13:54625 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11626}
627
628void ExtensionInstallPrompt::LoadImageIfNeeded() {
treib3d41d9f2016-04-12 08:53:01629 // Don't override an icon that was passed in. Also, |profile_| can be null in
630 // unit tests.
631 if (!icon_.empty() || !profile_) {
[email protected]4e494872013-09-06 09:13:54632 ShowConfirmation();
[email protected]c82da8c42012-06-08 19:49:11633 return;
634 }
635
[email protected]993da5e2013-03-23 21:25:16636 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
[email protected]702d8b42013-02-27 20:55:50637 extension_,
638 extension_misc::EXTENSION_ICON_LARGE,
639 ExtensionIconSet::MATCH_BIGGER);
[email protected]0d0ba182014-06-03 12:40:43640
641 // Load the image asynchronously. The response will be sent to OnImageLoaded.
pkotwicza57a1f322014-10-21 00:24:30642 extensions::ImageLoader* loader = extensions::ImageLoader::Get(profile_);
[email protected]0d0ba182014-06-03 12:40:43643
644 std::vector<extensions::ImageLoader::ImageRepresentation> images_list;
645 images_list.push_back(extensions::ImageLoader::ImageRepresentation(
646 image,
647 extensions::ImageLoader::ImageRepresentation::NEVER_RESIZE,
648 gfx::Size(),
649 ui::SCALE_FACTOR_100P));
650 loader->LoadImagesAsync(
651 extension_,
652 images_list,
rdevlin.cronin3fe4bd32016-01-12 18:45:40653 base::Bind(&ExtensionInstallPrompt::OnImageLoaded,
654 weak_factory_.GetWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11655}
656
657void ExtensionInstallPrompt::ShowConfirmation() {
dchengc963c7142016-04-08 03:55:22658 std::unique_ptr<const PermissionSet> permissions_wrapper;
rdevlin.cronine2d0fd02015-09-24 22:35:49659 const PermissionSet* permissions_to_display = nullptr;
gpdavis.chromium0fbac4d2014-09-19 20:57:54660 if (custom_permissions_.get()) {
rdevlin.cronine2d0fd02015-09-24 22:35:49661 permissions_to_display = custom_permissions_.get();
gpdavis.chromium0fbac4d2014-09-19 20:57:54662 } else if (extension_) {
663 // Initialize permissions if they have not already been set so that
Devlin Cronind6e136a2018-05-15 23:39:32664 // any transformations are correctly reflected in the install prompt.
gpdavis.chromium0fbac4d2014-09-19 20:57:54665 extensions::PermissionsUpdater(
pkotwicza57a1f322014-10-21 00:24:30666 profile_, extensions::PermissionsUpdater::INIT_FLAG_TRANSIENT)
gpdavis.chromium0fbac4d2014-09-19 20:57:54667 .InitializePermissions(extension_);
668 permissions_to_display =
rdevlin.cronind630c302015-09-30 20:19:33669 &extension_->permissions_data()->active_permissions();
treib7496f63c2015-03-04 12:18:53670 // For delegated installs, all optional permissions are pre-approved by the
671 // person who triggers the install, so add them to the list.
treib3d41d9f2016-04-12 08:53:01672 if (prompt_->type() == DELEGATED_PERMISSIONS_PROMPT) {
rdevlin.cronind630c302015-09-30 20:19:33673 const PermissionSet& optional_permissions =
treib7496f63c2015-03-04 12:18:53674 extensions::PermissionsParser::GetOptionalPermissions(extension_);
rdevlin.cronine2d0fd02015-09-24 22:35:49675 permissions_wrapper = PermissionSet::CreateUnion(*permissions_to_display,
rdevlin.cronind630c302015-09-30 20:19:33676 optional_permissions);
rdevlin.cronine2d0fd02015-09-24 22:35:49677 permissions_to_display = permissions_wrapper.get();
treib7496f63c2015-03-04 12:18:53678 }
gpdavis.chromium0fbac4d2014-09-19 20:57:54679 }
680
rdevlin.cronine2d0fd02015-09-24 22:35:49681 if (permissions_to_display &&
[email protected]a397ec02014-08-08 15:48:13682 (!extension_ ||
683 !extensions::PermissionsData::ShouldSkipPermissionWarnings(
684 extension_->id()))) {
685 Manifest::Type type =
686 extension_ ? extension_->GetType() : Manifest::TYPE_UNKNOWN;
687 const extensions::PermissionMessageProvider* message_provider =
688 extensions::PermissionMessageProvider::Get();
treib7b45a34b2015-04-16 11:48:02689
isandrk98a3e4a12017-05-26 21:14:50690 prompt_->AddPermissions(message_provider->GetPermissionMessages(
Devlin Cronind6e136a2018-05-15 23:39:32691 message_provider->GetAllPermissionIDs(*permissions_to_display, type)));
[email protected]bebe1d02012-08-02 20:17:09692 }
[email protected]c82da8c42012-06-08 19:49:11693
rdevlin.croninc7f8d132017-02-22 14:29:02694 prompt_->set_extension(extension_);
treibd9e1d9d2015-04-24 11:17:04695 prompt_->set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]af6efb22012-10-12 02:23:05696
pkotwicz2f181782014-10-29 17:33:45697 if (show_params_->WasParentDestroyed()) {
rdevlin.cronin41593052016-01-08 01:40:12698 base::ResetAndReturn(&done_callback_).Run(Result::ABORTED);
pkotwicz2f181782014-10-29 17:33:45699 return;
700 }
701
rdevlin.croninca5bf2da2015-12-17 21:21:08702 g_last_prompt_type_for_tests = prompt_->type();
703 did_call_show_dialog_ = true;
704
rdevlin.cronin41593052016-01-08 01:40:12705 if (AutoConfirmPrompt(&done_callback_))
rdevlin.croninca5bf2da2015-12-17 21:21:08706 return;
707
[email protected]5db2e882012-12-20 10:17:26708 if (show_dialog_callback_.is_null())
rdevlin.cronin41593052016-01-08 01:40:12709 show_dialog_callback_ = GetDefaultShowDialogCallback();
710 base::ResetAndReturn(&show_dialog_callback_)
711 .Run(show_params_.get(), base::ResetAndReturn(&done_callback_),
712 std::move(prompt_));
[email protected]c82da8c42012-06-08 19:49:11713}