blob: 9faaa3a38106b86605ebe2c58a15ba053f0adc91 [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]c82da8c42012-06-08 19:49:1110#include "base/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]b70a2d92012-06-28 19:51:2120#include "chrome/browser/signin/token_service.h"
21#include "chrome/browser/signin/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"
29#include "chrome/common/extensions/extension_manifest_constants.h"
[email protected]544471a2012-10-13 05:27:0930#include "chrome/common/extensions/feature_switch.h"
[email protected]1d5e58b2013-01-31 08:41:4031#include "chrome/common/extensions/manifest.h"
[email protected]abe720c02013-04-12 04:00:1032#include "chrome/common/extensions/manifest_handlers/icons_handler.h"
[email protected]bebe1d02012-08-02 20:17:0933#include "chrome/common/extensions/permissions/permission_set.h"
[email protected]13c68b62013-05-17 11:29:0534#include "chrome/common/extensions/permissions/permissions_data.h"
[email protected]fc5077942012-08-15 21:37:5935#include "chrome/common/pref_names.h"
[email protected]91e51d612012-10-21 23:03:0536#include "content/public/browser/web_contents.h"
[email protected]5db2e882012-12-20 10:17:2637#include "content/public/browser/web_contents_view.h"
[email protected]993da5e2013-03-23 21:25:1638#include "extensions/common/extension_resource.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]5db2e882012-12-20 10:17:26194 rating_count_(0) {
[email protected]c82da8c42012-06-08 19:49:11195}
196
197ExtensionInstallPrompt::Prompt::~Prompt() {
198}
199
200void ExtensionInstallPrompt::Prompt::SetPermissions(
201 const std::vector<string16>& permissions) {
202 permissions_ = permissions;
203}
204
[email protected]b70a2d92012-06-28 19:51:21205void ExtensionInstallPrompt::Prompt::SetOAuthIssueAdvice(
206 const IssueAdviceInfo& issue_advice) {
207 oauth_issue_advice_ = issue_advice;
208}
209
[email protected]5db2e882012-12-20 10:17:26210void ExtensionInstallPrompt::Prompt::SetUserNameFromProfile(Profile* profile) {
211 // |profile| can be NULL in unit tests.
212 if (profile) {
213 oauth_user_name_ = UTF8ToUTF16(profile->GetPrefs()->GetString(
214 prefs::kGoogleServicesUsername));
215 } else {
216 oauth_user_name_.clear();
217 }
218}
219
[email protected]c82da8c42012-06-08 19:49:11220void ExtensionInstallPrompt::Prompt::SetInlineInstallWebstoreData(
221 const std::string& localized_user_count,
222 double average_rating,
223 int rating_count) {
224 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
225 localized_user_count_ = localized_user_count;
226 average_rating_ = average_rating;
227 rating_count_ = rating_count;
228}
229
230string16 ExtensionInstallPrompt::Prompt::GetDialogTitle() const {
[email protected]c82da8c42012-06-08 19:49:11231 int resource_id = kTitleIds[type_];
232
233 if (type_ == INSTALL_PROMPT) {
234 if (extension_->is_app())
235 resource_id = IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE;
236 else if (extension_->is_theme())
237 resource_id = IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE;
238 else
239 resource_id = IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE;
[email protected]612a1cb12012-10-17 13:18:03240 } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
241 return l10n_util::GetStringFUTF16(
242 resource_id, UTF8ToUTF16(extension_->name()));
[email protected]c82da8c42012-06-08 19:49:11243 }
244
245 return l10n_util::GetStringUTF16(resource_id);
246}
247
248string16 ExtensionInstallPrompt::Prompt::GetHeading() const {
249 if (type_ == INLINE_INSTALL_PROMPT) {
250 return UTF8ToUTF16(extension_->name());
251 } else if (type_ == BUNDLE_INSTALL_PROMPT) {
252 return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING);
[email protected]612a1cb12012-10-17 13:18:03253 } else if (type_ == EXTERNAL_INSTALL_PROMPT) {
[email protected]846606012012-10-19 18:42:25254 int resource_id = -1;
255 if (extension_->is_app())
256 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_APP;
257 else if (extension_->is_theme())
258 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_THEME;
259 else
260 resource_id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_HEADING_EXTENSION;
261 return l10n_util::GetStringUTF16(resource_id);
[email protected]c82da8c42012-06-08 19:49:11262 } else {
263 return l10n_util::GetStringFUTF16(
264 kHeadingIds[type_], UTF8ToUTF16(extension_->name()));
265 }
266}
267
[email protected]15d267b42013-02-14 23:43:32268int ExtensionInstallPrompt::Prompt::GetDialogButtons() const {
[email protected]a2886e8b2013-06-08 05:15:02269 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT &&
270 ShouldDisplayRevokeFilesButton()) {
271 return kButtons[type_] | ui::DIALOG_BUTTON_OK;
272 }
273
[email protected]15d267b42013-02-14 23:43:32274 return kButtons[type_];
275}
276
277bool ExtensionInstallPrompt::Prompt::HasAcceptButtonLabel() const {
[email protected]a2886e8b2013-06-08 05:15:02278 if (kAcceptButtonIds[type_] == 0)
279 return false;
280
281 if (type_ == POST_INSTALL_PERMISSIONS_PROMPT)
282 return ShouldDisplayRevokeFilesButton();
283
284 return true;
[email protected]15d267b42013-02-14 23:43:32285}
286
[email protected]c82da8c42012-06-08 19:49:11287string16 ExtensionInstallPrompt::Prompt::GetAcceptButtonLabel() const {
[email protected]846606012012-10-19 18:42:25288 if (type_ == EXTERNAL_INSTALL_PROMPT) {
289 int id = -1;
290 if (extension_->is_app())
291 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_APP;
292 else if (extension_->is_theme())
293 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_THEME;
294 else
295 id = IDS_EXTENSION_EXTERNAL_INSTALL_PROMPT_ACCEPT_BUTTON_EXTENSION;
296 return l10n_util::GetStringUTF16(id);
297 }
[email protected]c82da8c42012-06-08 19:49:11298 return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]);
299}
300
301bool ExtensionInstallPrompt::Prompt::HasAbortButtonLabel() const {
302 return kAbortButtonIds[type_] > 0;
303}
304
305string16 ExtensionInstallPrompt::Prompt::GetAbortButtonLabel() const {
306 CHECK(HasAbortButtonLabel());
307 return l10n_util::GetStringUTF16(kAbortButtonIds[type_]);
308}
309
310string16 ExtensionInstallPrompt::Prompt::GetPermissionsHeading() const {
311 return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]);
312}
313
[email protected]813475e2012-07-02 23:54:18314string16 ExtensionInstallPrompt::Prompt::GetOAuthHeading() const {
[email protected]5db2e882012-12-20 10:17:26315 return l10n_util::GetStringFUTF16(kOAuthHeaderIds[type_], oauth_user_name_);
[email protected]813475e2012-07-02 23:54:18316}
317
[email protected]a2886e8b2013-06-08 05:15:02318string16 ExtensionInstallPrompt::Prompt::GetRetainedFilesHeading() const {
319 return l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_RETAINED_FILES);
320}
321
[email protected]f2cd8992013-06-11 17:30:18322bool ExtensionInstallPrompt::Prompt::ShouldShowPermissions() const {
323 return GetPermissionCount() > 0 || type_ == POST_INSTALL_PERMISSIONS_PROMPT;
324}
325
[email protected]c82da8c42012-06-08 19:49:11326void ExtensionInstallPrompt::Prompt::AppendRatingStars(
327 StarAppender appender, void* data) const {
328 CHECK(appender);
329 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
330 int rating_integer = floor(average_rating_);
331 double rating_fractional = average_rating_ - rating_integer;
332
333 if (rating_fractional > 0.66) {
334 rating_integer++;
335 }
336
337 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
338 rating_fractional = 0;
339 }
340
341 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
342 int i;
343 for (i = 0; i < rating_integer; i++) {
344 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
345 }
346 if (rating_fractional) {
347 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
348 i++;
349 }
350 for (; i < kMaxExtensionRating; i++) {
351 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
352 }
353}
354
355string16 ExtensionInstallPrompt::Prompt::GetRatingCount() const {
356 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
357 return l10n_util::GetStringFUTF16(
358 IDS_EXTENSION_RATING_COUNT,
359 UTF8ToUTF16(base::IntToString(rating_count_)));
360}
361
362string16 ExtensionInstallPrompt::Prompt::GetUserCount() const {
363 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
364 return l10n_util::GetStringFUTF16(
365 IDS_EXTENSION_USER_COUNT,
366 UTF8ToUTF16(localized_user_count_));
367}
368
369size_t ExtensionInstallPrompt::Prompt::GetPermissionCount() const {
370 return permissions_.size();
371}
372
373string16 ExtensionInstallPrompt::Prompt::GetPermission(size_t index) const {
374 CHECK_LT(index, permissions_.size());
[email protected]ef2654e42012-08-11 03:57:56375 return permissions_[index];
[email protected]c82da8c42012-06-08 19:49:11376}
377
[email protected]b70a2d92012-06-28 19:51:21378size_t ExtensionInstallPrompt::Prompt::GetOAuthIssueCount() const {
379 return oauth_issue_advice_.size();
380}
381
382const IssueAdviceInfoEntry& ExtensionInstallPrompt::Prompt::GetOAuthIssue(
383 size_t index) const {
384 CHECK_LT(index, oauth_issue_advice_.size());
385 return oauth_issue_advice_[index];
386}
387
[email protected]a2886e8b2013-06-08 05:15:02388size_t ExtensionInstallPrompt::Prompt::GetRetainedFileCount() const {
389 return retained_files_.size();
390}
391
392string16 ExtensionInstallPrompt::Prompt::GetRetainedFile(size_t index) const {
393 CHECK_LT(index, retained_files_.size());
394 return base::UTF8ToUTF16(retained_files_[index].AsUTF8Unsafe());
395}
396
397bool ExtensionInstallPrompt::Prompt::ShouldDisplayRevokeFilesButton() const {
398 return !retained_files_.empty();
399}
400
[email protected]5db2e882012-12-20 10:17:26401ExtensionInstallPrompt::ShowParams::ShowParams(content::WebContents* contents)
402 : parent_web_contents(contents),
403 parent_window(NativeWindowForWebContents(contents)),
404 navigator(contents) {
405}
406
407ExtensionInstallPrompt::ShowParams::ShowParams(
408 gfx::NativeWindow window,
409 content::PageNavigator* navigator)
410 : parent_web_contents(NULL),
411 parent_window(window),
412 navigator(navigator) {
413}
414
[email protected]c82da8c42012-06-08 19:49:11415// static
416scoped_refptr<Extension>
417 ExtensionInstallPrompt::GetLocalizedExtensionForDisplay(
418 const DictionaryValue* manifest,
[email protected]c422a862012-07-31 15:46:13419 int flags,
[email protected]c82da8c42012-06-08 19:49:11420 const std::string& id,
421 const std::string& localized_name,
422 const std::string& localized_description,
423 std::string* error) {
424 scoped_ptr<DictionaryValue> localized_manifest;
425 if (!localized_name.empty() || !localized_description.empty()) {
426 localized_manifest.reset(manifest->DeepCopy());
427 if (!localized_name.empty()) {
428 localized_manifest->SetString(extension_manifest_keys::kName,
429 localized_name);
430 }
431 if (!localized_description.empty()) {
432 localized_manifest->SetString(extension_manifest_keys::kDescription,
433 localized_description);
434 }
435 }
436
437 return Extension::Create(
[email protected]650b2d52013-02-10 03:41:45438 base::FilePath(),
[email protected]1d5e58b2013-01-31 08:41:40439 Manifest::INTERNAL,
[email protected]c82da8c42012-06-08 19:49:11440 localized_manifest.get() ? *localized_manifest.get() : *manifest,
[email protected]c422a862012-07-31 15:46:13441 flags,
[email protected]c82da8c42012-06-08 19:49:11442 id,
443 error);
444}
445
[email protected]619f86182012-07-03 21:30:18446ExtensionInstallPrompt::ExtensionInstallPrompt(
[email protected]91e51d612012-10-21 23:03:05447 content::WebContents* contents)
[email protected]16798da832012-08-30 20:46:04448 : record_oauth2_grant_(false),
[email protected]b3a25092013-05-28 22:08:16449 ui_loop_(base::MessageLoop::current()),
[email protected]c82da8c42012-06-08 19:49:11450 extension_(NULL),
[email protected]91e51d612012-10-21 23:03:05451 install_ui_(ExtensionInstallUI::Create(ProfileForWebContents(contents))),
[email protected]5db2e882012-12-20 10:17:26452 show_params_(contents),
[email protected]c82da8c42012-06-08 19:49:11453 delegate_(NULL),
[email protected]5db2e882012-12-20 10:17:26454 prompt_(UNSET_PROMPT_TYPE) {
455 prompt_.SetUserNameFromProfile(install_ui_->profile());
456}
457
458ExtensionInstallPrompt::ExtensionInstallPrompt(
459 Profile* profile,
460 gfx::NativeWindow native_window,
461 content::PageNavigator* navigator)
462 : record_oauth2_grant_(false),
[email protected]b3a25092013-05-28 22:08:16463 ui_loop_(base::MessageLoop::current()),
[email protected]5db2e882012-12-20 10:17:26464 extension_(NULL),
465 install_ui_(ExtensionInstallUI::Create(profile)),
466 show_params_(native_window, navigator),
467 delegate_(NULL),
468 prompt_(UNSET_PROMPT_TYPE) {
469 prompt_.SetUserNameFromProfile(install_ui_->profile());
[email protected]c82da8c42012-06-08 19:49:11470}
471
472ExtensionInstallPrompt::~ExtensionInstallPrompt() {
473}
474
475void ExtensionInstallPrompt::ConfirmBundleInstall(
476 extensions::BundleInstaller* bundle,
[email protected]c2e66e12012-06-27 06:27:06477 const PermissionSet* permissions) {
[email protected]b3a25092013-05-28 22:08:16478 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11479 bundle_ = bundle;
480 permissions_ = permissions;
481 delegate_ = bundle;
[email protected]5db2e882012-12-20 10:17:26482 prompt_.set_type(BUNDLE_INSTALL_PROMPT);
[email protected]c82da8c42012-06-08 19:49:11483
[email protected]b70a2d92012-06-28 19:51:21484 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11485}
486
[email protected]734bcec2012-10-08 20:29:05487void ExtensionInstallPrompt::ConfirmStandaloneInstall(
[email protected]c82da8c42012-06-08 19:49:11488 Delegate* delegate,
489 const Extension* extension,
490 SkBitmap* icon,
491 const ExtensionInstallPrompt::Prompt& prompt) {
[email protected]b3a25092013-05-28 22:08:16492 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11493 extension_ = extension;
494 permissions_ = extension->GetActivePermissions();
495 delegate_ = delegate;
496 prompt_ = prompt;
[email protected]c82da8c42012-06-08 19:49:11497
498 SetIcon(icon);
[email protected]b70a2d92012-06-28 19:51:21499 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11500}
501
[email protected]af6efb22012-10-12 02:23:05502void ExtensionInstallPrompt::ConfirmWebstoreInstall(
503 Delegate* delegate,
504 const Extension* extension,
505 const SkBitmap* icon,
506 const ShowDialogCallback& show_dialog_callback) {
[email protected]c82da8c42012-06-08 19:49:11507 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
508 // remaining fields.
509 extension_ = extension;
510 SetIcon(icon);
[email protected]af6efb22012-10-12 02:23:05511 ConfirmInstall(delegate, extension, show_dialog_callback);
[email protected]c82da8c42012-06-08 19:49:11512}
513
[email protected]af6efb22012-10-12 02:23:05514void ExtensionInstallPrompt::ConfirmInstall(
515 Delegate* delegate,
516 const Extension* extension,
517 const ShowDialogCallback& show_dialog_callback) {
[email protected]b3a25092013-05-28 22:08:16518 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11519 extension_ = extension;
520 permissions_ = extension->GetActivePermissions();
521 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26522 prompt_.set_type(INSTALL_PROMPT);
[email protected]af6efb22012-10-12 02:23:05523 show_dialog_callback_ = show_dialog_callback;
[email protected]c82da8c42012-06-08 19:49:11524
525 // We special-case themes to not show any confirm UI. Instead they are
526 // immediately installed, and then we show an infobar (see OnInstallSuccess)
527 // to allow the user to revert if they don't like it.
528 //
529 // We don't do this in the case where off-store extension installs are
530 // disabled because in that case, we don't show the dangerous download UI, so
531 // we need the UI confirmation.
532 if (extension->is_theme()) {
533 if (extension->from_webstore() ||
[email protected]544471a2012-10-13 05:27:09534 extensions::FeatureSwitch::easy_off_store_install()->IsEnabled()) {
[email protected]c82da8c42012-06-08 19:49:11535 delegate->InstallUIProceed();
536 return;
537 }
538 }
539
540 LoadImageIfNeeded();
541}
542
543void ExtensionInstallPrompt::ConfirmReEnable(Delegate* delegate,
544 const Extension* extension) {
[email protected]b3a25092013-05-28 22:08:16545 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11546 extension_ = extension;
547 permissions_ = extension->GetActivePermissions();
548 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26549 prompt_.set_type(RE_ENABLE_PROMPT);
[email protected]c82da8c42012-06-08 19:49:11550
551 LoadImageIfNeeded();
552}
553
[email protected]612a1cb12012-10-17 13:18:03554void ExtensionInstallPrompt::ConfirmExternalInstall(
[email protected]c8ff7c9e2013-04-20 12:36:27555 Delegate* delegate,
556 const Extension* extension,
557 const ShowDialogCallback& show_dialog_callback) {
[email protected]b3a25092013-05-28 22:08:16558 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]612a1cb12012-10-17 13:18:03559 extension_ = extension;
560 permissions_ = extension->GetActivePermissions();
561 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26562 prompt_.set_type(EXTERNAL_INSTALL_PROMPT);
[email protected]c8ff7c9e2013-04-20 12:36:27563 show_dialog_callback_ = show_dialog_callback;
[email protected]612a1cb12012-10-17 13:18:03564
565 LoadImageIfNeeded();
566}
567
[email protected]c82da8c42012-06-08 19:49:11568void ExtensionInstallPrompt::ConfirmPermissions(
569 Delegate* delegate,
570 const Extension* extension,
[email protected]c2e66e12012-06-27 06:27:06571 const PermissionSet* permissions) {
[email protected]b3a25092013-05-28 22:08:16572 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]c82da8c42012-06-08 19:49:11573 extension_ = extension;
574 permissions_ = permissions;
575 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26576 prompt_.set_type(PERMISSIONS_PROMPT);
[email protected]c82da8c42012-06-08 19:49:11577
578 LoadImageIfNeeded();
579}
580
[email protected]f746b3f2012-07-03 17:53:37581void ExtensionInstallPrompt::ConfirmIssueAdvice(
582 Delegate* delegate,
583 const Extension* extension,
584 const IssueAdviceInfo& issue_advice) {
[email protected]b3a25092013-05-28 22:08:16585 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]f746b3f2012-07-03 17:53:37586 extension_ = extension;
587 delegate_ = delegate;
[email protected]5db2e882012-12-20 10:17:26588 prompt_.set_type(PERMISSIONS_PROMPT);
[email protected]f746b3f2012-07-03 17:53:37589
590 record_oauth2_grant_ = true;
591 prompt_.SetOAuthIssueAdvice(issue_advice);
592
593 LoadImageIfNeeded();
594}
595
[email protected]a2886e8b2013-06-08 05:15:02596void ExtensionInstallPrompt::ReviewPermissions(
597 Delegate* delegate,
598 const Extension* extension,
599 const std::vector<base::FilePath>& retained_file_paths) {
[email protected]b3a25092013-05-28 22:08:16600 DCHECK(ui_loop_ == base::MessageLoop::current());
[email protected]15d267b42013-02-14 23:43:32601 extension_ = extension;
602 permissions_ = extension->GetActivePermissions();
[email protected]a2886e8b2013-06-08 05:15:02603 prompt_.set_retained_files(retained_file_paths);
[email protected]15d267b42013-02-14 23:43:32604 delegate_ = delegate;
605 prompt_.set_type(POST_INSTALL_PERMISSIONS_PROMPT);
606
607 LoadImageIfNeeded();
608}
609
[email protected]c82da8c42012-06-08 19:49:11610void ExtensionInstallPrompt::OnInstallSuccess(const Extension* extension,
611 SkBitmap* icon) {
612 extension_ = extension;
613 SetIcon(icon);
614
615 install_ui_->OnInstallSuccess(extension, &icon_);
616}
617
[email protected]bf3d9df2012-07-24 23:20:27618void ExtensionInstallPrompt::OnInstallFailure(
619 const extensions::CrxInstallerError& error) {
[email protected]c82da8c42012-06-08 19:49:11620 install_ui_->OnInstallFailure(error);
621}
622
623void ExtensionInstallPrompt::SetIcon(const SkBitmap* image) {
624 if (image)
625 icon_ = *image;
626 else
627 icon_ = SkBitmap();
[email protected]dd46a4ce2012-09-15 10:50:50628 if (icon_.empty()) {
629 // Let's set default icon bitmap whose size is equal to the default icon's
630 // pixel size under maximal supported scale factor. If the bitmap is larger
631 // than the one we need, it will be scaled down by the ui code.
632 icon_ = GetDefaultIconBitmapForMaxScaleFactor(extension_->is_app());
633 }
[email protected]c82da8c42012-06-08 19:49:11634}
635
[email protected]ec7de0c5a2012-11-16 07:40:47636void ExtensionInstallPrompt::OnImageLoaded(const gfx::Image& image) {
[email protected]c82da8c42012-06-08 19:49:11637 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
[email protected]b70a2d92012-06-28 19:51:21638 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11639}
640
641void ExtensionInstallPrompt::LoadImageIfNeeded() {
642 // Bundle install prompts do not have an icon.
[email protected]5db2e882012-12-20 10:17:26643 // Also |install_ui_.profile()| can be NULL in unit tests.
644 if (!icon_.empty() || !install_ui_->profile()) {
[email protected]b70a2d92012-06-28 19:51:21645 FetchOAuthIssueAdviceIfNeeded();
[email protected]c82da8c42012-06-08 19:49:11646 return;
647 }
648
649 // Load the image asynchronously. For the response, check OnImageLoaded.
[email protected]993da5e2013-03-23 21:25:16650 extensions::ExtensionResource image = extensions::IconsInfo::GetIconResource(
[email protected]702d8b42013-02-27 20:55:50651 extension_,
652 extension_misc::EXTENSION_ICON_LARGE,
653 ExtensionIconSet::MATCH_BIGGER);
[email protected]dd46a4ce2012-09-15 10:50:50654 // Load the icon whose pixel size is large enough to be displayed under
655 // maximal supported scale factor. UI code will scale the icon down if needed.
656 // TODO(tbarzic): We should use IconImage here and load the required bitmap
657 // lazily.
658 int pixel_size = GetSizeForMaxScaleFactor(kIconSize);
[email protected]5db2e882012-12-20 10:17:26659 extensions::ImageLoader::Get(install_ui_->profile())->LoadImageAsync(
[email protected]ec7de0c5a2012-11-16 07:40:47660 extension_, image, gfx::Size(pixel_size, pixel_size),
661 base::Bind(&ExtensionInstallPrompt::OnImageLoaded, AsWeakPtr()));
[email protected]c82da8c42012-06-08 19:49:11662}
663
[email protected]b70a2d92012-06-28 19:51:21664void ExtensionInstallPrompt::FetchOAuthIssueAdviceIfNeeded() {
[email protected]668ed2c2012-09-12 10:52:41665 // |extension_| may be NULL, e.g. in the bundle install case.
666 if (!extension_ ||
[email protected]5db2e882012-12-20 10:17:26667 prompt_.type() == BUNDLE_INSTALL_PROMPT ||
668 prompt_.type() == INLINE_INSTALL_PROMPT ||
669 prompt_.type() == EXTERNAL_INSTALL_PROMPT ||
[email protected]668ed2c2012-09-12 10:52:41670 prompt_.GetOAuthIssueCount() != 0U) {
671 ShowConfirmation();
672 return;
673 }
674
[email protected]29e0c4e72013-02-01 04:42:56675 const extensions::OAuth2Info& oauth2_info =
676 extensions::OAuth2Info::GetOAuth2Info(extension_);
[email protected]668ed2c2012-09-12 10:52:41677 if (oauth2_info.client_id.empty() ||
678 oauth2_info.scopes.empty()) {
[email protected]b70a2d92012-06-28 19:51:21679 ShowConfirmation();
680 return;
681 }
682
[email protected]7cac5262012-09-26 17:32:25683 Profile* profile = install_ui_->profile();
[email protected]54df7eb22013-04-30 03:39:12684 // The token service can be NULL for incognito profiles.
[email protected]b70a2d92012-06-28 19:51:21685 TokenService* token_service = TokenServiceFactory::GetForProfile(profile);
[email protected]54df7eb22013-04-30 03:39:12686 if (!token_service) {
687 ShowConfirmation();
688 return;
689 }
[email protected]b70a2d92012-06-28 19:51:21690
691 token_flow_.reset(new OAuth2MintTokenFlow(
692 profile->GetRequestContext(),
693 this,
694 OAuth2MintTokenFlow::Parameters(
695 token_service->GetOAuth2LoginRefreshToken(),
696 extension_->id(),
697 oauth2_info.client_id,
[email protected]d4a37f1c2012-07-09 21:36:13698 oauth2_info.scopes,
[email protected]b70a2d92012-06-28 19:51:21699 OAuth2MintTokenFlow::MODE_ISSUE_ADVICE)));
700 token_flow_->Start();
701}
702
703void ExtensionInstallPrompt::OnIssueAdviceSuccess(
704 const IssueAdviceInfo& advice_info) {
705 prompt_.SetOAuthIssueAdvice(advice_info);
706 record_oauth2_grant_ = true;
707 ShowConfirmation();
708}
709
710void ExtensionInstallPrompt::OnMintTokenFailure(
711 const GoogleServiceAuthError& error) {
712 ShowConfirmation();
713}
714
[email protected]c82da8c42012-06-08 19:49:11715void ExtensionInstallPrompt::ShowConfirmation() {
[email protected]dc24976f2013-06-02 21:15:09716 if (permissions_.get() &&
[email protected]13c68b62013-05-17 11:29:05717 (!extension_ ||
718 !extensions::PermissionsData::ShouldSkipPermissionWarnings(
719 extension_))) {
[email protected]1d5e58b2013-01-31 08:41:40720 Manifest::Type extension_type = extension_ ?
721 extension_->GetType() : Manifest::TYPE_UNKNOWN;
[email protected]a6e5c9b2012-08-09 04:06:25722 prompt_.SetPermissions(permissions_->GetWarningMessages(extension_type));
[email protected]bebe1d02012-08-02 20:17:09723 }
[email protected]c82da8c42012-06-08 19:49:11724
[email protected]5db2e882012-12-20 10:17:26725 switch (prompt_.type()) {
[email protected]c82da8c42012-06-08 19:49:11726 case PERMISSIONS_PROMPT:
727 case RE_ENABLE_PROMPT:
728 case INLINE_INSTALL_PROMPT:
[email protected]612a1cb12012-10-17 13:18:03729 case EXTERNAL_INSTALL_PROMPT:
[email protected]15d267b42013-02-14 23:43:32730 case INSTALL_PROMPT:
731 case POST_INSTALL_PERMISSIONS_PROMPT: {
[email protected]c82da8c42012-06-08 19:49:11732 prompt_.set_extension(extension_);
[email protected]32e7a9b2013-01-23 23:00:19733 prompt_.set_icon(gfx::Image::CreateFrom1xBitmap(icon_));
[email protected]c82da8c42012-06-08 19:49:11734 break;
735 }
736 case BUNDLE_INSTALL_PROMPT: {
737 prompt_.set_bundle(bundle_);
[email protected]c82da8c42012-06-08 19:49:11738 break;
739 }
740 default:
741 NOTREACHED() << "Unknown message";
[email protected]af6efb22012-10-12 02:23:05742 return;
[email protected]c82da8c42012-06-08 19:49:11743 }
[email protected]af6efb22012-10-12 02:23:05744
745 if (AutoConfirmPrompt(delegate_))
746 return;
747
[email protected]5db2e882012-12-20 10:17:26748 if (show_dialog_callback_.is_null())
749 GetDefaultShowDialogCallback().Run(show_params_, delegate_, prompt_);
750 else
751 show_dialog_callback_.Run(show_params_, delegate_, prompt_);
[email protected]c82da8c42012-06-08 19:49:11752}