Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(424)

Side by Side Diff: chrome/browser/extensions/extension_install_ui.cc

Issue 10388252: Refactoring ExtenionInstallUI to abstract the Browser references. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Synced + mac fix Created 8 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/extension_install_ui.h" 5 #include "chrome/browser/extensions/extension_install_ui.h"
6 6 ExtensionInstallUI::ExtensionInstallUI() {
7 #include <map>
8
9 #include "base/command_line.h"
10 #include "base/file_util.h"
11 #include "base/message_loop.h"
12 #include "base/string_number_conversions.h"
13 #include "base/string_util.h"
14 #include "base/stringprintf.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/extensions/bundle_installer.h"
17 #include "chrome/browser/extensions/extension_install_dialog.h"
18 #include "chrome/browser/extensions/theme_installed_infobar_delegate.h"
19 #include "chrome/browser/infobars/infobar_tab_helper.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/themes/theme_service.h"
22 #include "chrome/browser/themes/theme_service_factory.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_dialogs.h"
25 #include "chrome/browser/ui/browser_finder.h"
26 #include "chrome/browser/ui/browser_navigator.h"
27 #include "chrome/browser/ui/browser_window.h"
28 #include "chrome/browser/ui/simple_message_box.h"
29 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
30 #include "chrome/browser/ui/tabs/tab_strip_model.h"
31 #include "chrome/browser/ui/webui/ntp/new_tab_ui.h"
32 #include "chrome/common/chrome_notification_types.h"
33 #include "chrome/common/chrome_switches.h"
34 #include "chrome/common/extensions/extension.h"
35 #include "chrome/common/extensions/extension_icon_set.h"
36 #include "chrome/common/extensions/extension_manifest_constants.h"
37 #include "chrome/common/extensions/extension_resource.h"
38 #include "chrome/common/extensions/extension_switch_utils.h"
39 #include "chrome/common/extensions/url_pattern.h"
40 #include "chrome/common/url_constants.h"
41 #include "content/public/browser/notification_service.h"
42 #include "grit/chromium_strings.h"
43 #include "grit/generated_resources.h"
44 #include "grit/theme_resources.h"
45 #include "ui/base/l10n/l10n_util.h"
46 #include "ui/base/resource/resource_bundle.h"
47 #include "ui/gfx/image/image.h"
48
49 #if defined(USE_ASH)
50 #include "ash/shell.h"
51 #endif
52
53 using content::WebContents;
54 using extensions::BundleInstaller;
55 using extensions::Extension;
56
57 static const int kTitleIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = {
58 0, // The regular install prompt depends on what's being installed.
59 IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE,
60 IDS_EXTENSION_INSTALL_PROMPT_TITLE,
61 IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE,
62 IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE
63 };
64 static const int kHeadingIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = {
65 IDS_EXTENSION_INSTALL_PROMPT_HEADING,
66 0, // Inline installs use the extension name.
67 0, // Heading for bundle installs depends on the bundle contents.
68 IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING,
69 IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING
70 };
71 static const int kAcceptButtonIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = {
72 IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
73 IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
74 IDS_EXTENSION_PROMPT_INSTALL_BUTTON,
75 IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON,
76 IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON
77 };
78 static const int kAbortButtonIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = {
79 0, // These all use the platform's default cancel label.
80 0,
81 0,
82 0,
83 IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON
84 };
85 static const int kPermissionsHeaderIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = {
86 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
87 IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO,
88 IDS_EXTENSION_PROMPT_THESE_WILL_HAVE_ACCESS_TO,
89 IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO,
90 IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO,
91 };
92
93 namespace {
94
95 // Size of extension icon in top left of dialog.
96 const int kIconSize = 69;
97
98 } // namespace
99
100 ExtensionInstallUI::Prompt::Prompt(PromptType type)
101 : type_(type),
102 extension_(NULL),
103 bundle_(NULL),
104 average_rating_(0.0),
105 rating_count_(0) {
106 }
107
108 ExtensionInstallUI::Prompt::~Prompt() {
109 }
110
111 void ExtensionInstallUI::Prompt::SetPermissions(
112 const std::vector<string16>& permissions) {
113 permissions_ = permissions;
114 }
115
116 void ExtensionInstallUI::Prompt::SetInlineInstallWebstoreData(
117 const std::string& localized_user_count,
118 double average_rating,
119 int rating_count) {
120 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
121 localized_user_count_ = localized_user_count;
122 average_rating_ = average_rating;
123 rating_count_ = rating_count;
124 }
125
126 string16 ExtensionInstallUI::Prompt::GetDialogTitle() const {
127 int resource_id = kTitleIds[type_];
128
129 if (type_ == INSTALL_PROMPT) {
130 if (extension_->is_app())
131 resource_id = IDS_EXTENSION_INSTALL_APP_PROMPT_TITLE;
132 else if (extension_->is_theme())
133 resource_id = IDS_EXTENSION_INSTALL_THEME_PROMPT_TITLE;
134 else
135 resource_id = IDS_EXTENSION_INSTALL_EXTENSION_PROMPT_TITLE;
136 }
137
138 return l10n_util::GetStringUTF16(resource_id);
139 }
140
141 string16 ExtensionInstallUI::Prompt::GetHeading() const {
142 if (type_ == INLINE_INSTALL_PROMPT) {
143 return UTF8ToUTF16(extension_->name());
144 } else if (type_ == BUNDLE_INSTALL_PROMPT) {
145 return bundle_->GetHeadingTextFor(BundleInstaller::Item::STATE_PENDING);
146 } else {
147 return l10n_util::GetStringFUTF16(
148 kHeadingIds[type_], UTF8ToUTF16(extension_->name()));
149 }
150 }
151
152 string16 ExtensionInstallUI::Prompt::GetAcceptButtonLabel() const {
153 return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]);
154 }
155
156 bool ExtensionInstallUI::Prompt::HasAbortButtonLabel() const {
157 return kAbortButtonIds[type_] > 0;
158 }
159
160 string16 ExtensionInstallUI::Prompt::GetAbortButtonLabel() const {
161 CHECK(HasAbortButtonLabel());
162 return l10n_util::GetStringUTF16(kAbortButtonIds[type_]);
163 }
164
165 string16 ExtensionInstallUI::Prompt::GetPermissionsHeading() const {
166 return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]);
167 }
168
169 void ExtensionInstallUI::Prompt::AppendRatingStars(
170 StarAppender appender, void* data) const {
171 CHECK(appender);
172 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
173 int rating_integer = floor(average_rating_);
174 double rating_fractional = average_rating_ - rating_integer;
175
176 if (rating_fractional > 0.66) {
177 rating_integer++;
178 }
179
180 if (rating_fractional < 0.33 || rating_fractional > 0.66) {
181 rating_fractional = 0;
182 }
183
184 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
185 int i;
186 for (i = 0; i < rating_integer; i++) {
187 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_ON), data);
188 }
189 if (rating_fractional) {
190 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT), data);
191 i++;
192 }
193 for (; i < kMaxExtensionRating; i++) {
194 appender(rb.GetImageSkiaNamed(IDR_EXTENSIONS_RATING_STAR_OFF), data);
195 }
196 }
197
198 string16 ExtensionInstallUI::Prompt::GetRatingCount() const {
199 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
200 return l10n_util::GetStringFUTF16(
201 IDS_EXTENSION_RATING_COUNT,
202 UTF8ToUTF16(base::IntToString(rating_count_)));
203 }
204
205 string16 ExtensionInstallUI::Prompt::GetUserCount() const {
206 CHECK_EQ(INLINE_INSTALL_PROMPT, type_);
207 return l10n_util::GetStringFUTF16(
208 IDS_EXTENSION_USER_COUNT,
209 UTF8ToUTF16(localized_user_count_));
210 }
211
212 size_t ExtensionInstallUI::Prompt::GetPermissionCount() const {
213 return permissions_.size();
214 }
215
216 string16 ExtensionInstallUI::Prompt::GetPermission(size_t index) const {
217 CHECK_LT(index, permissions_.size());
218 return l10n_util::GetStringFUTF16(
219 IDS_EXTENSION_PERMISSION_LINE, permissions_[index]);
220 }
221
222 // static
223 scoped_refptr<Extension> ExtensionInstallUI::GetLocalizedExtensionForDisplay(
224 const DictionaryValue* manifest,
225 const std::string& id,
226 const std::string& localized_name,
227 const std::string& localized_description,
228 std::string* error) {
229 scoped_ptr<DictionaryValue> localized_manifest;
230 if (!localized_name.empty() || !localized_description.empty()) {
231 localized_manifest.reset(manifest->DeepCopy());
232 if (!localized_name.empty()) {
233 localized_manifest->SetString(extension_manifest_keys::kName,
234 localized_name);
235 }
236 if (!localized_description.empty()) {
237 localized_manifest->SetString(extension_manifest_keys::kDescription,
238 localized_description);
239 }
240 }
241
242 return Extension::Create(
243 FilePath(),
244 Extension::INTERNAL,
245 localized_manifest.get() ? *localized_manifest.get() : *manifest,
246 Extension::NO_FLAGS,
247 id,
248 error);
249 }
250
251 ExtensionInstallUI::ExtensionInstallUI(Profile* profile)
252 : profile_(profile),
253 ui_loop_(MessageLoop::current()),
254 previous_using_native_theme_(false),
255 extension_(NULL),
256 delegate_(NULL),
257 prompt_(UNSET_PROMPT_TYPE),
258 prompt_type_(UNSET_PROMPT_TYPE),
259 ALLOW_THIS_IN_INITIALIZER_LIST(tracker_(this)),
260 use_app_installed_bubble_(false),
261 skip_post_install_ui_(false) {
262 // Remember the current theme in case the user presses undo.
263 if (profile_) {
264 const Extension* previous_theme =
265 ThemeServiceFactory::GetThemeForProfile(profile_);
266 if (previous_theme)
267 previous_theme_id_ = previous_theme->id();
268 previous_using_native_theme_ =
269 ThemeServiceFactory::GetForProfile(profile_)->UsingNativeTheme();
270 }
271 } 7 }
272 8
273 ExtensionInstallUI::~ExtensionInstallUI() { 9 ExtensionInstallUI::~ExtensionInstallUI() {
274 } 10 }
275
276 void ExtensionInstallUI::ConfirmBundleInstall(
277 extensions::BundleInstaller* bundle,
278 const ExtensionPermissionSet* permissions) {
279 DCHECK(ui_loop_ == MessageLoop::current());
280 bundle_ = bundle;
281 permissions_ = permissions;
282 delegate_ = bundle;
283 prompt_type_ = BUNDLE_INSTALL_PROMPT;
284
285 ShowConfirmation();
286 }
287
288 void ExtensionInstallUI::ConfirmInlineInstall(
289 Delegate* delegate,
290 const Extension* extension,
291 SkBitmap* icon,
292 const ExtensionInstallUI::Prompt& prompt) {
293 DCHECK(ui_loop_ == MessageLoop::current());
294 extension_ = extension;
295 permissions_ = extension->GetActivePermissions();
296 delegate_ = delegate;
297 prompt_ = prompt;
298 prompt_type_ = INLINE_INSTALL_PROMPT;
299
300 SetIcon(icon);
301 ShowConfirmation();
302 }
303
304 void ExtensionInstallUI::ConfirmWebstoreInstall(Delegate* delegate,
305 const Extension* extension,
306 const SkBitmap* icon) {
307 // SetIcon requires |extension_| to be set. ConfirmInstall will setup the
308 // remaining fields.
309 extension_ = extension;
310 SetIcon(icon);
311 ConfirmInstall(delegate, extension);
312 }
313
314 void ExtensionInstallUI::ConfirmInstall(Delegate* delegate,
315 const Extension* extension) {
316 DCHECK(ui_loop_ == MessageLoop::current());
317 extension_ = extension;
318 permissions_ = extension->GetActivePermissions();
319 delegate_ = delegate;
320 prompt_type_ = INSTALL_PROMPT;
321
322 // We special-case themes to not show any confirm UI. Instead they are
323 // immediately installed, and then we show an infobar (see OnInstallSuccess)
324 // to allow the user to revert if they don't like it.
325 //
326 // We don't do this in the case where off-store extension installs are
327 // disabled because in that case, we don't show the dangerous download UI, so
328 // we need the UI confirmation.
329 if (extension->is_theme()) {
330 if (extension->from_webstore() ||
331 extensions::switch_utils::IsEasyOffStoreInstallEnabled()) {
332 delegate->InstallUIProceed();
333 return;
334 }
335 }
336
337 LoadImageIfNeeded();
338 }
339
340 void ExtensionInstallUI::ConfirmReEnable(Delegate* delegate,
341 const Extension* extension) {
342 DCHECK(ui_loop_ == MessageLoop::current());
343 extension_ = extension;
344 permissions_ = extension->GetActivePermissions();
345 delegate_ = delegate;
346 prompt_type_ = RE_ENABLE_PROMPT;
347
348 LoadImageIfNeeded();
349 }
350
351 void ExtensionInstallUI::ConfirmPermissions(
352 Delegate* delegate,
353 const Extension* extension,
354 const ExtensionPermissionSet* permissions) {
355 DCHECK(ui_loop_ == MessageLoop::current());
356 extension_ = extension;
357 permissions_ = permissions;
358 delegate_ = delegate;
359 prompt_type_ = PERMISSIONS_PROMPT;
360
361 LoadImageIfNeeded();
362 }
363
364 void ExtensionInstallUI::OnInstallSuccess(const Extension* extension,
365 SkBitmap* icon) {
366 if (skip_post_install_ui_)
367 return;
368
369 extension_ = extension;
370 SetIcon(icon);
371
372 if (extension->is_theme()) {
373 ShowThemeInfoBar(previous_theme_id_, previous_using_native_theme_,
374 extension, profile_);
375 return;
376 }
377
378 // Extensions aren't enabled by default in incognito so we confirm
379 // the install in a normal window.
380 Profile* profile = profile_->GetOriginalProfile();
381 Browser* browser = browser::FindOrCreateTabbedBrowser(profile);
382 if (browser->tab_count() == 0)
383 browser->AddBlankTab(true);
384 browser->window()->Show();
385
386 bool use_bubble_for_apps = false;
387
388 #if defined(TOOLKIT_VIEWS)
389 CommandLine* cmdline = CommandLine::ForCurrentProcess();
390 use_bubble_for_apps = (use_app_installed_bubble_ ||
391 cmdline->HasSwitch(switches::kAppsNewInstallBubble));
392 #endif
393
394 if (extension->is_app() && !use_bubble_for_apps) {
395 ExtensionInstallUI::OpenAppInstalledUI(browser, extension->id());
396 return;
397 }
398
399 browser::ShowExtensionInstalledBubble(extension, browser, icon_, profile);
400 }
401
402 namespace {
403
404 bool disable_failure_ui_for_tests = false;
405
406 } // namespace
407
408 void ExtensionInstallUI::OnInstallFailure(const string16& error) {
409 DCHECK(ui_loop_ == MessageLoop::current());
410 if (disable_failure_ui_for_tests || skip_post_install_ui_)
411 return;
412
413 Browser* browser = browser::FindLastActiveWithProfile(profile_);
414 browser::ShowMessageBox(browser ? browser->window()->GetNativeWindow() : NULL,
415 l10n_util::GetStringUTF16(IDS_EXTENSION_INSTALL_FAILURE_TITLE), error,
416 browser::MESSAGE_BOX_TYPE_WARNING);
417 }
418
419 void ExtensionInstallUI::SetIcon(const SkBitmap* image) {
420 if (image)
421 icon_ = *image;
422 else
423 icon_ = SkBitmap();
424 if (icon_.empty())
425 icon_ = Extension::GetDefaultIcon(extension_->is_app());
426 }
427
428 void ExtensionInstallUI::OnImageLoaded(const gfx::Image& image,
429 const std::string& extension_id,
430 int index) {
431 SetIcon(image.IsEmpty() ? NULL : image.ToSkBitmap());
432 ShowConfirmation();
433 }
434
435 // static
436 void ExtensionInstallUI::OpenAppInstalledUI(Browser* browser,
437 const std::string& app_id) {
438 if (NewTabUI::ShouldShowApps()) {
439 browser::NavigateParams params = browser->GetSingletonTabNavigateParams(
440 GURL(chrome::kChromeUINewTabURL));
441 browser::Navigate(&params);
442
443 content::NotificationService::current()->Notify(
444 chrome::NOTIFICATION_APP_INSTALLED_TO_NTP,
445 content::Source<WebContents>(params.target_contents->web_contents()),
446 content::Details<const std::string>(&app_id));
447 } else {
448 #if defined(USE_ASH)
449 ash::Shell::GetInstance()->ToggleAppList();
450
451 content::NotificationService::current()->Notify(
452 chrome::NOTIFICATION_APP_INSTALLED_TO_APPLIST,
453 content::Source<Profile>(browser->profile()),
454 content::Details<const std::string>(&app_id));
455 #else
456 NOTREACHED();
457 #endif
458 }
459 }
460
461 // static
462 void ExtensionInstallUI::DisableFailureUIForTests() {
463 disable_failure_ui_for_tests = true;
464 }
465
466 void ExtensionInstallUI::ShowThemeInfoBar(const std::string& previous_theme_id,
467 bool previous_using_native_theme,
468 const Extension* new_theme,
469 Profile* profile) {
470 if (!new_theme->is_theme())
471 return;
472
473 // Get last active tabbed browser of profile.
474 Browser* browser = browser::FindTabbedBrowser(profile, true);
475 if (!browser)
476 return;
477
478 TabContentsWrapper* tab_contents = browser->GetSelectedTabContentsWrapper();
479 if (!tab_contents)
480 return;
481 InfoBarTabHelper* infobar_helper = tab_contents->infobar_tab_helper();
482
483 // First find any previous theme preview infobars.
484 InfoBarDelegate* old_delegate = NULL;
485 for (size_t i = 0; i < infobar_helper->infobar_count(); ++i) {
486 InfoBarDelegate* delegate = infobar_helper->GetInfoBarDelegateAt(i);
487 ThemeInstalledInfoBarDelegate* theme_infobar =
488 delegate->AsThemePreviewInfobarDelegate();
489 if (theme_infobar) {
490 // If the user installed the same theme twice, ignore the second install
491 // and keep the first install info bar, so that they can easily undo to
492 // get back the previous theme.
493 if (theme_infobar->MatchesTheme(new_theme))
494 return;
495 old_delegate = delegate;
496 break;
497 }
498 }
499
500 // Then either replace that old one or add a new one.
501 InfoBarDelegate* new_delegate = GetNewThemeInstalledInfoBarDelegate(
502 tab_contents, new_theme, previous_theme_id, previous_using_native_theme);
503
504 if (old_delegate)
505 infobar_helper->ReplaceInfoBar(old_delegate, new_delegate);
506 else
507 infobar_helper->AddInfoBar(new_delegate);
508 }
509
510 void ExtensionInstallUI::LoadImageIfNeeded() {
511 // Bundle install prompts do not have an icon.
512 if (!icon_.empty()) {
513 ShowConfirmation();
514 return;
515 }
516
517 // Load the image asynchronously. For the response, check OnImageLoaded.
518 ExtensionResource image =
519 extension_->GetIconResource(ExtensionIconSet::EXTENSION_ICON_LARGE,
520 ExtensionIconSet::MATCH_BIGGER);
521 tracker_.LoadImage(extension_, image,
522 gfx::Size(kIconSize, kIconSize),
523 ImageLoadingTracker::DONT_CACHE);
524 }
525
526 void ExtensionInstallUI::ShowConfirmation() {
527 prompt_.set_type(prompt_type_);
528 prompt_.SetPermissions(permissions_->GetWarningMessages());
529
530 switch (prompt_type_) {
531 case PERMISSIONS_PROMPT:
532 case RE_ENABLE_PROMPT:
533 case INLINE_INSTALL_PROMPT:
534 case INSTALL_PROMPT: {
535 prompt_.set_extension(extension_);
536 prompt_.set_icon(gfx::Image(icon_));
537 ShowExtensionInstallDialog(profile_, delegate_, prompt_);
538 break;
539 }
540 case BUNDLE_INSTALL_PROMPT: {
541 prompt_.set_bundle(bundle_);
542 ShowExtensionInstallDialog(profile_, delegate_, prompt_);
543 break;
544 }
545 default:
546 NOTREACHED() << "Unknown message";
547 break;
548 }
549 }
550
551 InfoBarDelegate* ExtensionInstallUI::GetNewThemeInstalledInfoBarDelegate(
552 TabContentsWrapper* tab_contents,
553 const Extension* new_theme,
554 const std::string& previous_theme_id,
555 bool previous_using_native_theme) {
556 Profile* profile = tab_contents->profile();
557 return new ThemeInstalledInfoBarDelegate(
558 tab_contents->infobar_tab_helper(),
559 profile->GetExtensionService(),
560 ThemeServiceFactory::GetForProfile(profile),
561 new_theme,
562 previous_theme_id,
563 previous_using_native_theme);
564 }
OLDNEW
« no previous file with comments | « chrome/browser/extensions/extension_install_ui.h ('k') | chrome/browser/extensions/extension_install_ui_android.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698