diff options
author | mihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-02 18:20:24 +0000 |
---|---|---|
committer | mihaip@chromium.org <mihaip@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-02 18:20:24 +0000 |
commit | 122e8dfabc4eff6875e41009a0b4195290743e33 (patch) | |
tree | 489764393824ff646c47682ba3aff2df95037055 | |
parent | 86d67adb3cc099cc16cee194a4e4457df8d79c05 (diff) | |
download | chromium_src-122e8dfabc4eff6875e41009a0b4195290743e33.zip chromium_src-122e8dfabc4eff6875e41009a0b4195290743e33.tar.gz chromium_src-122e8dfabc4eff6875e41009a0b4195290743e33.tar.bz2 |
Implement inline install UI for views.
To make this easier, the extension install dialog was changed from doing layout
by hand to using GridLayout (thus no longer necessitating overrides of
GetPreferredSize/Layout or lots of math).
To lessen the repeated logic between the inline install UIs,
ExtensionInstallUI::Prompt was beefed up to return formatted data directly
(and in the case of rating stars, include the logic for which ones to show).
Also includes some other tweaks:
- Rename inline install dialog to "Add to Chrome" and the install button in it
to "Add"
- Use the final webstore JSON data URLs
R=asargent@chromium.org
BUG=93380
TEST=no
Review URL: http://codereview.chromium.org/7826029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@99407 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 6 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_dialog.cc | 4 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_ui.cc | 127 | ||||
-rw-r--r-- | chrome/browser/extensions/extension_install_ui.h | 54 | ||||
-rw-r--r-- | chrome/browser/extensions/webstore_inline_installer.cc | 6 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm | 29 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller_unittest.mm | 18 | ||||
-rw-r--r-- | chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc | 94 | ||||
-rw-r--r-- | chrome/browser/ui/views/extensions/extension_install_dialog_view.cc | 374 | ||||
-rw-r--r-- | chrome/common/extensions/extension_constants.cc | 2 | ||||
-rw-r--r-- | chrome/test/data/extensions/api_test/webstore_inline_install/inlineinstall/detail/ecglahbcnmdpdciemllbhojghbkagdje (renamed from chrome/test/data/extensions/api_test/webstore_inline_install/detail/ecglahbcnmdpdciemllbhojghbkagdje) | 0 |
11 files changed, 407 insertions, 307 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index cc64bd4..1ccd041 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -3705,6 +3705,9 @@ are declared in build/common.gypi. <message name="IDS_EXTENSION_INSTALL_PROMPT_TITLE" desc="Titlebar of the extension or app installation prompt window"> Confirm Installation </message> + <message name="IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE" desc="Titlebar of the extension or app inline installation prompt window"> + Add to <ph name="SHORT_PRODUCT_NAME">$1<ex>Chrome</ex></ph> + </message> <message name="IDS_EXTENSION_UNINSTALL_PROMPT_TITLE" desc="Titlebar of the extension or app uninstallation prompt window"> Confirm Uninstallation </message> @@ -4108,6 +4111,9 @@ Keep your key file in a safe place. You will need it to create new versions of y <message name="IDS_EXTENSION_PROMPT_INSTALL_BUTTON" desc="Text for the install button on the extension install prompt"> Install </message> + <message name="IDS_EXTENSION_PROMPT_INLINE_INSTALL_BUTTON" desc="Text for the inline install button on the extension install prompt"> + Add + </message> <message name="IDS_EXTENSION_PROMPT_UNINSTALL_BUTTON" desc="Text for the uninstall button on the extension uninstall prompt"> Uninstall </message> diff --git a/chrome/browser/extensions/extension_install_dialog.cc b/chrome/browser/extensions/extension_install_dialog.cc index 336c2c7..813e63b 100644 --- a/chrome/browser/extensions/extension_install_dialog.cc +++ b/chrome/browser/extensions/extension_install_dialog.cc @@ -67,8 +67,8 @@ void ShowExtensionInstallDialogForManifest( } ExtensionInstallUI::Prompt filled_out_prompt = prompt; - filled_out_prompt.permissions = - (*dummy_extension)->GetPermissionMessageStrings(); + filled_out_prompt.SetPermissions( + (*dummy_extension)->GetPermissionMessageStrings()); ShowExtensionInstallDialog(profile, delegate, diff --git a/chrome/browser/extensions/extension_install_ui.cc b/chrome/browser/extensions/extension_install_ui.cc index 77fc4a4..6fd551f 100644 --- a/chrome/browser/extensions/extension_install_ui.cc +++ b/chrome/browser/extensions/extension_install_ui.cc @@ -39,36 +39,31 @@ #include "grit/theme_resources.h" #include "ui/base/l10n/l10n_util.h" -// static -const int ExtensionInstallUI::kTitleIds[NUM_PROMPT_TYPES] = { - IDS_EXTENSION_INSTALL_PROMPT_TITLE, +static const int kTitleIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = { IDS_EXTENSION_INSTALL_PROMPT_TITLE, + IDS_EXTENSION_INLINE_INSTALL_PROMPT_TITLE, IDS_EXTENSION_RE_ENABLE_PROMPT_TITLE, IDS_EXTENSION_PERMISSIONS_PROMPT_TITLE }; -// static -const int ExtensionInstallUI::kHeadingIds[NUM_PROMPT_TYPES] = { +static const int kHeadingIds[ExtensionInstallUI::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, +static const int kAcceptButtonIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = { IDS_EXTENSION_PROMPT_INSTALL_BUTTON, + IDS_EXTENSION_PROMPT_INLINE_INSTALL_BUTTON, IDS_EXTENSION_PROMPT_RE_ENABLE_BUTTON, IDS_EXTENSION_PROMPT_PERMISSIONS_BUTTON }; -// static -const int ExtensionInstallUI::kAbortButtonIds[NUM_PROMPT_TYPES] = { +static const int kAbortButtonIds[ExtensionInstallUI::NUM_PROMPT_TYPES] = { 0, 0, 0, IDS_EXTENSION_PROMPT_PERMISSIONS_ABORT_BUTTON }; -// static -const int ExtensionInstallUI::kWarningIds[NUM_PROMPT_TYPES] = { +static const int kPermissionsHeaderIds[ExtensionInstallUI::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, @@ -82,12 +77,116 @@ const int kIconSize = 69; } // namespace -ExtensionInstallUI::Prompt::Prompt(PromptType type) : type(type) { +ExtensionInstallUI::Prompt::Prompt(PromptType type) : type_(type) { } ExtensionInstallUI::Prompt::~Prompt() { } +void ExtensionInstallUI::Prompt::SetPermissions( + std::vector<string16> permissions) { + permissions_ = permissions; +} + +void ExtensionInstallUI::Prompt::SetInlineInstallWebstoreData( + std::string localized_user_count, + double average_rating, + int rating_count) { + CHECK_EQ(INLINE_INSTALL_PROMPT, type_); + localized_user_count_ = localized_user_count; + average_rating_ = average_rating; + rating_count_ = rating_count; +} + +string16 ExtensionInstallUI::Prompt::GetDialogTitle() const { + if (type_ == INLINE_INSTALL_PROMPT) { + return l10n_util::GetStringFUTF16( + kTitleIds[type_], l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); + } else { + return l10n_util::GetStringUTF16(kTitleIds[type_]); + } +} + +string16 ExtensionInstallUI::Prompt::GetHeading(std::string extension_name) + const { + if (type_ == INLINE_INSTALL_PROMPT) { + return UTF8ToUTF16(extension_name); + } else { + return l10n_util::GetStringFUTF16( + kHeadingIds[type_], UTF8ToUTF16(extension_name)); + } +} + +string16 ExtensionInstallUI::Prompt::GetAcceptButtonLabel() const { + return l10n_util::GetStringUTF16(kAcceptButtonIds[type_]); +} + +bool ExtensionInstallUI::Prompt::HasAbortButtonLabel() const { + return kAbortButtonIds[type_] > 0; +} + +string16 ExtensionInstallUI::Prompt::GetAbortButtonLabel() const { + CHECK(HasAbortButtonLabel()); + return l10n_util::GetStringUTF16(kAbortButtonIds[type_]); +} + +string16 ExtensionInstallUI::Prompt::GetPermissionsHeader() const { + return l10n_util::GetStringUTF16(kPermissionsHeaderIds[type_]); +} + +void ExtensionInstallUI::Prompt::AppendRatingStars( + StarAppender appender, void* data) const { + CHECK(appender); + CHECK_EQ(INLINE_INSTALL_PROMPT, type_); + 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; + } + + int i; + for (i = 0; i < rating_integer; i++) { + appender(IDR_EXTENSIONS_RATING_STAR_ON, data); + } + if (rating_fractional) { + appender(IDR_EXTENSIONS_RATING_STAR_HALF_LEFT, data); + i++; + } + for (; i < kMaxExtensionRating; i++) { + appender(IDR_EXTENSIONS_RATING_STAR_OFF, data); + } +} + +string16 ExtensionInstallUI::Prompt::GetRatingCount() const { + CHECK_EQ(INLINE_INSTALL_PROMPT, type_); + return l10n_util::GetStringFUTF16( + IDS_EXTENSION_RATING_COUNT, + UTF8ToUTF16(base::IntToString(rating_count_))); +} + +string16 ExtensionInstallUI::Prompt::GetUserCount() const { + CHECK_EQ(INLINE_INSTALL_PROMPT, type_); + return l10n_util::GetStringFUTF16( + IDS_EXTENSION_USER_COUNT, + UTF8ToUTF16(localized_user_count_)); +} + +size_t ExtensionInstallUI::Prompt::GetPermissionCount() const { + CHECK_EQ(INLINE_INSTALL_PROMPT, type_); + return permissions_.size(); +} + +string16 ExtensionInstallUI::Prompt::GetPermission(int index) const { + CHECK_EQ(INLINE_INSTALL_PROMPT, type_); + return l10n_util::GetStringFUTF16( + IDS_EXTENSION_PERMISSION_LINE, permissions_[index]); +} + ExtensionInstallUI::ExtensionInstallUI(Profile* profile) : profile_(profile), ui_loop_(MessageLoop::current()), @@ -227,7 +326,7 @@ void ExtensionInstallUI::OnImageLoaded( NotificationService::NoDetails()); Prompt prompt(prompt_type_); - prompt.permissions = permissions_->GetWarningMessages(); + prompt.SetPermissions(permissions_->GetWarningMessages()); ShowExtensionInstallDialog( profile_, delegate_, extension_, &icon_, prompt); break; diff --git a/chrome/browser/extensions/extension_install_ui.h b/chrome/browser/extensions/extension_install_ui.h index 4cc10c2..18f69d3 100644 --- a/chrome/browser/extensions/extension_install_ui.h +++ b/chrome/browser/extensions/extension_install_ui.h @@ -37,35 +37,61 @@ class ExtensionInstallUI : public ImageLoadingTracker::Observer { }; // Extra information needed to display an installation or uninstallation - // prompt. - struct Prompt { + // prompt. Gets populated with raw data and exposes getters for formatted + // strings so that the GTK/views/Cocoa install dialogs don't have to repeat + // that logic. + class Prompt { + public: explicit Prompt(PromptType type); ~Prompt(); - PromptType type; + void SetPermissions(std::vector<string16> permissions); + void SetInlineInstallWebstoreData(std::string localized_user_count, + double average_rating, + int rating_count); + + PromptType type() const { return type_; } + + // Getters for UI element labels. + string16 GetDialogTitle() const; + string16 GetHeading(std::string extension_name) const; + string16 GetAcceptButtonLabel() const; + bool HasAbortButtonLabel() const; + string16 GetAbortButtonLabel() const; + string16 GetPermissionsHeader() const; + + // Getters for webstore metadata. Only populated when the type is + // INLINE_INSTALL_PROMPT. + + // The star display logic replicates the one used by the webstore (from + // components.ratingutils.setFractionalYellowStars). Callers pass in an + // "appender", which will be called back with the resource ID that they + // need to load/append. + typedef void*(*StarAppender)(int, void*); + void AppendRatingStars(StarAppender appender, void* data) const; + string16 GetRatingCount() const; + string16 GetUserCount() const; + size_t GetPermissionCount() const; + string16 GetPermission(int index) const; + + private: + 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; + 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; + std::string localized_user_count_; // Range is kMinExtensionRating to kMaxExtensionRating - double average_rating; - int rating_count; + 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]; - static const int kButtonIds[NUM_PROMPT_TYPES]; - static const int kWarningIds[NUM_PROMPT_TYPES]; - static const int kAbortButtonIds[NUM_PROMPT_TYPES]; - class Delegate { public: // We call this method to signal that the installation should continue. diff --git a/chrome/browser/extensions/webstore_inline_installer.cc b/chrome/browser/extensions/webstore_inline_installer.cc index ffa8158..ad08d85 100644 --- a/chrome/browser/extensions/webstore_inline_installer.cc +++ b/chrome/browser/extensions/webstore_inline_installer.cc @@ -252,9 +252,9 @@ void WebstoreInlineInstaller::OnWebstoreParseSuccess( tab_contents_->browser_context()); ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INLINE_INSTALL_PROMPT); - prompt.localized_user_count = localized_user_count_; - prompt.average_rating = average_rating_; - prompt.rating_count = rating_count_; + prompt.SetInlineInstallWebstoreData(localized_user_count_, + average_rating_, + rating_count_); ShowExtensionInstallDialogForManifest(profile, this, 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 5877481..cd383ee 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_install_dialog_controller.mm @@ -67,7 +67,7 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { // 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()) { + if (prompt.GetPermissionCount() == 0) { nibpath = [base::mac::MainAppBundle() pathForResource:@"ExtensionInstallPromptNoWarnings" ofType:@"nib"]; @@ -83,27 +83,26 @@ void OffsetControlVertically(NSControl* control, CGFloat amount) { icon_ = *icon; delegate_ = delegate; - title_.reset( - [l10n_util::GetNSStringF(ExtensionInstallUI::kHeadingIds[prompt.type], - UTF8ToUTF16(extension->name())) retain]); - subtitle_.reset( - [l10n_util::GetNSString(ExtensionInstallUI::kWarningIds[prompt.type]) - retain]); - 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]); + title_.reset([base::SysUTF16ToNSString( + prompt.GetHeading(extension->name())) retain]); + subtitle_.reset([base::SysUTF16ToNSString( + prompt.GetPermissionsHeader()) retain]); + button_.reset([base::SysUTF16ToNSString( + prompt.GetAcceptButtonLabel()) retain]); + NSString* cancel_button_label = prompt.HasAbortButtonLabel() ? + base::SysUTF16ToNSString(prompt.GetAbortButtonLabel()) : + l10n_util::GetNSString(IDS_CANCEL); + cancel_button_.reset([cancel_button_label retain]); // We display the permission warnings as a simple text string, separated by // newlines. - if (!prompt.permissions.empty()) { + if (prompt.GetPermissionCount()) { string16 joined_warnings; - for (size_t i = 0; i < prompt.permissions.size(); ++i) { + for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) { if (i > 0) joined_warnings += UTF8ToUTF16("\n\n"); - joined_warnings += prompt.permissions[i]; + joined_warnings += prompt.GetPermission(i); } warnings_.reset( 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 4a175b0..c29cbf6 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 @@ -108,7 +108,9 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalCancel) { new MockExtensionInstallUIDelegate); ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INSTALL_PROMPT); - prompt.permissions.push_back(UTF8ToUTF16("warning 1")); + std::vector<string16> permissions; + permissions.push_back(UTF8ToUTF16("warning 1")); + prompt.SetPermissions(permissions); scoped_nsobject<ExtensionInstallDialogController> controller([[ExtensionInstallDialogController alloc] @@ -140,7 +142,7 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalCancel) { EXPECT_TRUE([controller warningsField] != nil); EXPECT_NSEQ([[controller warningsField] stringValue], - base::SysUTF16ToNSString(prompt.permissions[0])); + base::SysUTF16ToNSString(prompt.GetPermission(0))); EXPECT_TRUE([controller warningsBox] != nil); @@ -164,7 +166,9 @@ TEST_F(ExtensionInstallDialogControllerTest, BasicsNormalOK) { new MockExtensionInstallUIDelegate); ExtensionInstallUI::Prompt prompt(ExtensionInstallUI::INSTALL_PROMPT); - prompt.permissions.push_back(UTF8ToUTF16("warning 1")); + std::vector<string16> permissions; + permissions.push_back(UTF8ToUTF16("warning 1")); + prompt.SetPermissions(permissions); scoped_nsobject<ExtensionInstallDialogController> controller([[ExtensionInstallDialogController alloc] @@ -192,12 +196,14 @@ TEST_F(ExtensionInstallDialogControllerTest, MultipleWarnings) { ExtensionInstallUI::Prompt one_warning_prompt( ExtensionInstallUI::INSTALL_PROMPT); - one_warning_prompt.permissions.push_back(UTF8ToUTF16("warning 1")); + std::vector<string16> permissions; + permissions.push_back(UTF8ToUTF16("warning 1")); + one_warning_prompt.SetPermissions(permissions); 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")); + permissions.push_back(UTF8ToUTF16("warning 2")); + two_warnings_prompt.SetPermissions(permissions); scoped_nsobject<ExtensionInstallDialogController> controller1([[ExtensionInstallDialogController alloc] 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 8544cda..00028b3 100644 --- a/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc +++ b/chrome/browser/ui/gtk/extensions/extension_install_dialog_gtk.cc @@ -4,7 +4,6 @@ #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" @@ -16,7 +15,6 @@ #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" @@ -45,33 +43,6 @@ GtkWidget* AddResourceIcon(int resource_id, GtkWidget* container) { return icon_widget; } -// 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; - } - - 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); - } -} - // Displays the dialog when constructed, deletes itself when dialog is // dismissed. Success/failure is passed back through the ExtensionInstallUI:: // Delegate instance. @@ -101,25 +72,24 @@ ExtensionInstallDialog::ExtensionInstallDialog( 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; + bool show_permissions = prompt.GetPermissionCount() > 0; + bool is_inline_install = + prompt.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT; // Build the dialog. dialog_ = gtk_dialog_new_with_buttons( - l10n_util::GetStringUTF8(ExtensionInstallUI::kTitleIds[type]).c_str(), + UTF16ToUTF8(prompt.GetDialogTitle()).c_str(), parent, GTK_DIALOG_MODAL, NULL); - int cancel = ExtensionInstallUI::kAbortButtonIds[type]; GtkWidget* close_button = gtk_dialog_add_button( GTK_DIALOG(dialog_), - cancel > 0 ? l10n_util::GetStringUTF8(cancel).c_str(): GTK_STOCK_CANCEL, + prompt.HasAbortButtonLabel() ? + UTF16ToUTF8(prompt.GetAbortButtonLabel()).c_str() : GTK_STOCK_CANCEL, GTK_RESPONSE_CLOSE); gtk_dialog_add_button( GTK_DIALOG(dialog_), - l10n_util::GetStringUTF8(ExtensionInstallUI::kButtonIds[type]).c_str(), + UTF16ToUTF8(prompt.GetAcceptButtonLabel()).c_str(), GTK_RESPONSE_ACCEPT); gtk_dialog_set_has_separator(GTK_DIALOG(dialog_), FALSE); @@ -142,17 +112,16 @@ ExtensionInstallDialog::ExtensionInstallDialog( 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); + // 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(left_column_area), heading_vbox, center_heading, + center_heading, 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); + // Heading + GtkWidget* heading_label = gtk_util::CreateBoldLabel( + UTF16ToUTF8(prompt.GetHeading(extension_->name())).c_str()); 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); @@ -160,18 +129,19 @@ ExtensionInstallDialog::ExtensionInstallDialog( // 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()); + prompt.AppendRatingStars( + reinterpret_cast<ExtensionInstallUI::Prompt::StarAppender>( + AddResourceIcon), + stars_hbox); + GtkWidget* rating_label = gtk_label_new(UTF16ToUTF8( + prompt.GetRatingCount()).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()); + GtkWidget* users_label = gtk_label_new(UTF16ToUTF8( + prompt.GetUserCount()).c_str()); gtk_util::SetLabelWidth(users_label, kLeftColumnMinWidth); gtk_util::SetLabelColor(users_label, &ui::kGdkGray); gtk_util::ForceFontSizePixels(rating_label, kRatingTextSize); @@ -219,17 +189,15 @@ ExtensionInstallDialog::ExtensionInstallDialog( permissions_container = left_column_area; } - 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); - - for (std::vector<string16>::const_iterator iter = permissions.begin(); - iter != permissions.end(); ++iter) { - GtkWidget* permission_label = gtk_label_new(l10n_util::GetStringFUTF8( - IDS_EXTENSION_PERMISSION_LINE, *iter).c_str()); + GtkWidget* permissions_header = gtk_util::CreateBoldLabel( + UTF16ToUTF8(prompt.GetPermissionsHeader()).c_str()); + gtk_util::SetLabelWidth(permissions_header, kLeftColumnMinWidth); + gtk_box_pack_start(GTK_BOX(permissions_container), permissions_header, + FALSE, FALSE, 0); + + for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) { + GtkWidget* permission_label = gtk_label_new(UTF16ToUTF8( + prompt.GetPermission(i)).c_str()); gtk_util::SetLabelWidth(permission_label, kLeftColumnMinWidth); gtk_box_pack_start(GTK_BOX(permissions_container), permission_label, FALSE, FALSE, kPermissionsPadding); 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 268c589..9e41a93 100644 --- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc +++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc @@ -14,8 +14,14 @@ #include "chrome/common/extensions/extension.h" #include "grit/generated_resources.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" #include "views/controls/image_view.h" #include "views/controls/label.h" +#include "views/controls/link.h" +#include "views/controls/link_listener.h" +#include "views/controls/separator.h" +#include "views/layout/box_layout.h" +#include "views/layout/grid_layout.h" #include "views/layout/layout_constants.h" #include "views/view.h" #include "views/widget/widget.h" @@ -26,30 +32,13 @@ namespace { // Size of extension icon in top left of dialog. const int kIconSize = 69; -// Width of the white permission box. This also is the max width of all -// elements in the right column of the dialog in the case where the extension -// requests permissions. -const int kPermissionBoxWidth = 270; - -// Width of the right column of the dialog when the extension requests no +// Width of the left column of the dialog when the extension requests // permissions. -const int kNoPermissionsRightColumnWidth = 210; - -// Width of the gray border around the permission box. -const int kPermissionBoxBorderWidth = 1; - -// Width of the horizontal padding inside the permission box border. -const int kPermissionBoxHorizontalPadding = 10; +const int kPermissionsLeftColumnWidth = 250; -// Width of the vertical padding inside the permission box border. -const int kPermissionBoxVerticalPadding = 11; - -// The max width of the individual permission strings inside the permission -// box. -const int kPermissionLabelWidth = - kPermissionBoxWidth - - kPermissionBoxBorderWidth * 2 - - kPermissionBoxHorizontalPadding * 2; +// Width of the left column of the dialog when the extension requests no +// permissions. +const int kNoPermissionsLeftColumnWidth = 200; // Heading font size correction. #if defined(CROS_FONTS_USING_BCI) @@ -58,22 +47,29 @@ const int kHeadingFontSizeDelta = 0; const int kHeadingFontSizeDelta = 1; #endif +const int kRatingFontSizeDelta = -1; + +void AddResourceIcon(int resource_id, views::View* parent) { + const SkBitmap* skia_image = ResourceBundle::GetSharedInstance(). + GetBitmapNamed(resource_id); + views::ImageView* image_view = new views::ImageView(); + image_view->SetImage(*skia_image); + parent->AddChildView(image_view); +} + } // namespace // Implements the extension installation dialog for TOOLKIT_VIEWS. -class ExtensionInstallDialogView : public views::DialogDelegateView { +class ExtensionInstallDialogView : public views::DialogDelegateView, + public views::LinkListener { public: ExtensionInstallDialogView(ExtensionInstallUI::Delegate* delegate, const Extension* extension, - SkBitmap* icon, + SkBitmap* skia_icon, const ExtensionInstallUI::Prompt& prompt); virtual ~ExtensionInstallDialogView(); private: - // views::View: - virtual gfx::Size GetPreferredSize() OVERRIDE; - virtual void Layout() OVERRIDE; - // views::DialogDelegate: virtual std::wstring GetDialogButtonLabel( MessageBoxFlags::DialogButton button) const OVERRIDE; @@ -86,31 +82,15 @@ class ExtensionInstallDialogView : public views::DialogDelegateView { virtual std::wstring GetWindowTitle() const OVERRIDE; virtual views::View* GetContentsView() OVERRIDE; - // The delegate that we will call back to when the user accepts or rejects - // the installation. - ExtensionInstallUI::Delegate* delegate_; - - // Displays the extension's icon. - views::ImageView* icon_; + // views::LinkListener: + virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE; - // Displays the main heading "Install FooBar?". - views::Label* heading_; - - // Displays the permission box header "The extension will have access to:". - views::Label* will_have_access_to_; - - // The white box containing the list of permissions the extension requires. - // This can be NULL if the extension requires no permissions. - views::View* permission_box_; - - // The labels describing each of the permissions the extension requires. - std::vector<views::Label*> permissions_; - - // The width of the right column of the dialog. Will be either - // kPermissionBoxWidth or kNoPermissionsRightColumnWidth, depending on - // whether the extension requires any permissions. - int right_column_width_; + bool is_inline_install() { + return prompt_.type() == ExtensionInstallUI::INLINE_INSTALL_PROMPT; + } + ExtensionInstallUI::Delegate* delegate_; + const Extension* extension_; ExtensionInstallUI::Prompt prompt_; DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogView); @@ -119,163 +99,171 @@ class ExtensionInstallDialogView : public views::DialogDelegateView { ExtensionInstallDialogView::ExtensionInstallDialogView( ExtensionInstallUI::Delegate* delegate, const Extension* extension, - SkBitmap* icon, + SkBitmap* skia_icon, const ExtensionInstallUI::Prompt& prompt) : delegate_(delegate), - icon_(NULL), - heading_(NULL), - will_have_access_to_(NULL), - permission_box_(NULL), - right_column_width_(0), + extension_(extension), prompt_(prompt) { + // Possible grid layouts: + // Inline install + // w/ permissions no permissions + // +--------------------+------+ +--------------+------+ + // | heading | icon | | heading | icon | + // +--------------------| | +--------------| | + // | rating | | | rating | | + // +--------------------| | +--------------+ | + // | user_count | | | user_count | | + // +--------------------| | +--------------| | + // | store_link | | | store_link | | + // +--------------------+------+ +--------------+------+ + // | separator | + // +--------------------+------+ + // | permissions_header | | + // +--------------------+------+ + // | permission1 | | + // +--------------------+------+ + // | permission2 | | + // +--------------------+------+ + // + // Regular install + // w/ permissions no permissions + // +--------------------+------+ +--------------+------+ + // | heading | icon | | heading | icon | + // +--------------------| | +--------------+------+ + // | permissions_header | | + // +--------------------| | + // | permission1 | | + // +--------------------| | + // | permission2 | | + // +--------------------+------+ + + using views::GridLayout; + GridLayout* layout = GridLayout::CreatePanel(this); + SetLayoutManager(layout); + + int column_set_id = 0; + views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); + + column_set->AddColumn(GridLayout::LEADING, + GridLayout::FILL, + 0, // no resizing + GridLayout::USE_PREF, + 0, // no fixed with + prompt.GetPermissionCount() > 0 ? + kNoPermissionsLeftColumnWidth : + kPermissionsLeftColumnWidth); + column_set->AddPaddingColumn(0, views::kPanelHorizMargin); + column_set->AddColumn(GridLayout::LEADING, + GridLayout::LEADING, + 0, // no resizing + GridLayout::USE_PREF, + 0, // no fixed width + kIconSize); + + layout->StartRow(0, column_set_id); + + views::Label* heading = new views::Label(UTF16ToWide( + prompt.GetHeading(extension->name()))); + heading->SetFont(heading->font().DeriveFont(kHeadingFontSizeDelta, + gfx::Font::BOLD)); + heading->SetMultiLine(true); + layout->AddView(heading); + // Scale down to icon size, but allow smaller icons (don't scale up). - gfx::Size size(icon->width(), icon->height()); + gfx::Size size(skia_icon->width(), skia_icon->height()); if (size.width() > kIconSize || size.height() > kIconSize) size = gfx::Size(kIconSize, kIconSize); - icon_ = new views::ImageView(); - icon_->SetImageSize(size); - icon_->SetImage(*icon); - icon_->SetHorizontalAlignment(views::ImageView::CENTER); - icon_->SetVerticalAlignment(views::ImageView::CENTER); - AddChildView(icon_); - - heading_ = new views::Label(UTF16ToWide( - l10n_util::GetStringFUTF16(ExtensionInstallUI::kHeadingIds[prompt.type], - UTF8ToUTF16(extension->name())))); - heading_->SetFont(heading_->font().DeriveFont(kHeadingFontSizeDelta, - gfx::Font::BOLD)); - heading_->SetMultiLine(true); - heading_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - AddChildView(heading_); - - 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[prompt.type]))); - will_have_access_to_->SetMultiLine(true); - will_have_access_to_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); - AddChildView(will_have_access_to_); - - permission_box_ = new views::View(); - permission_box_->set_background( - views::Background::CreateSolidBackground(SK_ColorWHITE)); - permission_box_->set_border( - views::Border::CreateSolidBorder(kPermissionBoxBorderWidth, - SK_ColorLTGRAY)); - AddChildView(permission_box_); + views::ImageView* icon = new views::ImageView(); + icon->SetImageSize(size); + icon->SetImage(*skia_icon); + icon->SetHorizontalAlignment(views::ImageView::CENTER); + icon->SetVerticalAlignment(views::ImageView::CENTER); + int icon_row_span = 1; + if (is_inline_install()) { + // Also span the rating, user_count and store_link rows. + icon_row_span = 4; + } else if (prompt.GetPermissionCount()) { + // Also span the permission header and each of the permission rows (all have + // a padding row above it). + icon_row_span = 3 + prompt.GetPermissionCount() * 2; } - - 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); - permissions_.push_back(label); + layout->AddView(icon, 1, icon_row_span); + + if (is_inline_install()) { + layout->StartRow(0, column_set_id); + views::View* rating = new views::View(); + rating->SetLayoutManager(new views::BoxLayout( + views::BoxLayout::kHorizontal, 0, 0, 0)); + layout->AddView(rating); + prompt.AppendRatingStars( + reinterpret_cast<ExtensionInstallUI::Prompt::StarAppender>( + AddResourceIcon), + rating); + + views::Label* rating_count = new views::Label( + UTF16ToWide(prompt.GetRatingCount())); + rating_count->SetFont( + rating_count->font().DeriveFont(kRatingFontSizeDelta)); + rating->AddChildView(rating_count); + + layout->StartRow(0, column_set_id); + views::Label* user_count = new views::Label( + UTF16ToWide(prompt.GetUserCount())); + user_count->SetColor(SK_ColorGRAY); + user_count->SetFont(user_count->font().DeriveFont(kRatingFontSizeDelta)); + layout->AddView(user_count); + + layout->StartRow(0, column_set_id); + views::Link* store_link = new views::Link(UTF16ToWide( + l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_STORE_LINK))); + store_link->SetFont(store_link->font().DeriveFont(kRatingFontSizeDelta)); + store_link->set_listener(this); + layout->AddView(store_link); } -} -ExtensionInstallDialogView::~ExtensionInstallDialogView() { -} - -gfx::Size ExtensionInstallDialogView::GetPreferredSize() { - int width = views::kPanelHorizMargin * 2; - width += kIconSize; - width += views::kPanelHorizMargin; // Gutter. - width += right_column_width_; - - int height = views::kPanelVertMargin * 2; - height += heading_->GetHeightForWidth(right_column_width_); - - if (permission_box_) { - height += views::kRelatedControlVerticalSpacing; - height += will_have_access_to_->GetHeightForWidth(right_column_width_); - - height += views::kRelatedControlVerticalSpacing; - height += kPermissionBoxBorderWidth * 2; - height += kPermissionBoxVerticalPadding * 2; + if (prompt.GetPermissionCount()) { + layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); - for (size_t i = 0; i < permissions_.size(); ++i) { - if (i > 0) - height += views::kRelatedControlVerticalSpacing; - height += permissions_[0]->GetHeightForWidth(kPermissionLabelWidth); + if (is_inline_install()) { + layout->StartRow(0, column_set_id); + views::Separator* separator = new views::Separator(); + layout->AddView(separator, 3, 1, GridLayout::FILL, GridLayout::FILL); + layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); } - } - - return gfx::Size(width, - std::max(height, kIconSize + views::kPanelVertMargin * 2)); -} - -void ExtensionInstallDialogView::Layout() { - int x = views::kPanelHorizMargin; - int y = views::kPanelVertMargin; - - icon_->SetBounds(x, y, kIconSize, kIconSize); - x += kIconSize; - x += views::kPanelHorizMargin; - heading_->SizeToFit(right_column_width_); - heading_->SetX(x); - - // If there's no special permissions, we do a slightly different layout with - // the heading centered vertically wrt the icon. - if (!permission_box_) { - heading_->SetY((GetPreferredSize().height() - heading_->height()) / 2); - return; - } - - // Otherwise, do the layout with the permission box. - heading_->SetY(y); - y += heading_->height(); - - y += views::kRelatedControlVerticalSpacing; - will_have_access_to_->SizeToFit(right_column_width_); - will_have_access_to_->SetX(x); - will_have_access_to_->SetY(y); - y += will_have_access_to_->height(); - - y += views::kRelatedControlVerticalSpacing; - permission_box_->SetX(x); - permission_box_->SetY(y); - - // First we layout the labels inside the permission box, so that we know how - // big the box will have to be. - int label_x = kPermissionBoxBorderWidth + kPermissionBoxHorizontalPadding; - int label_y = kPermissionBoxBorderWidth + kPermissionBoxVerticalPadding; - int permission_box_height = kPermissionBoxBorderWidth * 2; - permission_box_height += kPermissionBoxVerticalPadding * 2; - - for (size_t i = 0; i < permissions_.size(); ++i) { - if (i > 0) { - label_y += views::kRelatedControlVerticalSpacing; - permission_box_height += views::kPanelVertMargin; + layout->StartRow(0, column_set_id); + views::Label* permissions_header = new views::Label(UTF16ToWide( + prompt.GetPermissionsHeader())); + permissions_header->SetMultiLine(true); + layout->AddView(permissions_header); + + for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) { + layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + layout->StartRow(0, column_set_id); + views::Label* permission_label = new views::Label( + UTF16ToWide(prompt.GetPermission(i))); + permission_label->SetMultiLine(true); + permission_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + layout->AddView(permission_label, + 1, 1, + GridLayout::LEADING, GridLayout::FILL, + kPermissionsLeftColumnWidth, 0); } - - permissions_[i]->SizeToFit(kPermissionLabelWidth); - permissions_[i]->SetX(label_x); - permissions_[i]->SetY(label_y); - - label_y += permissions_[i]->height(); - permission_box_height += permissions_[i]->height(); } +} - // Now finally we can size the permission box itself. - permission_box_->SetBounds(permission_box_->x(), permission_box_->y(), - right_column_width_, permission_box_height); +ExtensionInstallDialogView::~ExtensionInstallDialogView() { } std::wstring ExtensionInstallDialogView::GetDialogButtonLabel( MessageBoxFlags::DialogButton button) const { switch (button) { case MessageBoxFlags::DIALOGBUTTON_OK: - return UTF16ToWide(l10n_util::GetStringUTF16( - ExtensionInstallUI::kButtonIds[prompt_.type])); - case MessageBoxFlags::DIALOGBUTTON_CANCEL: { - int id = ExtensionInstallUI::kAbortButtonIds[prompt_.type]; - return UTF16ToWide(l10n_util::GetStringUTF16(id > 0 ? id : IDS_CANCEL)); - } + return UTF16ToWide(prompt_.GetAcceptButtonLabel()); + case MessageBoxFlags::DIALOGBUTTON_CANCEL: + return prompt_.HasAbortButtonLabel() ? + UTF16ToWide(prompt_.GetAbortButtonLabel()) : + UTF16ToWide(l10n_util::GetStringUTF16(IDS_CANCEL)); default: NOTREACHED(); return std::wstring(); @@ -301,14 +289,22 @@ bool ExtensionInstallDialogView::IsModal() const { } std::wstring ExtensionInstallDialogView::GetWindowTitle() const { - return UTF16ToWide( - l10n_util::GetStringUTF16(ExtensionInstallUI::kTitleIds[prompt_.type])); + return UTF16ToWide(prompt_.GetDialogTitle()); } views::View* ExtensionInstallDialogView::GetContentsView() { return this; } +void ExtensionInstallDialogView::LinkClicked(views::Link* source, + int event_flags) { + GURL store_url( + extension_urls::GetWebstoreItemDetailURLPrefix() + extension_->id()); + BrowserList::GetLastActive()-> + OpenURL(store_url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); + GetWidget()->Close(); +} + void ShowExtensionInstallDialog( Profile* profile, ExtensionInstallUI::Delegate* delegate, diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index 96289ea..e61e8f6 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -426,7 +426,7 @@ std::string GetWebstoreItemDetailURLPrefix() { } GURL GetWebstoreItemJsonDataURL(const std::string& extension_id) { - return GURL(GetWebstoreItemDetailURLPrefix() + extension_id + "?output=json"); + return GURL(GetWebstoreLaunchURL() + "/inlineinstall/detail/" + extension_id); } const char* kGalleryUpdateHttpUrl = diff --git a/chrome/test/data/extensions/api_test/webstore_inline_install/detail/ecglahbcnmdpdciemllbhojghbkagdje b/chrome/test/data/extensions/api_test/webstore_inline_install/inlineinstall/detail/ecglahbcnmdpdciemllbhojghbkagdje index 1814b1e..1814b1e 100644 --- a/chrome/test/data/extensions/api_test/webstore_inline_install/detail/ecglahbcnmdpdciemllbhojghbkagdje +++ b/chrome/test/data/extensions/api_test/webstore_inline_install/inlineinstall/detail/ecglahbcnmdpdciemllbhojghbkagdje |