blob: 544c04e97d2f0b6f2b532009406a8c4f8486b330 [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
7#include <map>
8
9#include "base/command_line.h"
[email protected]b19fe572013-07-18 04:54:2610#include "base/message_loop/message_loop.h"
[email protected]3853a4c2013-02-11 17:15:5711#include "base/prefs/pref_service.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"
14#include "base/strings/stringprintf.h"
[email protected]112158af2013-06-07 23:46:1815#include "base/strings/utf_string_conversions.h"
[email protected]c82da8c42012-06-08 19:49:1116#include "chrome/browser/extensions/bundle_installer.h"
[email protected]c82da8c42012-06-08 19:49:1117#include "chrome/browser/extensions/extension_install_ui.h"
[email protected]ec7de0c5a2012-11-16 07:40:4718#include "chrome/browser/extensions/image_loader.h"
[email protected]c82da8c42012-06-08 19:49:1119#include "chrome/browser/profiles/profile.h"
[email protected]4f3fb352013-07-17 04:07:0120#include "chrome/browser/signin/profile_oauth2_token_service.h"
21#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
[email protected]32fc4ff72012-06-15 21:50:0122#include "chrome/browser/ui/browser.h"
[email protected]619f86182012-07-03 21:30:1823#include "chrome/browser/ui/browser_window.h"
[email protected]c82da8c42012-06-08 19:49:1124#include "chrome/common/chrome_switches.h"
[email protected]29e0c4e72013-02-01 04:42:5625#include "chrome/common/extensions/api/identity/oauth2_manifest_handler.h"
[email protected]c82da8c42012-06-08 19:49:1126#include "chrome/common/extensions/extension.h"
[email protected]faf87192012-08-17 00:07:5927#include "chrome/common/extensions/extension_constants.h"
[email protected]c82da8c42012-06-08 19:49:1128#include "chrome/common/extensions/extension_icon_set.h"
[email protected]544471a2012-10-13 05:27:0929#include "chrome/common/extensions/feature_switch.h"
[email protected]abe720c02013-04-12 04:00:1030#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
[email protected]bebe1d02012-08-02 20:17:0931#include "chrome/common/extensions/permissions/permission_set.h"
[email protected]13c68b62013-05-17 11:29:0532#include "chrome/common/extensions/permissions/permissions_data.h"
[email protected]fc5077942012-08-15 21:37:5933#include "chrome/common/pref_names.h"
[email protected]91e51d612012-10-21 23:03:0534#include "content/public/browser/web_contents.h"
[email protected]5db2e882012-12-20 10:17:2635#include "content/public/browser/web_contents_view.h"
[email protected]993da5e2013-03-23 21:25:1636#include "extensions/common/extension_resource.h"
[email protected]d42c11152013-08-22 19:36:3237#include "extensions/common/manifest.h"
[email protected]6bf90612013-08-15 00:36:2738#include "extensions/common/manifest_constants.h"
[email protected]885c0e92012-11-13 20:27:4239#include "extensions/common/url_pattern.h"
[email protected]c82da8c42012-06-08 19:49:1140#include "grit/chromium_strings.h"
41#include "grit/generated_resources.h"
[email protected]2a281332012-07-11 22:20:2342#include "grit/theme_resources.h"
[email protected]c82da8c42012-06-08 19:49:1143#include "ui/base/l10n/l10n_util.h"
44#include "ui/base/resource/resource_bundle.h"
45#include "ui/gfx/image/image.h"
46
[email protected]c82da8c42012-06-08 19:49:1147using extensions::BundleInstaller;
48using extensions::Extension;
[email protected]1d5e58b2013-01-31 08:41:4049using extensions::Manifest;
[email protected]c2e66e12012-06-27 06:27:0650using extensions::PermissionSet;
[email protected]c82da8c42012-06-08 19:49:1151
[email protected]612a1cb12012-10-17 13:18:0352namespace {
53
[email protected]c82da8c42012-06-08 19:49:1154static const int kTitleIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
55 0, // The regular install prompt depends on what's being installed.
56 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE,
57 IDS_EXTENSION_INSTALL_PROMPT_TITLE,
58 IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE,
[email protected]612a1cb12012-10-17 13:18:0359 IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE,
60 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_TITLE,
[email protected]15d267b42013-02-14 23:43:3261 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_TITLE,
[email protected]c82da8c42012-06-08 19:49:1162};
63static const int kHeadingIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
64 IDS_EXTENSION_INSTALL_PROMPT_HEADING,
65 0, // Inline installs use the extension name.
66 0, // Heading for bundle installs depends on the bundle contents.
67 IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING,
[email protected]612a1cb12012-10-17 13:18:0368 IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING,
[email protected]846606012012-10-19 18:42:2569 0, // External installs use different strings for extensions/apps.
[email protected]15d267b42013-02-14 23:43:3270 IDS_EXTENSION_POST_INSTALL_PERMISSIONS_PROMPT_HEADING,
71};
72static const int kButtons[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
73 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
74 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
75 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
76 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
77 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
78 ui::DIALOG_BUTTON_OK | ui::DIALOG_BUTTON_CANCEL,
79 ui::DIALOG_BUTTON_CANCEL,
[email protected]c82da8c42012-06-08 19:49:1180};
81static const int kAcceptButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
82 IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
83 IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
84 IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
85 IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON,
[email protected]612a1cb12012-10-17 13:18:0386 IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON,
[email protected]846606012012-10-19 18:42:2587 0, // External installs use different strings for extensions/apps.
[email protected]a2886e8b2013-06-08 05:15:0288 IDS_EXTENSION_PROMPT_PERMISSIONS_CLEAR_RETAINED_FILES_BUTTON,
[email protected]c82da8c42012-06-08 19:49:1189};
90static const int kAbortButtonIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
91 0, // These all use the platform's default cancel label.
92 0,
93 0,
94 0,
[email protected]612a1cb12012-10-17 13:18:0395 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON,
96 IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ABORT_BUTTON,
[email protected]15d267b42013-02-14 23:43:3297 IDS_CLOSE,
[email protected]c82da8c42012-06-08 19:49:1198};
99static const int kPermissionsHeaderIds[
100 ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
101 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
102 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
103 IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO,
104 IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO,
105 IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO,
[email protected]612a1cb12012-10-17 13:18:03106 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
[email protected]15d267b42013-02-14 23:43:32107 IDS_EXTENSION_PROMPT_CAN_ACCESS,
[email protected]c82da8c42012-06-08 19:49:11108};
[email protected]612a1cb12012-10-17 13:18:03109static const int kOAuthHeaderIds[ExtensionInstallPrompt::NUM_PROMPT_TYPES] = {
[email protected]fc5077942012-08-15 21:37:59110 IDS_EXTENSION_PROMPT_OAUTH_HEADER,
111 0, // Inline installs don't show OAuth permissions.
112 0, // Bundle installs don't show OAuth permissions.
113 IDS_EXTENSION_PROMPT_OAUTH_REENABLE_HEADER,
114 IDS_EXTENSION_PROMPT_OAUTH_PERMISSIONS_HEADER,
[email protected]612a1cb12012-10-17 13:18:03115 // TODO(mpcomplete): Do we need this for external install UI? If we do,
116 // we need to update FetchOAuthIssueAdviceIfNeeded.
117 0,
[email protected]15d267b42013-02-14 23:43:32118 0,
[email protected]fc5077942012-08-15 21:37:59119};
[email protected]c82da8c42012-06-08 19:49:11120
[email protected]c82da8c42012-06-08 19:49:11121// Size of extension icon in top left of dialog.
122const int kIconSize = 69;
123
[email protected]dd46a4ce2012-09-15 10:50:50124// Returns pixel size under maximal scale factor for the icon whose device
125// independent size is |size_in_dip|
126int GetSizeForMaxScaleFactor(int size_in_dip) {
[email protected]059d76b2012-11-16 17:25:42127 float max_scale_factor_scale =
128 ui::GetScaleFactorScale(ui::GetMaxScaleFactor());
[email protected]dd46a4ce2012-09-15 10:50:50129 return static_cast<int>(size_in_dip * max_scale_factor_scale);
130}
131
132// Returns bitmap for the default icon with size equal to the default icon's
133// pixel size under maximal supported scale factor.
134SkBitmap GetDefaultIconBitmapForMaxScaleFactor(bool is_app) {
[email protected]702d8b42013-02-27 20:55:50135 const gfx::ImageSkia& image = is_app ?
136 extensions::IconsInfo::GetDefaultAppIcon() :
137 extensions::IconsInfo::GetDefaultExtensionIcon();
138 return image.GetRepresentation(ui::GetMaxScaleFactor()).sk_bitmap();
[email protected]dd46a4ce2012-09-15 10:50:50139}
140
[email protected]af6efb22012-10-12 02:23:05141// If auto confirm is enabled then posts a task to proceed with or cancel the
142// install and returns true. Otherwise returns false.
143bool AutoConfirmPrompt(ExtensionInstallPrompt::Delegate* delegate) {
144 const CommandLine* cmdline = CommandLine::ForCurrentProcess();
145 if (!cmdline->HasSwitch(switches::kAppsGalleryInstallAutoConfirmForTests))
146 return false;
147 std::string value = cmdline->GetSwitchValueASCII(
148 switches::kAppsGalleryInstallAutoConfirmForTests);
149
150 // We use PostTask instead of calling the delegate directly here, because in
151 // the real implementations it's highly likely the message loop will be
152 // pumping a few times before the user clicks accept or cancel.
153 if (value == "accept") {
[email protected]b3a25092013-05-28 22:08:16154 base::MessageLoop::current()->PostTask(
[email protected]af6efb22012-10-12 02:23:05155 FROM_HERE,
156 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIProceed,
157 base::Unretained(delegate)));
158 return true;
159 }
160
161 if (value == "cancel") {
[email protected]b3a25092013-05-28 22:08:16162 base::MessageLoop::current()->PostTask(
[email protected]af6efb22012-10-12 02:23:05163 FROM_HERE,
164 base::Bind(&ExtensionInstallPrompt::Delegate::InstallUIAbort,
165 base::Unretained(delegate),
166 true));
167 return true;
168 }
169
170 NOTREACHED();
171 return false;
172}
173
[email protected]91e51d612012-10-21 23:03:05174Profile* ProfileForWebContents(content::WebContents* web_contents) {
175 if (!web_contents)
176 return NULL;
177 return Profile::FromBrowserContext(web_contents->GetBrowserContext());
178}
179
[email protected]5db2e882012-12-20 10:17:26180gfx::NativeWindow NativeWindowForWebContents(content::WebContents* contents) {
181 if (!contents)
182 return NULL;
183
184 return contents->GetView()->GetTopLevelNativeWindow();
185}
186
[email protected]c82da8c42012-06-08 19:49:11187} // namespace
188
[email protected]5db2e882012-12-20 10:17:26189ExtensionInstallPrompt::Prompt::Prompt(PromptType type)
[email protected]c82da8c42012-06-08 19:49:11190 : type_(type),
191 extension_(NULL),
192 bundle_(NULL),
193 average_rating_(0.0),
[email protected]dcde34b32013-07-31 02:28:45194 rating_count_(0),
195 show_user_count_(false) {
[email protected]c82da8c42012-06-08 19:49:11196}
197
198ExtensionInstallPrompt::Prompt::~Prompt() {
199}
200
201void ExtensionInstallPrompt::Prompt::SetPermissions(
202 const std::vector<string16>& permissions) {
203 permissions_ = permissions;
204}
205
[email protected]8ab7e6c2013-07-11 21:15:03206void ExtensionInstallPrompt::Prompt::SetPermissionsDetails(
207 const std::vector<string16>& details) {
208 details_ = details;
209}
210
[email protected]b70a2d92012-06-28 19:51:21211void ExtensionInstallPrompt::Prompt::SetOAuthIssueAdvice(
212 const IssueAdviceInfo& issue_advice) {
213 oauth_issue_advice_ = issue_advice;
214}
215
[email protected]5db2e882012-12-20 10:17:26216void ExtensionInstallPrompt::Prompt::SetUserNameFromProfile(Profile* profile) {
217 // |profile| can be NULL in unit tests.
218 if (profile) {
219 oauth_user_name_ = UTF8ToUTF16(profile->GetPrefs()->GetString(
220 prefs::kGoogleServicesUsername));
221 } else {
222 oauth_user_name_.clear();
223 }
224}
225
[email protected]c82da8c42012-06-08 19:49:11226void ExtensionInstallPrompt::Prompt::SetInlineInstallWebstoreData(
227 const std::string& localized_user_count,
[email protected]dcde34b32013-07-31 02:28:45228 bool show_user_count,
[email protected]c82da8c42012-06-08 19:49:11229 double average_rating,
230 int rating_count) {
231 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
232 localized_user_count_ = localized_user_count;
[email protected]dcde34b32013-07-31 02:28:45233 show_user_count_ = show_user_count;
[email protected]c82da8c42012-06-08 19:49:11234 average_rating_ = average_rating;
235 rating_count_ = rating_count;
236}
237
238string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
[email protected]c82da8c42012-06-08 19:49:11239 int resource_id = kTitleIds[type_];
240
241 if (type_ == INSTALL_PROMPT) {
242 if (extension_->is_app())
243 resource_id = IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE;
244 else if (extension_->is_theme())
245 resource_id = IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE;
246 else
247 resource_id = IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE;
[email protected]612a1cb12012-10-17 13:18:03248 } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
249 return l10n_util::GetStringFUTF16(
250 resource_id, UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11251 }
252
253 return l10n_util::GetStringUTF16(resource_id);
254}
255
256string16 ExtensionInstallPrompt::Prompt::GetHeading() const {
257 if (type_ == INLINE_INSTALL_PROMPT) {
258 return UTF8ToUTF16(extension_->name());
259 } else if (type_ == BUNDLE_INSTALL_PROMPT) {
260 return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING);
[email protected]612a1cb12012-10-17 13:18:03261 } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
[email protected]846606012012-10-19 18:42:25262 int resource_id = -1;
263 if (extension_->is_app())
264 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP;
265 else if (extension_->is_theme())
266 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME;
267 else
268 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION;
269 return l10n_util::GetStringUTF16(resource_id);
[email protected]c82da8c42012-06-08 19:49:11270 } else {
271 return l10n_util::GetStringFUTF16(
272 kHeadingIds[type_], UTF8ToUTF16(extension_->name()));
273 }
274}
275
[email protected]15d267b42013-02-14 23:43:32276int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
[email protected]a2886e8b2013-06-08 05:15:02277 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
278 ShouldDisplayRevokeFilesButton()) {
279 return kButtons[type_] | ui::DIALOG_BUTTON_OK;
280 }
281
[email protected]15d267b42013-02-14 23:43:32282 return kButtons[type_];
283}
284
285bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const {
[email protected]a2886e8b2013-06-08 05:15:02286 if (kAcceptButtonIds[type_] == 0)
287 return false;
288
289 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT)
290 return ShouldDisplayRevokeFilesButton();
291
292 return true;
[email protected]15d267b42013-02-14 23:43:32293}
294
[email protected]c82da8c42012-06-08 19:49:11295string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
[email protected]846606012012-10-19 18:42:25296 if (type_ == EXTERNAL_INSTALL_PROMPT) {
297 int id = -1;
298 if (extension_->is_app())
299 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
300 else if (extension_->is_theme())
301 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
302 else
303 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
304 return l10n_util::GetStringUTF16(id);
305 }
[email protected]c82da8c42012-06-08 19:49:11306 return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]);
307}
308
309bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
310 return kAbortButtonIds[type_] > 0;
311}
312
313string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
314 CHECK(HasAbortButtonLabel());
315 return l10n_util::GetStringUTF16(kAbortButtonIds[type_]);
316}
317
318string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
319 return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]);
320}
321
[email protected]813475e2012-07-02 23:54:18322string16 ExtensionInstallPrompt::Prompt::GetOAuthHeading() const {
[email protected]5db2e882012-12-20 10:17:26323 return l10n_util::GetStringFUTF16(kOAuthHeaderIds[type_], oauth_user_name_);
[email protected]813475e2012-07-02 23:54:18324}
325
[email protected]a2886e8b2013-06-08 05:15:02326string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
[email protected]8ab7e6c2013-07-11 21:15:03327 // TODO(finnur): Remove this once all platforms are using
328 // GetRetainedFilesHeadingWithCount().
[email protected]a2886e8b2013-06-08 05:15:02329 return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_RETAINED_FILES);
330}
331
[email protected]8ab7e6c2013-07-11 21:15:03332string16
333ExtensionInstallPrompt::Prompt::GetRetainedFilesHeadingWithCount() const {
334 return l10n_util::GetStringFUTF16(
335 IDS_EXTENSION_PROMPT_RETAINED_FILES_WITH_COUNT,
336 base::IntToString16(GetRetainedFileCount()));
337}
338
[email protected]f2cd8992013-06-11 17:30:18339bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
340 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
341}
342
[email protected]c82da8c42012-06-08 19:49:11343void ExtensionInstallPrompt::Prompt::AppendRatingStars(
344 StarAppender appender, void* data) const {
345 CHECK(appender);
346 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
347 int rating_integer = floor(average_rating_);
348 double rating_fractional = average_rating_ - rating_integer;
349
350 if (rating_fractional > 0.66) {
351 rating_integer++;
352 }
353
354 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
355 rating_fractional = 0;
356 }
357
358 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
359 int i;
360 for (i = 0; i < rating_integer; i++) {
361 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
362 }
363 if (rating_fractional) {
364 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
365 i++;
366 }
367 for (; i < kMaxExtensionRating; i++) {
368 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
369 }
370}
371
372string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
373 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
374 return l10n_util::GetStringFUTF16(
375 IDS_EXTENSION_RATING_COUNT,
376 UTF8ToUTF16(base::IntToString(rating_count_)));
377}
378
379string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
380 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
[email protected]dcde34b32013-07-31 02:28:45381
382 if (show_user_count_) {
383 return l10n_util::GetStringFUTF16(
384 IDS_EXTENSION_USER_COUNT,
385 UTF8ToUTF16(localized_user_count_));
386 } else {
387 return string16();
388 }
[email protected]c82da8c42012-06-08 19:49:11389}
390
391size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
392 return permissions_.size();
393}
394
[email protected]8ab7e6c2013-07-11 21:15:03395size_t ExtensionInstallPrompt::Prompt::GetPermissionsDetailsCount() const {
396 return details_.size();
397}
398
[email protected]c82da8c42012-06-08 19:49:11399string16 ExtensionInstallPrompt::Prompt::GetPermission(size_t index) const {
400 CHECK_LT(index, permissions_.size());
[email protected]ef2654e42012-08-11 03:57:56401 return permissions_[index];
[email protected]c82da8c42012-06-08 19:49:11402}
403
[email protected]8ab7e6c2013-07-11 21:15:03404string16 ExtensionInstallPrompt::Prompt::GetPermissionsDetails(
405 size_t index) const {
406 CHECK_LT(index, details_.size());
407 return details_[index];
408}
409
[email protected]b70a2d92012-06-28 19:51:21410size_t ExtensionInstallPrompt::Prompt::GetOAuthIssueCount() const {
411 return oauth_issue_advice_.size();
412}
413
414const IssueAdviceInfoEntry& ExtensionInstallPrompt::Prompt::GetOAuthIssue(
415 size_t index) const {
416 CHECK_LT(index, oauth_issue_advice_.size());
417 return oauth_issue_advice_[index];
418}
419
[email protected]a2886e8b2013-06-08 05:15:02420size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
421 return retained_files_.size();
422}
423
424string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index) const {
425 CHECK_LT(index, retained_files_.size());
426 return base::UTF8ToUTF16(retained_files_[index].AsUTF8Unsafe());
427}
428
429bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
430 return !retained_files_.empty();
431}
432
[email protected]5db2e882012-12-20 10:17:26433ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents)
434 : parent_web_contents(contents),
435 parent_window(NativeWindowForWebContents(contents)),
436 navigator(contents) {
437}
438
439ExtensionInstallPrompt::ShowParams::ShowParams(
440 gfx::NativeWindow window,
441 content::PageNavigator* navigator)
442 : parent_web_contents(NULL),
443 parent_window(window),
444 navigator(navigator) {
445}
446
[email protected]c82da8c42012-06-08 19:49:11447// static
448scoped_refptr<Extension>
449 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
450 const DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13451 int flags,
[email protected]c82da8c42012-06-08 19:49:11452 const std::string& id,
453 const std::string& localized_name,
454 const std::string& localized_description,
455 std::string* error) {
456 scoped_ptr<DictionaryValue> localized_manifest;
457 if (!localized_name.empty() || !localized_description.empty()) {
458 localized_manifest.reset(manifest->DeepCopy());
459 if (!localized_name.empty()) {
[email protected]6bf90612013-08-15 00:36:27460 localized_manifest->SetString(extensions::manifest_keys::kName,
[email protected]c82da8c42012-06-08 19:49:11461 localized_name);
462 }
463 if (!localized_description.empty()) {
[email protected]6bf90612013-08-15 00:36:27464 localized_manifest->SetString(extensions::manifest_keys::kDescription,
[email protected]c82da8c42012-06-08 19:49:11465 localized_description);
466 }
467 }
468
469 return Extension::Create(
[email protected]650b2d52013-02-10 03:41:45470 base::FilePath(),
[email protected]1d5e58b2013-01-31 08:41:40471 Manifest::INTERNAL,
[email protected]c82da8c42012-06-08 19:49:11472 localized_manifest.get() ? *localized_manifest.get() : *manifest,
[email protected]c422a862012-07-31 15:46:13473 flags,
[email protected]c82da8c42012-06-08 19:49:11474 id,
475 error);
476}
477
[email protected]619f86182012-07-03 21:30:18478ExtensionInstallPrompt::ExtensionInstallPrompt(
[email protected]91e51d612012-10-21 23:03:05479 content::WebContents* contents)
[email protected]16798da832012-08-30 20:46:04480 : record_oauth2_grant_(false),
[email protected]b3a25092013-05-28 22:08:16481 ui_loop_(base::MessageLoop::current()),
[email protected]c82da8c42012-06-08 19:49:11482 extension_(NULL),
[email protected]91e51d612012-10-21 23:03:05483 install_ui_(ExtensionInstallUI::Create(ProfileForWebContents(contents))),
[email protected]5db2e882012-12-20 10:17:26484 show_params_(contents),
[email protected]c82da8c42012-06-08 19:49:11485 delegate_(NULL),
[email protected]5db2e882012-12-20 10:17:26486 prompt_(UNSET_PROMPT_TYPE) {
487 prompt_.SetUserNameFromProfile(install_ui_->profile());
488}
489
490ExtensionInstallPrompt::ExtensionInstallPrompt(
491 Profile* profile,
492 gfx::NativeWindow native_window,
493 content::PageNavigator* navigator)
494 : record_oauth2_grant_(false),
[email protected]b3a25092013-05-28 22:08:16495 ui_loop_(base::MessageLoop::current()),
[email protected]5db2e882012-12-20 10:17:26496 extension_(NULL),
497 install_ui_(ExtensionInstallUI::Create(profile)),
498 show_params_(native_window, navigator),
499 delegate_(NULL),
500 prompt_(UNSET_PROMPT_TYPE) {
501 prompt_.SetUserNameFromProfile(install_ui_->profile());
[email protected]c82da8c42012-06-08 19:49:11502}
503
504ExtensionInstallPrompt::~ExtensionInstallPrompt() {
505}
506
507void ExtensionInstallPrompt::ConfirmBundleInstall(
508 extensions::BundleInstaller* bundle,
[email protected]c2e66e12012-06-27 06:27:06509 const PermissionSet* permissions) {
[email protected]b3a25092013-05-28 22:08:16510 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11511 bundle_ = bundle;
512 permissions_ = permissions;
513 delegate_ = bundle;
[email protected]5db2e882012-12-20 10:17:26514 prompt_.set_type(BUNDLE_INSTALL_PROMPT);
[email protected]c82da8c42012-06-08 19:49:11515
[email protected]b70a2d92012-06-28 19:51:21516 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11517}
518
[email protected]734bcec2012-10-08 20:29:05519void ExtensionInstallPrompt::ConfirmStandaloneInstall(
[email protected]c82da8c42012-06-08 19:49:11520 Delegate* delegate,
521 const Extension* extension,
522 SkBitmap* icon,
523 const ExtensionInstallPrompt::Prompt& prompt) {
[email protected]b3a25092013-05-28 22:08:16524 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11525 extension_ = extension;
526 permissions_ = extension->GetActivePermissions();
527 delegate_ = delegate;
528 prompt_ = prompt;
[email protected]c82da8c42012-06-08 19:49:11529
530 SetIcon(icon);
[email protected]b70a2d92012-06-28 19:51:21531 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11532}
533
[email protected]af6efb22012-10-12 02:23:05534void ExtensionInstallPrompt::ConfirmWebstoreInstall(
535 Delegate* delegate,
536 const Extension* extension,
537 const SkBitmap* icon,
538 const ShowDialogCallback& show_dialog_callback) {
[email protected]c82da8c42012-06-08 19:49:11539 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
540 // remaining fields.
541 extension_ = extension;
542 SetIcon(icon);
[email protected]af6efb22012-10-12 02:23:05543 ConfirmInstall(delegate, extension, show_dialog_callback);
[email protected]c82da8c42012-06-08 19:49:11544}
545
[email protected]af6efb22012-10-12 02:23:05546void ExtensionInstallPrompt::ConfirmInstall(
547 Delegate* delegate,
548 const Extension* extension,
549 const ShowDialogCallback& show_dialog_callback) {
[email protected]b3a25092013-05-28 22:08:16550 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11551 extension_ = extension;
552 permissions_ = extension->GetActivePermissions();
553 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26554 prompt_.set_type(INSTALL_PROMPT);
[email protected]af6efb22012-10-12 02:23:05555 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11556
557 // We special-case themes to not show any confirm UI. Instead they are
558 // immediately installed, and then we show an infobar (see OnInstallSuccess)
559 // to allow the user to revert if they don't like it.
560 //
561 // We don't do this in the case where off-store extension installs are
562 // disabled because in that case, we don't show the dangerous download UI, so
563 // we need the UI confirmation.
564 if (extension->is_theme()) {
565 if (extension->from_webstore() ||
[email protected]544471a2012-10-13 05:27:09566 extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
[email protected]c82da8c42012-06-08 19:49:11567 delegate->InstallUIProceed();
568 return;
569 }
570 }
571
572 LoadImageIfNeeded();
573}
574
575void ExtensionInstallPrompt::ConfirmReEnable(Delegate* delegate,
576 const Extension* extension) {
[email protected]b3a25092013-05-28 22:08:16577 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11578 extension_ = extension;
579 permissions_ = extension->GetActivePermissions();
580 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26581 prompt_.set_type(RE_ENABLE_PROMPT);
[email protected]c82da8c42012-06-08 19:49:11582
583 LoadImageIfNeeded();
584}
585
[email protected]612a1cb12012-10-17 13:18:03586void ExtensionInstallPrompt::ConfirmExternalInstall(
[email protected]c8ff7c9e2013-04-20 12:36:27587 Delegate* delegate,
588 const Extension* extension,
589 const ShowDialogCallback& show_dialog_callback) {
[email protected]b3a25092013-05-28 22:08:16590 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]612a1cb12012-10-17 13:18:03591 extension_ = extension;
592 permissions_ = extension->GetActivePermissions();
593 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26594 prompt_.set_type(EXTERNAL_INSTALL_PROMPT);
[email protected]c8ff7c9e2013-04-20 12:36:27595 show_dialog_callback_ = show_dialog_callback;
[email protected]612a1cb12012-10-17 13:18:03596
597 LoadImageIfNeeded();
598}
599
[email protected]c82da8c42012-06-08 19:49:11600void ExtensionInstallPrompt::ConfirmPermissions(
601 Delegate* delegate,
602 const Extension* extension,
[email protected]c2e66e12012-06-27 06:27:06603 const PermissionSet* permissions) {
[email protected]b3a25092013-05-28 22:08:16604 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11605 extension_ = extension;
606 permissions_ = permissions;
607 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26608 prompt_.set_type(PERMISSIONS_PROMPT);
[email protected]c82da8c42012-06-08 19:49:11609
610 LoadImageIfNeeded();
611}
612
[email protected]f746b3f2012-07-03 17:53:37613void ExtensionInstallPrompt::ConfirmIssueAdvice(
614 Delegate* delegate,
615 const Extension* extension,
616 const IssueAdviceInfo& issue_advice) {
[email protected]b3a25092013-05-28 22:08:16617 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]f746b3f2012-07-03 17:53:37618 extension_ = extension;
619 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26620 prompt_.set_type(PERMISSIONS_PROMPT);
[email protected]f746b3f2012-07-03 17:53:37621
622 record_oauth2_grant_ = true;
623 prompt_.SetOAuthIssueAdvice(issue_advice);
624
625 LoadImageIfNeeded();
626}
627
[email protected]a2886e8b2013-06-08 05:15:02628void ExtensionInstallPrompt::ReviewPermissions(
629 Delegate* delegate,
630 const Extension* extension,
631 const std::vector<base::FilePath>& retained_file_paths) {
[email protected]b3a25092013-05-28 22:08:16632 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]15d267b42013-02-14 23:43:32633 extension_ = extension;
634 permissions_ = extension->GetActivePermissions();
[email protected]a2886e8b2013-06-08 05:15:02635 prompt_.set_retained_files(retained_file_paths);
[email protected]15d267b42013-02-14 23:43:32636 delegate_ = delegate;
637 prompt_.set_type(POST_INSTALL_PERMISSIONS_PROMPT);
638
639 LoadImageIfNeeded();
640}
641
[email protected]c82da8c42012-06-08 19:49:11642void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
643 SkBitmap* icon) {
644 extension_ = extension;
645 SetIcon(icon);
646
647 install_ui_->OnInstallSuccess(extension, &icon_);
648}
649
[email protected]bf3d9df2012-07-24 23:20:27650void ExtensionInstallPrompt::OnInstallFailure(
651 const extensions::CrxInstallerError& error) {
[email protected]c82da8c42012-06-08 19:49:11652 install_ui_->OnInstallFailure(error);
653}
654
655void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
656 if (image)
657 icon_ = *image;
658 else
659 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50660 if (icon_.empty()) {
661 // Let's set default icon bitmap whose size is equal to the default icon's
662 // pixel size under maximal supported scale factor. If the bitmap is larger
663 // than the one we need, it will be scaled down by the ui code.
664 icon_ = GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app());
665 }
[email protected]c82da8c42012-06-08 19:49:11666}
667
[email protected]ec7de0c5a2012-11-16 07:40:47668void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
[email protected]c82da8c42012-06-08 19:49:11669 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
[email protected]b70a2d92012-06-28 19:51:21670 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11671}
672
673void ExtensionInstallPrompt::LoadImageIfNeeded() {
674 // Bundle install prompts do not have an icon.
[email protected]5db2e882012-12-20 10:17:26675 // Also |install_ui_.profile()| can be NULL in unit tests.
676 if (!icon_.empty() || !install_ui_->profile()) {
[email protected]b70a2d92012-06-28 19:51:21677 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11678 return;
679 }
680
681 // Load the image asynchronously. For the response, check OnImageLoaded.
[email protected]993da5e2013-03-23 21:25:16682 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
[email protected]702d8b42013-02-27 20:55:50683 extension_,
684 extension_misc::EXTENSION_ICON_LARGE,
685 ExtensionIconSet::MATCH_BIGGER);
[email protected]dd46a4ce2012-09-15 10:50:50686 // Load the icon whose pixel size is large enough to be displayed under
687 // maximal supported scale factor. UI code will scale the icon down if needed.
688 // TODO(tbarzic): We should use IconImage here and load the required bitmap
689 // lazily.
690 int pixel_size = GetSizeForMaxScaleFactor(kIconSize);
[email protected]5db2e882012-12-20 10:17:26691 extensions::ImageLoader::Get(install_ui_->profile())->LoadImageAsync(
[email protected]ec7de0c5a2012-11-16 07:40:47692 extension_, image, gfx::Size(pixel_size, pixel_size),
693 base::Bind(&ExtensionInstallPrompt::OnImageLoaded, AsWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11694}
695
[email protected]b70a2d92012-06-28 19:51:21696void ExtensionInstallPrompt::FetchOAuthIssueAdviceIfNeeded() {
[email protected]668ed2c2012-09-12 10:52:41697 // |extension_| may be NULL, e.g. in the bundle install case.
698 if (!extension_ ||
[email protected]5db2e882012-12-20 10:17:26699 prompt_.type() == BUNDLE_INSTALL_PROMPT ||
700 prompt_.type() == INLINE_INSTALL_PROMPT ||
701 prompt_.type() == EXTERNAL_INSTALL_PROMPT ||
[email protected]668ed2c2012-09-12 10:52:41702 prompt_.GetOAuthIssueCount() != 0U) {
703 ShowConfirmation();
704 return;
705 }
706
[email protected]29e0c4e72013-02-01 04:42:56707 const extensions::OAuth2Info& oauth2_info =
708 extensions::OAuth2Info::GetOAuth2Info(extension_);
[email protected]668ed2c2012-09-12 10:52:41709 if (oauth2_info.client_id.empty() ||
710 oauth2_info.scopes.empty()) {
[email protected]b70a2d92012-06-28 19:51:21711 ShowConfirmation();
712 return;
713 }
714
[email protected]4f3fb352013-07-17 04:07:01715 ProfileOAuth2TokenService* token_service =
716 ProfileOAuth2TokenServiceFactory::GetForProfile(install_ui_->profile());
717 if (!token_service || !token_service->RefreshTokenIsAvailable()) {
[email protected]54df7eb22013-04-30 03:39:12718 ShowConfirmation();
719 return;
720 }
[email protected]b70a2d92012-06-28 19:51:21721
[email protected]4f3fb352013-07-17 04:07:01722 // Get an access token from the token service.
723 login_token_request_ = token_service->StartRequest(
724 OAuth2TokenService::ScopeSet(), this);
725}
726
727void ExtensionInstallPrompt::OnGetTokenSuccess(
728 const OAuth2TokenService::Request* request,
729 const std::string& access_token,
730 const base::Time& expiration_time) {
731 DCHECK_EQ(login_token_request_.get(), request);
732 login_token_request_.reset();
733
734 const extensions::OAuth2Info& oauth2_info =
735 extensions::OAuth2Info::GetOAuth2Info(extension_);
736
[email protected]b70a2d92012-06-28 19:51:21737 token_flow_.reset(new OAuth2MintTokenFlow(
[email protected]4f3fb352013-07-17 04:07:01738 install_ui_->profile()->GetRequestContext(),
[email protected]b70a2d92012-06-28 19:51:21739 this,
740 OAuth2MintTokenFlow::Parameters(
[email protected]4f3fb352013-07-17 04:07:01741 access_token,
[email protected]b70a2d92012-06-28 19:51:21742 extension_->id(),
743 oauth2_info.client_id,
[email protected]d4a37f1c2012-07-09 21:36:13744 oauth2_info.scopes,
[email protected]b70a2d92012-06-28 19:51:21745 OAuth2MintTokenFlow::MODE_ISSUE_ADVICE)));
746 token_flow_->Start();
747}
748
[email protected]4f3fb352013-07-17 04:07:01749void ExtensionInstallPrompt::OnGetTokenFailure(
750 const OAuth2TokenService::Request* request,
751 const GoogleServiceAuthError& error) {
752 DCHECK_EQ(login_token_request_.get(), request);
753 login_token_request_.reset();
754 ShowConfirmation();
755}
756
[email protected]b70a2d92012-06-28 19:51:21757void ExtensionInstallPrompt::OnIssueAdviceSuccess(
758 const IssueAdviceInfo& advice_info) {
759 prompt_.SetOAuthIssueAdvice(advice_info);
760 record_oauth2_grant_ = true;
761 ShowConfirmation();
762}
763
764void ExtensionInstallPrompt::OnMintTokenFailure(
765 const GoogleServiceAuthError& error) {
766 ShowConfirmation();
767}
768
[email protected]c82da8c42012-06-08 19:49:11769void ExtensionInstallPrompt::ShowConfirmation() {
[email protected]dc24976f2013-06-02 21:15:09770 if (permissions_.get() &&
[email protected]13c68b62013-05-17 11:29:05771 (!extension_ ||
772 !extensions::PermissionsData::ShouldSkipPermissionWarnings(
773 extension_))) {
[email protected]1d5e58b2013-01-31 08:41:40774 Manifest::Type extension_type = extension_ ?
775 extension_->GetType() : Manifest::TYPE_UNKNOWN;
[email protected]8ab7e6c2013-07-11 21:15:03776 prompt_.SetPermissions(
777 permissions_->GetWarningMessages(extension_type));
778 prompt_.SetPermissionsDetails(
779 permissions_->GetWarningMessagesDetails(extension_type));
[email protected]bebe1d02012-08-02 20:17:09780 }
[email protected]c82da8c42012-06-08 19:49:11781
[email protected]5db2e882012-12-20 10:17:26782 switch (prompt_.type()) {
[email protected]c82da8c42012-06-08 19:49:11783 case PERMISSIONS_PROMPT:
784 case RE_ENABLE_PROMPT:
785 case INLINE_INSTALL_PROMPT:
[email protected]612a1cb12012-10-17 13:18:03786 case EXTERNAL_INSTALL_PROMPT:
[email protected]15d267b42013-02-14 23:43:32787 case INSTALL_PROMPT:
788 case POST_INSTALL_PERMISSIONS_PROMPT: {
[email protected]c82da8c42012-06-08 19:49:11789 prompt_.set_extension(extension_);
[email protected]32e7a9b2013-01-23 23:00:19790 prompt_.set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]c82da8c42012-06-08 19:49:11791 break;
792 }
793 case BUNDLE_INSTALL_PROMPT: {
794 prompt_.set_bundle(bundle_);
[email protected]c82da8c42012-06-08 19:49:11795 break;
796 }
797 default:
798 NOTREACHED() << "Unknown message";
[email protected]af6efb22012-10-12 02:23:05799 return;
[email protected]c82da8c42012-06-08 19:49:11800 }
[email protected]af6efb22012-10-12 02:23:05801
802 if (AutoConfirmPrompt(delegate_))
803 return;
804
[email protected]5db2e882012-12-20 10:17:26805 if (show_dialog_callback_.is_null())
806 GetDefaultShowDialogCallback().Run(show_params_, delegate_, prompt_);
807 else
808 show_dialog_callback_.Run(show_params_, delegate_, prompt_);
[email protected]c82da8c42012-06-08 19:49:11809}