diff options
author | mihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-01 17:33:04 +0000 |
---|---|---|
committer | mihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-01 17:33:04 +0000 |
commit | 5fdbd6f113b07f3bf866ec6c00c1734c8f378309 (patch) | |
tree | 6604d12053a481baac7a20ddc2c6d2402b3c6b75 /chrome/browser | |
parent | 2ac2cf7366841cf88be24dbdffe8f87784d9a9f1 (diff) | |
download | chromium_src-5fdbd6f113b07f3bf866ec6c00c1734c8f378309.zip chromium_src-5fdbd6f113b07f3bf866ec6c00c1734c8f378309.tar.gz chromium_src-5fdbd6f113b07f3bf866ec6c00c1734c8f378309.tar.bz2 |
Parse and show CWS data (reviews, number of users) in the inline install dialog.
Data is put into a ExtensionInstallUI::Prompt struct that is passed to
ShowExtensionInstallDialog implementations (currently only the GTK one actually
displays it). Ratings are shown as stars (resources were r99027). The dialog
also includes a CWS link for the item (this necessitated switching the GTK
implementation to a class so that the click handler callback could get at the
delegate and extension).
R=asargent@chromium.org
Review URL: http://codereview.chromium.org/7819006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99205 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
12 files changed, 379 insertions, 206 deletions
diff --git a/chrome/browser/extensions/extension_install_dialog.cc b/chrome/browser/extensions/extension_install_dialog.cc index a58e259..336c2c7 100644 --- a/chrome/browser/extensions/extension_install_dialog.cc +++ b/chrome/browser/extensions/extension_install_dialog.cc @@ -23,14 +23,21 @@ void ShowExtensionInstallDialogForManifest( const DictionaryValue* manifest, const std::string& id, const std::string& localized_name, + const std::string& localized_description, SkBitmap* icon, - ExtensionInstallUI::PromptType type, + const ExtensionInstallUI::Prompt& prompt, scoped_refptr<Extension>* dummy_extension) { scoped_ptr<DictionaryValue> localized_manifest; - if (!localized_name.empty()) { + if (!localized_name.empty() || !localized_description.empty()) { localized_manifest.reset(manifest->DeepCopy()); - localized_manifest->SetString(extension_manifest_keys::kName, - localized_name); + if (!localized_name.empty()) { + localized_manifest->SetString(extension_manifest_keys::kName, + localized_name); + } + if (!localized_description.empty()) { + localized_manifest->SetString(extension_manifest_keys::kDescription, + localized_description); + } } std::string init_errors; @@ -59,13 +66,15 @@ void ShowExtensionInstallDialogForManifest( return; } + ExtensionInstallUI::Prompt filled_out_prompt = prompt; + filled_out_prompt.permissions = + (*dummy_extension)->GetPermissionMessageStrings(); + ShowExtensionInstallDialog(profile, delegate, dummy_extension->get(), icon, - (*dummy_extension)->GetPermissionMessageStrings(), - ExtensionInstallUI::INSTALL_PROMPT); - return; + filled_out_prompt); } void SetExtensionInstallDialogForManifestAutoConfirmForTests( diff --git a/chrome/browser/extensions/extension_install_dialog.h b/chrome/browser/extensions/extension_install_dialog.h index 4cc67fc..9f00e89 100644 --- a/chrome/browser/extensions/extension_install_dialog.h +++ b/chrome/browser/extensions/extension_install_dialog.h @@ -25,22 +25,25 @@ void ShowExtensionInstallDialog(Profile* profile, ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, - const std::vector<string16>& permissions, - ExtensionInstallUI::PromptType type); + const ExtensionInstallUI::Prompt& prompt); // Wrapper around ShowExtensionInstallDialog that shows the install dialog for // a given manifest (that corresponds to an extension about to be installed with -// ID |id|). If the name in the manifest is a localized placeholder, it may be -// overidden with |localized_name| (which may be empty). The Extension instance -// that's parsed is returned via |dummy_extension|. +// ID |id|). If the name or description in the manifest is a localized +// placeholder, it may be overidden with |localized_name| or +// |localized_description| (which may be empty). The Extension instance +// that's parsed is returned via |dummy_extension|. |prompt| should be fully +// populated except for the permissions field, which will be extracted from the +// extension. void ShowExtensionInstallDialogForManifest( Profile *profile, ExtensionInstallUI::Delegate* delegate, const base::DictionaryValue* manifest, const std::string& id, const std::string& localized_name, + const std::string& localized_description, SkBitmap* icon, - ExtensionInstallUI::PromptType type, + const ExtensionInstallUI::Prompt& prompt, scoped_refptr<Extension>* dummy_extension); // For use only in tests - sets a flag that makes invocations of diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc index 44c7b5e..1393d38 100644 --- a/chrome/browser/extensions/extension_install_ui.cc +++ b/chrome/browser/extensions/extension_install_ui.cc @@ -41,18 +41,21 @@ // static const int ExtensionInstallUI::kTitleIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_INSTALL_PROMPT_TITLE, + IDS_EXTENSION_INSTALL_PROMPT_TITLE, IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE, IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE }; // static const int ExtensionInstallUI::kHeadingIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_INSTALL_PROMPT_HEADING, + IDS_EXTENSION_INSTALL_PROMPT_HEADING, IDS_EXTENSION_RE_ENABLE_PROMPT_HEADING, IDS_EXTENSION_PERMISSIONS_PROMPT_HEADING }; // static const int ExtensionInstallUI::kButtonIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_INSTALL_BUTTON, + IDS_EXTENSION_PROMPT_INSTALL_BUTTON, IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON, IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON }; @@ -60,11 +63,13 @@ const int ExtensionInstallUI::kButtonIds[NUM_PROMPT_TYPES] = { const int ExtensionInstallUI::kAbortButtonIds[NUM_PROMPT_TYPES] = { 0, 0, + 0, IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON }; // static const int ExtensionInstallUI::kWarningIds[NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, + IDS_EXTENSION_PROMPT_WILL_HAVE_ACCESS_TO, IDS_EXTENSION_PROMPT_WILL_NOW_HAVE_ACCESS_TO, IDS_EXTENSION_PROMPT_WANTS_ACCESS_TO, }; @@ -76,6 +81,12 @@ const int kIconSize = 69; } // namespace +ExtensionInstallUI::Prompt::Prompt(PromptType type) : type(type) { +} + +ExtensionInstallUI::Prompt::~Prompt() { +} + ExtensionInstallUI::ExtensionInstallUI(Profile* profile) : profile_(profile), ui_loop_(MessageLoop::current()), @@ -214,10 +225,10 @@ void ExtensionInstallUI::OnImageLoaded( Source<ExtensionInstallUI>(this), NotificationService::NoDetails()); - std::vector<string16> warnings = - permissions_->GetWarningMessages(); + Prompt prompt(prompt_type_); + prompt.permissions = permissions_->GetWarningMessages(); ShowExtensionInstallDialog( - profile_, delegate_, extension_, &icon_, warnings, prompt_type_); + profile_, delegate_, extension_, &icon_, prompt); break; } default: diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h index e73e756..4cc10c2 100644 --- a/chrome/browser/extensions/extension_install_ui.h +++ b/chrome/browser/extensions/extension_install_ui.h @@ -30,11 +30,35 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { enum PromptType { UNSET_PROMPT_TYPE = -1, INSTALL_PROMPT = 0, + INLINE_INSTALL_PROMPT, RE_ENABLE_PROMPT, PERMISSIONS_PROMPT, NUM_PROMPT_TYPES }; + // Extra information needed to display an installation or uninstallation + // prompt. + struct Prompt { + explicit Prompt(PromptType type); + ~Prompt(); + + PromptType type; + // Permissions that are being requested (may not be all of an extension's + // permissions if only additional ones are being requested) + std::vector<string16> permissions; + + // These fields are populated only when the prompt type is + // INLINE_INSTALL_PROMPT + // Already formatted to be locale-specific. + std::string localized_user_count; + // Range is kMinExtensionRating to kMaxExtensionRating + double average_rating; + int rating_count; + }; + + static const int kMinExtensionRating = 0; + static const int kMaxExtensionRating = 5; + // A mapping from PromptType to message ID for various dialog content. static const int kTitleIds[NUM_PROMPT_TYPES]; static const int kHeadingIds[NUM_PROMPT_TYPES]; diff --git a/chrome/browser/extensions/extension_webstore_private_api.cc b/chrome/browser/extensions/extension_webstore_private_api.cc index aacc504..5c810a9 100644 --- a/chrome/browser/extensions/extension_webstore_private_api.cc +++ b/chrome/browser/extensions/extension_webstore_private_api.cc @@ -256,14 +256,17 @@ void BeginInstallWithManifestFunction::OnWebstoreParseSuccess( icon_ = icon; parsed_manifest_.reset(parsed_manifest); + ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INSTALL_PROMPT); + ShowExtensionInstallDialogForManifest( profile(), this, parsed_manifest, id_, localized_name_, + "", // no localized description &icon_, - ExtensionInstallUI::INSTALL_PROMPT, + prompt, &dummy_extension_); if (!dummy_extension_.get()) { OnWebstoreParseFailure(WebstoreInstallHelper::Delegate::MANIFEST_ERROR, diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc index f13c94f..ffa8158 100644 --- a/chrome/browser/extensions/webstore_inline_installer.cc +++ b/chrome/browser/extensions/webstore_inline_installer.cc @@ -24,6 +24,10 @@ const char kManifestKey[] = "manifest"; const char kIconUrlKey[] = "icon_url"; const char kLocalizedNameKey[] = "localized_name"; +const char kLocalizedDescriptionKey[] = "localized_description"; +const char kUsersKey[] = "users"; +const char kAverageRatingKey[] = "average_rating"; +const char kRatingCountKey[] = "rating_count"; const char kInvalidWebstoreItemId[] = "Invalid webstore item ID"; const char kWebstoreRequestError[] = "Could not fetch data from webstore"; @@ -181,9 +185,26 @@ void WebstoreInlineInstaller::OnWebstoreResponseParseSuccess( return; } - // Localized name is optional. - if (webstore_data->HasKey(kLocalizedNameKey) && - !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) { + // Number of users, average rating and rating count are required. + if (!webstore_data->GetString(kUsersKey, &localized_user_count_) || + !webstore_data->GetDouble(kAverageRatingKey, &average_rating_) || + !webstore_data->GetInteger(kRatingCountKey, &rating_count_)) { + CompleteInstall(kInvalidWebstoreResponseError); + return; + } + + if (average_rating_ < ExtensionInstallUI::kMinExtensionRating || + average_rating_ >ExtensionInstallUI::kMaxExtensionRating) { + CompleteInstall(kInvalidWebstoreResponseError); + return; + } + + // Localized name and description are optional. + if ((webstore_data->HasKey(kLocalizedNameKey) && + !webstore_data->GetString(kLocalizedNameKey, &localized_name_)) || + (webstore_data->HasKey(kLocalizedDescriptionKey) && + !webstore_data->GetString( + kLocalizedDescriptionKey, &localized_description_))) { CompleteInstall(kInvalidWebstoreResponseError); return; } @@ -229,17 +250,23 @@ void WebstoreInlineInstaller::OnWebstoreParseSuccess( Profile* profile = Profile::FromBrowserContext( tab_contents_->browser_context()); - scoped_refptr<Extension> dummy_extension; + + ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INLINE_INSTALL_PROMPT); + prompt.localized_user_count = localized_user_count_; + prompt.average_rating = average_rating_; + prompt.rating_count = rating_count_; + ShowExtensionInstallDialogForManifest(profile, this, manifest, id_, localized_name_, + localized_description_, &icon_, - ExtensionInstallUI::INSTALL_PROMPT, - &dummy_extension); + prompt, + &dummy_extension_); - if (!dummy_extension.get()) { + if (!dummy_extension_.get()) { CompleteInstall(kInvalidManifestError); return; } diff --git a/chrome/browser/extensions/webstore_inline_installer.h b/chrome/browser/extensions/webstore_inline_installer.h index 8d0606c..d63eb0b 100644 --- a/chrome/browser/extensions/webstore_inline_installer.h +++ b/chrome/browser/extensions/webstore_inline_installer.h @@ -90,8 +90,13 @@ class WebstoreInlineInstaller // Extracted from the webstore JSON data response. std::string localized_name_; + std::string localized_description_; + std::string localized_user_count_; + double average_rating_; + int rating_count_; scoped_ptr<DictionaryValue> webstore_data_; scoped_ptr<DictionaryValue> manifest_; + scoped_refptr<Extension> dummy_extension_; SkBitmap icon_; DISALLOW_IMPLICIT_CONSTRUCTORS(WebstoreInlineInstaller); diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h index 2617a53..49aaaef 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.h @@ -55,8 +55,7 @@ class Profile; extension:(const Extension*)extension delegate:(ExtensionInstallUI::Delegate*)delegate icon:(SkBitmap*)bitmap - warnings:(const std::vector<string16>&)warnings - type:(ExtensionInstallUI::PromptType)type; + prompt:(const ExtensionInstallUI::Prompt&)prompt; - (void)runAsModalSheet; - (IBAction)cancel:(id)sender; - (IBAction)ok:(id)sender; diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm index 0e79076f..5877481 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm @@ -62,13 +62,12 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { extension:(const Extension*)extension delegate:(ExtensionInstallUI::Delegate*)delegate icon:(SkBitmap*)icon - warnings:(const std::vector<string16>&)warnings - type:(ExtensionInstallUI::PromptType)type { + prompt:(const ExtensionInstallUI::Prompt&)prompt { NSString* nibpath = nil; - // We use a different XIB in the case of no warnings, that is a little bit - // more nicely laid out. - if (warnings.empty()) { + // We use a different XIB in the case of no permission warnings, that is a + // little bit more nicely laid out. + if (prompt.permissions.empty()) { nibpath = [base::mac::MainAppBundle() pathForResource:@"ExtensionInstallPromptNoWarnings" ofType:@"nib"]; @@ -85,25 +84,26 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { delegate_ = delegate; title_.reset( - [l10n_util::GetNSStringF(ExtensionInstallUI::kHeadingIds[type], + [l10n_util::GetNSStringF(ExtensionInstallUI::kHeadingIds[prompt.type], UTF8ToUTF16(extension->name())) retain]); subtitle_.reset( - [l10n_util::GetNSString(ExtensionInstallUI::kWarningIds[type]) + [l10n_util::GetNSString(ExtensionInstallUI::kWarningIds[prompt.type]) retain]); - button_.reset([l10n_util::GetNSString(ExtensionInstallUI::kButtonIds[type]) - retain]); - int cancel_id = ExtensionInstallUI::kAbortButtonIds[type]; + button_.reset([l10n_util::GetNSString( + ExtensionInstallUI::kButtonIds[prompt.type]) retain]); + int cancel_id = ExtensionInstallUI::kAbortButtonIds[prompt.type]; cancel_button_.reset([l10n_util::GetNSString( cancel_id > 0 ? cancel_id : IDS_CANCEL) retain]); - // We display the warnings as a simple text string, separated by newlines. - if (!warnings.empty()) { + // We display the permission warnings as a simple text string, separated by + // newlines. + if (!prompt.permissions.empty()) { string16 joined_warnings; - for (size_t i = 0; i < warnings.size(); ++i) { + for (size_t i = 0; i < prompt.permissions.size(); ++i) { if (i > 0) joined_warnings += UTF8ToUTF16("\n\n"); - joined_warnings += warnings[i]; + joined_warnings += prompt.permissions[i]; } warnings_.reset( @@ -211,8 +211,7 @@ void ShowExtensionInstallDialog( ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, - const std::vector<string16>& warnings, - ExtensionInstallUI::PromptType type) { + const ExtensionInstallUI::Prompt& prompt) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) { delegate->InstallUIAbort(false); @@ -234,8 +233,7 @@ void ShowExtensionInstallDialog( extension:extension delegate:delegate icon:icon - warnings:warnings - type:type]; + prompt:prompt]; [controller runAsModalSheet]; } diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_unittest.mm index f54ec2b..4a175b0 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_unittest.mm @@ -107,8 +107,8 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalCancel) { scoped_ptr<MockExtensionInstallUIDelegate> delegate( new MockExtensionInstallUIDelegate); - std::vector<string16> warnings; - warnings.push_back(UTF8ToUTF16("warning 1")); + ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INSTALL_PROMPT); + prompt.permissions.push_back(UTF8ToUTF16("warning 1")); scoped_nsobject<ExtensionInstallDialogController> controller([[ExtensionInstallDialogController alloc] @@ -117,8 +117,7 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalCancel) { extension:extension_.get() delegate:delegate.get() icon:&icon_ - warnings:warnings - type:ExtensionInstallUI::INSTALL_PROMPT]); + prompt:prompt]); [controller window]; // force nib load @@ -141,7 +140,7 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalCancel) { EXPECT_TRUE([controller warningsField] != nil); EXPECT_NSEQ([[controller warningsField] stringValue], - base::SysUTF16ToNSString(warnings[0])); + base::SysUTF16ToNSString(prompt.permissions[0])); EXPECT_TRUE([controller warningsBox] != nil); @@ -164,18 +163,17 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalOK) { scoped_ptr<MockExtensionInstallUIDelegate> delegate( new MockExtensionInstallUIDelegate); - std::vector<string16> warnings; - warnings.push_back(UTF8ToUTF16("warning 1")); + ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INSTALL_PROMPT); + prompt.permissions.push_back(UTF8ToUTF16("warning 1")); scoped_nsobject<ExtensionInstallDialogController> controller([[ExtensionInstallDialogController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - extension:extension_.get() - delegate:delegate.get() - icon:&icon_ - warnings:warnings - type:ExtensionInstallUI::INSTALL_PROMPT]); + initWithParentWindow:test_window() + profile:helper_.profile() + extension:extension_.get() + delegate:delegate.get() + icon:&icon_ + prompt:prompt]); [controller window]; // force nib load [controller ok:nil]; @@ -192,34 +190,34 @@ TEST_F(ExtensionInstallDialogControllerTest, MultipleWarnings) { scoped_ptr<MockExtensionInstallUIDelegate> delegate2( new MockExtensionInstallUIDelegate); - std::vector<string16> one_warning; - one_warning.push_back(UTF8ToUTF16("warning 1")); + ExtensionInstallUI::Prompt one_warning_prompt( + ExtensionInstallUI::INSTALL_PROMPT); + one_warning_prompt.permissions.push_back(UTF8ToUTF16("warning 1")); - std::vector<string16> two_warnings; - two_warnings.push_back(UTF8ToUTF16("warning 1")); - two_warnings.push_back(UTF8ToUTF16("warning 2")); + ExtensionInstallUI::Prompt two_warnings_prompt( + ExtensionInstallUI::INSTALL_PROMPT); + two_warnings_prompt.permissions.push_back(UTF8ToUTF16("warning 1")); + two_warnings_prompt.permissions.push_back(UTF8ToUTF16("warning 2")); scoped_nsobject<ExtensionInstallDialogController> controller1([[ExtensionInstallDialogController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - extension:extension_.get() - delegate:delegate1.get() - icon:&icon_ - warnings:one_warning - type:ExtensionInstallUI::INSTALL_PROMPT]); + initWithParentWindow:test_window() + profile:helper_.profile() + extension:extension_.get() + delegate:delegate1.get() + icon:&icon_ + prompt:one_warning_prompt]); [controller1 window]; // force nib load scoped_nsobject<ExtensionInstallDialogController> controller2([[ExtensionInstallDialogController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - extension:extension_.get() - delegate:delegate2.get() - icon:&icon_ - warnings:two_warnings - type:ExtensionInstallUI::INSTALL_PROMPT]); + initWithParentWindow:test_window() + profile:helper_.profile() + extension:extension_.get() + delegate:delegate2.get() + icon:&icon_ + prompt:two_warnings_prompt]); [controller2 window]; // force nib load @@ -252,17 +250,19 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsSkinny) { new MockExtensionInstallUIDelegate); // No warnings should trigger skinny prompt. + ExtensionInstallUI::Prompt no_warnings_prompt( + ExtensionInstallUI::INSTALL_PROMPT); + std::vector<string16> warnings; scoped_nsobject<ExtensionInstallDialogController> controller([[ExtensionInstallDialogController alloc] - initWithParentWindow:test_window() - profile:helper_.profile() - extension:extension_.get() - delegate:delegate.get() - icon:&icon_ - warnings:warnings - type:ExtensionInstallUI::INSTALL_PROMPT]); + initWithParentWindow:test_window() + profile:helper_.profile() + extension:extension_.get() + delegate:delegate.get() + icon:&icon_ + prompt:no_warnings_prompt]); [controller window]; // force nib load diff --git a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc index c9a5806..c553231 100644 --- a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc +++ b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc @@ -4,6 +4,7 @@ #include <gtk/gtk.h> +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/browser/extensions/extension_install_dialog.h" @@ -11,75 +12,183 @@ #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/gtk/browser_window_gtk.h" +#include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" #include "chrome/browser/ui/gtk/gtk_util.h" #include "chrome/common/extensions/extension.h" #include "grit/generated_resources.h" +#include "grit/theme_resources.h" #include "skia/ext/image_operations.h" #include "ui/base/gtk/gtk_hig_constants.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/gfx/gtk_util.h" namespace { -const int kRightColumnMinWidth = 290; +const int kLeftColumnMinWidth = 250; const int kImageSize = 69; -// Padding on all sides of each permission in the permissions list. -const int kPermissionsPadding = 8; +// Additional padding (beyond on ui::kControlSpacing) all sides of each +// permission in the permissions list. +const int kPermissionsPadding = 2; -// Make a GtkLabel with |str| as its text, using the formatting in |format|. -GtkWidget* MakeMarkupLabel(const char* format, const std::string& str) { - GtkWidget* label = gtk_label_new(NULL); - char* markup = g_markup_printf_escaped(format, str.c_str()); - gtk_label_set_markup(GTK_LABEL(label), markup); - g_free(markup); +const double kRatingTextSize = 12.1; // 12.1px = 9pt @ 96dpi - // Left align it. - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - - return label; +// Loads an image and adds it as an icon control to the given container. +GtkWidget* AddResourceIcon(int resource_id, GtkWidget* container) { + const SkBitmap* icon = ResourceBundle::GetSharedInstance().GetBitmapNamed( + resource_id); + GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(icon); + GtkWidget* icon_widget = gtk_image_new_from_pixbuf(icon_pixbuf); + g_object_unref(icon_pixbuf); + gtk_box_pack_start(GTK_BOX(container), icon_widget, FALSE, FALSE, 0); + return icon_widget; } -void OnResponse(GtkWidget* dialog, int response_id, - ExtensionInstallUI::Delegate* delegate) { - if (response_id == GTK_RESPONSE_ACCEPT) { - delegate->InstallUIProceed(); - } else { - delegate->InstallUIAbort(true); +// Adds star icons corresponding to a rating. Replicates the CWS stars display +// logic (from components.ratingutils.setFractionalYellowStars). +void AddRatingStars(double average_rating, GtkWidget* container) { + int rating_integer = floor(average_rating); + double rating_fractional = average_rating - rating_integer; + + if (rating_fractional > 0.66) { + rating_integer++; + } + + if (rating_fractional < 0.33 || rating_fractional > 0.66) { + rating_fractional = 0; } - gtk_widget_destroy(dialog); + int i = 0; + while (i++ < rating_integer) { + AddResourceIcon(IDR_EXTENSIONS_RATING_STAR_ON, container); + } + if (rating_fractional) { + AddResourceIcon(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT, container); + i++; + } + while (i++ < ExtensionInstallUI::kMaxExtensionRating) { + AddResourceIcon(IDR_EXTENSIONS_RATING_STAR_OFF, container); + } } -void ShowInstallDialog(GtkWindow* parent, - SkBitmap* skia_icon, - const Extension* extension, - ExtensionInstallUI::Delegate *delegate, - const std::vector<string16>& permissions, - ExtensionInstallUI::PromptType type) { +// Displays the dialog when constructed, deletes itself when dialog is +// dismissed. Success/failure is passed back through the ExtensionInstallUI:: +// Delegate instance. +class ExtensionInstallDialog { + public: + ExtensionInstallDialog(GtkWindow* parent, + ExtensionInstallUI::Delegate *delegate, + const Extension* extension, + SkBitmap* skia_icon, + const ExtensionInstallUI::Prompt& prompt); + private: + virtual ~ExtensionInstallDialog(); + + CHROMEGTK_CALLBACK_1(ExtensionInstallDialog, void, OnResponse, int); + CHROMEGTK_CALLBACK_0(ExtensionInstallDialog, void, OnStoreLinkClick); + + ExtensionInstallUI::Delegate* delegate_; + const Extension* extension_; + GtkWidget* dialog_; +}; + +ExtensionInstallDialog::ExtensionInstallDialog( + GtkWindow* parent, + ExtensionInstallUI::Delegate *delegate, + const Extension* extension, + SkBitmap* skia_icon, + const ExtensionInstallUI::Prompt& prompt) + : delegate_(delegate), + extension_(extension) { + ExtensionInstallUI::PromptType type = prompt.type; + const std::vector<string16>& permissions = prompt.permissions; + bool show_permissions = !permissions.empty(); + bool is_inline_install = type == ExtensionInstallUI::INLINE_INSTALL_PROMPT; + // Build the dialog. - GtkWidget* dialog = gtk_dialog_new_with_buttons( + dialog_ = gtk_dialog_new_with_buttons( l10n_util::GetStringUTF8(ExtensionInstallUI::kTitleIds[type]).c_str(), parent, GTK_DIALOG_MODAL, NULL); int cancel = ExtensionInstallUI::kAbortButtonIds[type]; GtkWidget* close_button = gtk_dialog_add_button( - GTK_DIALOG(dialog), + GTK_DIALOG(dialog_), cancel > 0 ? l10n_util::GetStringUTF8(cancel).c_str(): GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE); gtk_dialog_add_button( - GTK_DIALOG(dialog), + GTK_DIALOG(dialog_), l10n_util::GetStringUTF8(ExtensionInstallUI::kButtonIds[type]).c_str(), GTK_RESPONSE_ACCEPT); - gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE); + gtk_dialog_set_has_separator(GTK_DIALOG(dialog_), FALSE); - // Create a two column layout. - GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + GtkWidget* content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog_)); gtk_box_set_spacing(GTK_BOX(content_area), ui::kContentAreaSpacing); - GtkWidget* icon_hbox = gtk_hbox_new(FALSE, ui::kContentAreaSpacing); - gtk_box_pack_start(GTK_BOX(content_area), icon_hbox, TRUE, TRUE, 0); + // Divide the dialog vertically (item data and icon on the top, permissions + // on the bottom). + GtkWidget* content_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing); + gtk_box_pack_start(GTK_BOX(content_area), content_vbox, TRUE, TRUE, 0); + + // Create a two column layout for the top (item data on the left, icon on + // the right). + GtkWidget* top_content_hbox = gtk_hbox_new(FALSE, ui::kContentAreaSpacing); + gtk_box_pack_start(GTK_BOX(content_vbox), top_content_hbox, TRUE, TRUE, 0); + + // Create a new vbox for the left column. + GtkWidget* left_column_area = gtk_vbox_new(FALSE, ui::kControlSpacing); + gtk_box_pack_start(GTK_BOX(top_content_hbox), left_column_area, + TRUE, TRUE, 0); + + GtkWidget* heading_vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(left_column_area), heading_vbox, FALSE, FALSE, 0); + + // Heading (name-only for inline installs) + std::string heading_text = is_inline_install ? extension_->name() : + l10n_util::GetStringFUTF8(ExtensionInstallUI::kHeadingIds[type], + UTF8ToUTF16(extension_->name())); + GtkWidget* heading_label = gtk_util::CreateBoldLabel(heading_text); + gtk_label_set_line_wrap(GTK_LABEL(heading_label), true); + gtk_misc_set_alignment(GTK_MISC(heading_label), 0.0, 0.5); + // If we are not going to show anything else, vertically center the title. + bool center_heading = !show_permissions && !is_inline_install; + gtk_box_pack_start(GTK_BOX(heading_vbox), heading_label, center_heading, + center_heading, 0); + + if (is_inline_install) { + // Average rating (as stars) and number of ratings + GtkWidget* stars_hbox = gtk_hbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX(heading_vbox), stars_hbox, FALSE, FALSE, 0); + AddRatingStars(prompt.average_rating, stars_hbox); + GtkWidget* rating_label = gtk_label_new(l10n_util::GetStringFUTF8( + IDS_EXTENSION_RATING_COUNT, + UTF8ToUTF16(base::IntToString(prompt.rating_count))).c_str()); + gtk_util::ForceFontSizePixels(rating_label, kRatingTextSize); + gtk_box_pack_start(GTK_BOX(stars_hbox), rating_label, + FALSE, FALSE, 3); + + // User count + GtkWidget* users_label = gtk_label_new(l10n_util::GetStringFUTF8( + IDS_EXTENSION_USER_COUNT, + UTF8ToUTF16(prompt.localized_user_count)).c_str()); + gtk_util::SetLabelWidth(users_label, kLeftColumnMinWidth); + gtk_util::SetLabelColor(users_label, &ui::kGdkGray); + gtk_util::ForceFontSizePixels(rating_label, kRatingTextSize); + gtk_box_pack_start(GTK_BOX(heading_vbox), users_label, + FALSE, FALSE, 0); + + // Store link + GtkWidget* store_link = gtk_chrome_link_button_new( + l10n_util::GetStringUTF8(IDS_EXTENSION_PROMPT_STORE_LINK).c_str()); + gtk_util::ForceFontSizePixels(store_link, kRatingTextSize); + GtkWidget* store_link_hbox = gtk_hbox_new(FALSE, 0); + // Stick it in an hbox so it doesn't expand to the whole width. + gtk_box_pack_start(GTK_BOX(store_link_hbox), store_link, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(heading_vbox), store_link_hbox, FALSE, FALSE, 0); + g_signal_connect(store_link, "clicked", + G_CALLBACK(OnStoreLinkClickThunk), this); + } // Resize the icon if necessary. SkBitmap scaled_icon = *skia_icon; @@ -89,88 +198,75 @@ void ShowInstallDialog(GtkWindow* parent, kImageSize, kImageSize); } - // Put Icon in the left column. + // Put icon in the right column. GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&scaled_icon); GtkWidget* icon = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); - gtk_box_pack_start(GTK_BOX(icon_hbox), icon, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(top_content_hbox), icon, FALSE, FALSE, 0); // Top justify the image. gtk_misc_set_alignment(GTK_MISC(icon), 0.5, 0.0); - // Create a new vbox for the right column. - GtkWidget* right_column_area = gtk_vbox_new(FALSE, ui::kControlSpacing); - gtk_box_pack_start(GTK_BOX(icon_hbox), right_column_area, TRUE, TRUE, 0); - - std::string heading_text = l10n_util::GetStringFUTF8( - ExtensionInstallUI::kHeadingIds[type], UTF8ToUTF16(extension->name())); - GtkWidget* heading_label = MakeMarkupLabel("<span weight=\"bold\">%s</span>", - heading_text); - gtk_label_set_line_wrap(GTK_LABEL(heading_label), true); - gtk_misc_set_alignment(GTK_MISC(heading_label), 0.0, 0.5); - bool show_permissions = !permissions.empty(); - // If we are not going to show the permissions, vertically center the title. - gtk_box_pack_start(GTK_BOX(right_column_area), heading_label, - !show_permissions, !show_permissions, 0); - + // Permissions are shown separated by a divider for inline installs, or + // directly under the heading for regular installs (where we don't have + // the store data) if (show_permissions) { - GtkWidget* warning_label = gtk_label_new(l10n_util::GetStringUTF8( - ExtensionInstallUI::kWarningIds[type]).c_str()); - gtk_util::SetLabelWidth(warning_label, kRightColumnMinWidth); + GtkWidget* permissions_container; + if (is_inline_install) { + permissions_container = content_vbox; + gtk_box_pack_start(GTK_BOX(content_vbox), gtk_hseparator_new(), + FALSE, FALSE, ui::kControlSpacing); + } else { + permissions_container = left_column_area; + } - gtk_box_pack_start(GTK_BOX(right_column_area), warning_label, - FALSE, FALSE, 0); + GtkWidget* warning_label = gtk_util::CreateBoldLabel( + l10n_util::GetStringUTF8( + ExtensionInstallUI::kWarningIds[type]).c_str()); + gtk_util::SetLabelWidth(warning_label, kLeftColumnMinWidth); + gtk_box_pack_start(GTK_BOX(permissions_container), warning_label, FALSE, + FALSE, is_inline_install ? 0 : ui::kControlSpacing); - GtkWidget* frame = gtk_frame_new(NULL); - gtk_box_pack_start(GTK_BOX(right_column_area), frame, FALSE, FALSE, 0); - - GtkWidget* text_view = gtk_text_view_new(); - gtk_container_add(GTK_CONTAINER(frame), text_view); - gtk_text_view_set_editable(GTK_TEXT_VIEW(text_view), FALSE); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_view), - kPermissionsPadding); - gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text_view), - kPermissionsPadding); - gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text_view), GTK_WRAP_WORD); - GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_view)); - GtkTextTagTable* tag_table = gtk_text_buffer_get_tag_table(buffer); - - GtkTextTag* padding_below_tag = gtk_text_tag_new(NULL); - g_object_set(G_OBJECT(padding_below_tag), "pixels-below-lines", - kPermissionsPadding, NULL); - g_object_set(G_OBJECT(padding_below_tag), "pixels-below-lines-set", - TRUE, NULL); - gtk_text_tag_table_add(tag_table, padding_below_tag); - g_object_unref(padding_below_tag); - GtkTextTag* padding_above_tag = gtk_text_tag_new(NULL); - g_object_set(G_OBJECT(padding_above_tag), "pixels-above-lines", - kPermissionsPadding, NULL); - g_object_set(G_OBJECT(padding_above_tag), "pixels-above-lines-set", - TRUE, NULL); - gtk_text_tag_table_add(tag_table, padding_above_tag); - g_object_unref(padding_above_tag); - - GtkTextIter end_iter; - gtk_text_buffer_get_end_iter(buffer, &end_iter); for (std::vector<string16>::const_iterator iter = permissions.begin(); iter != permissions.end(); ++iter) { - if (iter != permissions.begin()) - gtk_text_buffer_insert(buffer, &end_iter, "\n", -1); - gtk_text_buffer_insert_with_tags( - buffer, &end_iter, UTF16ToUTF8(*iter).c_str(), -1, - padding_below_tag, - iter == permissions.begin() ? padding_above_tag : NULL, - NULL); + GtkWidget* permission_label = gtk_label_new(l10n_util::GetStringFUTF8( + IDS_EXTENSION_PERMISSION_LINE, *iter).c_str()); + gtk_util::SetLabelWidth(permission_label, kLeftColumnMinWidth); + gtk_box_pack_start(GTK_BOX(permissions_container), permission_label, + FALSE, FALSE, kPermissionsPadding); } } - g_signal_connect(dialog, "response", G_CALLBACK(OnResponse), delegate); - gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); + gtk_window_set_resizable(GTK_WINDOW(dialog_), FALSE); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE); - gtk_widget_show_all(dialog); + gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_CLOSE); + gtk_widget_show_all(dialog_); gtk_widget_grab_focus(close_button); } +ExtensionInstallDialog::~ExtensionInstallDialog() { +} + +void ExtensionInstallDialog::OnResponse(GtkWidget* dialog, int response_id) { + if (response_id == GTK_RESPONSE_ACCEPT) { + delegate_->InstallUIProceed(); + } else { + delegate_->InstallUIAbort(true); + } + + gtk_widget_destroy(dialog_); + delete this; +} + +void ExtensionInstallDialog::OnStoreLinkClick(GtkWidget* sender) { + GURL store_url( + extension_urls::GetWebstoreItemDetailURLPrefix() + extension_->id()); + BrowserList::GetLastActive()-> + OpenURL(store_url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); + + OnResponse(dialog_, GTK_RESPONSE_CLOSE); +} + } // namespace void ShowExtensionInstallDialog( @@ -178,8 +274,7 @@ void ShowExtensionInstallDialog( ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, - const std::vector<string16>& permissions, - ExtensionInstallUI::PromptType type) { + const ExtensionInstallUI::Prompt& prompt) { Browser* browser = BrowserList::GetLastActiveWithProfile(profile); if (!browser) { delegate->InstallUIAbort(false); @@ -193,6 +288,9 @@ void ShowExtensionInstallDialog( return; } - ShowInstallDialog( - browser_window->window(), icon, extension, delegate, permissions, type); + new ExtensionInstallDialog(browser_window->window(), + delegate, + extension, + icon, + prompt); } diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc index 36323de..268c589 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc @@ -66,8 +66,7 @@ class ExtensionInstallDialogView : public views::DialogDelegateView { ExtensionInstallDialogView(ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, - const std::vector<string16>& permissions, - ExtensionInstallUI::PromptType type); + const ExtensionInstallUI::Prompt& prompt); virtual ~ExtensionInstallDialogView(); private: @@ -112,9 +111,7 @@ class ExtensionInstallDialogView : public views::DialogDelegateView { // whether the extension requires any permissions. int right_column_width_; - // The type of install dialog, which must be INSTALL_PROMPT or - // RE_ENABLE_PROMPT. - ExtensionInstallUI::PromptType type_; + ExtensionInstallUI::Prompt prompt_; DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogView); }; @@ -123,15 +120,14 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, - const std::vector<string16>& permissions, - ExtensionInstallUI::PromptType type) + const ExtensionInstallUI::Prompt& prompt) : delegate_(delegate), icon_(NULL), heading_(NULL), will_have_access_to_(NULL), permission_box_(NULL), right_column_width_(0), - type_(type) { + prompt_(prompt) { // Scale down to icon size, but allow smaller icons (don't scale up). gfx::Size size(icon->width(), icon->height()); if (size.width() > kIconSize || size.height() > kIconSize) @@ -144,7 +140,7 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( AddChildView(icon_); heading_ = new views::Label(UTF16ToWide( - l10n_util::GetStringFUTF16(ExtensionInstallUI::kHeadingIds[type_], + l10n_util::GetStringFUTF16(ExtensionInstallUI::kHeadingIds[prompt.type], UTF8ToUTF16(extension->name())))); heading_->SetFont(heading_->font().DeriveFont(kHeadingFontSizeDelta, gfx::Font::BOLD)); @@ -152,12 +148,13 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); AddChildView(heading_); - if (permissions.empty()) { + if (prompt.permissions.empty()) { right_column_width_ = kNoPermissionsRightColumnWidth; } else { right_column_width_ = kPermissionBoxWidth; will_have_access_to_ = new views::Label(UTF16ToWide( - l10n_util::GetStringUTF16(ExtensionInstallUI::kWarningIds[type_]))); + l10n_util::GetStringUTF16( + ExtensionInstallUI::kWarningIds[prompt.type]))); will_have_access_to_->SetMultiLine(true); will_have_access_to_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); AddChildView(will_have_access_to_); @@ -171,8 +168,8 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( AddChildView(permission_box_); } - for (size_t i = 0; i < permissions.size(); ++i) { - views::Label* label = new views::Label(UTF16ToWide(permissions[i])); + for (size_t i = 0; i < prompt.permissions.size(); ++i) { + views::Label* label = new views::Label(UTF16ToWide(prompt.permissions[i])); label->SetMultiLine(true); label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); permission_box_->AddChildView(label); @@ -273,10 +270,10 @@ std::wstring ExtensionInstallDialogView::GetDialogButtonLabel( MessageBoxFlags::DialogButton button) const { switch (button) { case MessageBoxFlags::DIALOGBUTTON_OK: - return UTF16ToWide( - l10n_util::GetStringUTF16(ExtensionInstallUI::kButtonIds[type_])); + return UTF16ToWide(l10n_util::GetStringUTF16( + ExtensionInstallUI::kButtonIds[prompt_.type])); case MessageBoxFlags::DIALOGBUTTON_CANCEL: { - int id = ExtensionInstallUI::kAbortButtonIds[type_]; + int id = ExtensionInstallUI::kAbortButtonIds[prompt_.type]; return UTF16ToWide(l10n_util::GetStringUTF16(id > 0 ? id : IDS_CANCEL)); } default: @@ -305,7 +302,7 @@ bool ExtensionInstallDialogView::IsModal() const { std::wstring ExtensionInstallDialogView::GetWindowTitle() const { return UTF16ToWide( - l10n_util::GetStringUTF16(ExtensionInstallUI::kTitleIds[type_])); + l10n_util::GetStringUTF16(ExtensionInstallUI::kTitleIds[prompt_.type])); } views::View* ExtensionInstallDialogView::GetContentsView() { @@ -317,8 +314,7 @@ void ShowExtensionInstallDialog( ExtensionInstallUI::Delegate* delegate, const Extension* extension, SkBitmap* icon, - const std::vector<string16>& permissions, - ExtensionInstallUI::PromptType type) { + const ExtensionInstallUI::Prompt& prompt) { #if defined(OS_CHROMEOS) // Use a tabbed browser window as parent on ChromeOS. Browser* browser = BrowserList::FindTabbedBrowser(profile, true); @@ -337,7 +333,7 @@ void ShowExtensionInstallDialog( } ExtensionInstallDialogView* dialog = new ExtensionInstallDialogView( - delegate, extension, icon, permissions, type); + delegate, extension, icon, prompt); views::Widget* window = browser::CreateViewsWindow( browser_window->GetNativeHandle(), dialog); |