Move decoration intelligence of profile menus into base class.

Formatting and decoration intelligence of ProfileChooserView and
IncognitoMenuView are moved to ProfileMenuViewBase.
This removes the different individual constants used in the child
classes and makes all menus more in harmony.

Screenshots of visual changes:
https://screenshot.googleplex.com/bTuWe5nYrXx

Bug: 934689
Change-Id: I0186cb0147bead43aa2b597ae41ab41253f8d43c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1563991
Commit-Queue: Ramin Halavati <rhalavati@chromium.org>
Reviewed-by: Peter Boström <pbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#653920}
diff --git a/chrome/browser/ui/views/profiles/incognito_menu_view.cc b/chrome/browser/ui/views/profiles/incognito_menu_view.cc
index fe5c72f..ff7095d 100644
--- a/chrome/browser/ui/views/profiles/incognito_menu_view.cc
+++ b/chrome/browser/ui/views/profiles/incognito_menu_view.cc
@@ -18,9 +18,10 @@
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
-#include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/image_view.h"
+#include "ui/views/style/typography.h"
 
 IncognitoMenuView::IncognitoMenuView(views::Button* anchor_button,
                                      const gfx::Rect& anchor_rect,
@@ -39,8 +40,7 @@
 
 void IncognitoMenuView::Reset() {
   ProfileMenuViewBase::Reset();
-  title_card_ = nullptr;
-  close_button_ = nullptr;
+  exit_button_ = nullptr;
 }
 
 void IncognitoMenuView::Init() {
@@ -57,7 +57,7 @@
 
 void IncognitoMenuView::ButtonPressed(views::Button* sender,
                                       const ui::Event& event) {
-  DCHECK_EQ(sender, close_button_);
+  DCHECK_EQ(sender, exit_button_);
 
   // Skipping before-unload trigger to give incognito mode users a chance to
   // quickly close all incognito windows without needing to confirm closing the
@@ -80,28 +80,18 @@
   incognito_icon->SetImage(
       gfx::CreateVectorIcon(kIncognitoProfileIcon, icon_color));
 
-  // TODO(https://crbug.com/915120): This Button is never clickable. Replace
-  // by an alternative list item.
-  std::unique_ptr<HoverButton> title_card = std::make_unique<HoverButton>(
-      nullptr, std::move(incognito_icon),
+  AddMenuGroup(false /* add_separator */);
+  CreateAndAddTitleCard(
+      std::move(incognito_icon),
       l10n_util::GetStringUTF16(IDS_INCOGNITO_PROFILE_MENU_TITLE),
       incognito_window_count > 1
           ? l10n_util::GetPluralStringFUTF16(IDS_INCOGNITO_WINDOW_COUNT_MESSAGE,
                                              incognito_window_count)
-          : base::string16());
-  title_card->SetEnabled(false);
-  title_card_ = title_card.get();
+          : base::string16(),
+      false);
 
-  ProfileMenuViewBase::MenuItems menu_items;
-  menu_items.push_back(std::move(title_card));
-  AddMenuItems(menu_items, true);
-
-  std::unique_ptr<HoverButton> close_button = std::make_unique<HoverButton>(
-      this, gfx::CreateVectorIcon(kCloseAllIcon, 16, gfx::kChromeIconGrey),
+  AddMenuGroup();
+  exit_button_ = CreateAndAddButton(
+      gfx::CreateVectorIcon(kCloseAllIcon, 16, gfx::kChromeIconGrey),
       l10n_util::GetStringUTF16(IDS_INCOGNITO_PROFILE_MENU_CLOSE_BUTTON));
-  close_button_ = close_button.get();
-
-  menu_items.clear();
-  menu_items.push_back(std::move(close_button));
-  AddMenuItems(menu_items, true);
 }
diff --git a/chrome/browser/ui/views/profiles/incognito_menu_view.h b/chrome/browser/ui/views/profiles/incognito_menu_view.h
index 80d161f..cd1aff8 100644
--- a/chrome/browser/ui/views/profiles/incognito_menu_view.h
+++ b/chrome/browser/ui/views/profiles/incognito_menu_view.h
@@ -13,7 +13,7 @@
 #include "chrome/browser/ui/views/profiles/profile_menu_view_base.h"
 
 namespace views {
-class LabelButton;
+class Button;
 }
 
 class Browser;
@@ -24,8 +24,7 @@
 
 // This bubble view is displayed when the user clicks on the avatar button in
 // incognito mode and displays the incognito menu.
-class IncognitoMenuView : public ProfileMenuViewBase,
-                          public views::ButtonListener {
+class IncognitoMenuView : public ProfileMenuViewBase {
  public:
   IncognitoMenuView(views::Button* anchor_button,
                     const gfx::Rect& anchor_rect,
@@ -46,8 +45,7 @@
   // Adds the incognito window count view.
   void AddIncognitoWindowCountView();
 
-  views::LabelButton* title_card_;
-  views::LabelButton* close_button_;
+  views::Button* exit_button_;
 
   DISALLOW_COPY_AND_ASSIGN(IncognitoMenuView);
 };
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.cc b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
index 135ba56..7e974ff 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.cc
@@ -62,9 +62,6 @@
 
 // Helpers --------------------------------------------------------------------
 
-// Spacing between the edge of the user menu and the top/bottom or left/right of
-// the menu items.
-constexpr int kMenuEdgeMargin = 16;
 
 // Number of times the Dice sign-in promo illustration should be shown.
 constexpr int kDiceSigninPromoIllustrationShowCountMax = 10;
@@ -463,7 +460,7 @@
   // Sets an overall horizontal layout.
   std::unique_ptr<views::View> view = std::make_unique<views::View>();
   auto layout = std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kHorizontal, gfx::Insets(kMenuEdgeMargin),
+      views::BoxLayout::kHorizontal, gfx::Insets(GetMenuEdgeMargin()),
       provider->GetDistanceMetric(DISTANCE_UNRELATED_CONTROL_HORIZONTAL));
   layout->set_cross_axis_alignment(
       views::BoxLayout::CROSS_AXIS_ALIGNMENT_START);
@@ -518,9 +515,9 @@
   }
 
   view->AddChildView(vertical_view);
-  ProfileMenuViewBase::MenuItems menu_items;
-  menu_items.push_back(std::move(view));
-  AddMenuItems(menu_items, true);
+  AddMenuGroup();
+  AddViewItem(std::move(view));
+
   return true;
 }
 
@@ -531,19 +528,20 @@
   // Creates a view containing an error hover button displaying the current
   // profile (only selectable when sync is paused or disabled) and when sync is
   // not disabled there is a blue button to resolve the error.
-  ProfileMenuViewBase::MenuItems menu_items;
-
   const bool show_sync_paused_ui = error == sync_ui_util::AUTH_ERROR;
   const bool sync_disabled = !browser()->profile()->IsSyncAllowed();
-  // Add profile hover button.
+
+  AddMenuGroup();
+
+  // Add profile card.
   auto current_profile_photo = std::make_unique<BadgedProfilePhoto>(
       show_sync_paused_ui
           ? BadgedProfilePhoto::BADGE_TYPE_SYNC_PAUSED
           : sync_disabled ? BadgedProfilePhoto::BADGE_TYPE_SYNC_DISABLED
                           : BadgedProfilePhoto::BADGE_TYPE_SYNC_ERROR,
       avatar_item.icon);
-  std::unique_ptr<HoverButton> current_profile = std::make_unique<HoverButton>(
-      this, std::move(current_profile_photo),
+  current_profile_card_ = CreateAndAddTitleCard(
+      std::move(current_profile_photo),
       l10n_util::GetStringUTF16(
           show_sync_paused_ui
               ? IDS_PROFILES_DICE_SYNC_PAUSED_TITLE
@@ -552,37 +550,18 @@
       avatar_item.username);
 
   if (!show_sync_paused_ui && !sync_disabled) {
-    current_profile->SetStyle(HoverButton::STYLE_ERROR);
-    current_profile->SetEnabled(false);
+    static_cast<HoverButton*>(current_profile_card_)
+        ->SetStyle(HoverButton::STYLE_ERROR);
+    current_profile_card_->SetEnabled(false);
   }
 
-  current_profile_card_ = current_profile.get();
-  menu_items.push_back(std::move(current_profile));
-
   if (!sync_disabled) {
-    // TODO(https://crbug.com/934689): Move layout management to
-    // ProfileMenuViewBase.
-    // Add blue button.
-    sync_error_button_ = views::MdTextButton::CreateSecondaryUiBlueButton(
-        this, l10n_util::GetStringUTF16(button_string_id));
+    sync_error_button_ = CreateAndAddBlueButton(
+        l10n_util::GetStringUTF16(button_string_id), true /* md_style */);
     sync_error_button_->set_id(error);
     base::RecordAction(
         base::UserMetricsAction("ProfileChooser_SignInAgainDisplayed"));
-    // Add horizontal and bottom margin to blue button.
-    std::unique_ptr<views::View> padded_view = std::make_unique<views::View>();
-    padded_view->SetLayoutManager(std::make_unique<views::FillLayout>());
-    const int current_profile_vertical_margin =
-        ChromeLayoutProvider::Get()->GetDistanceMetric(
-            views::DISTANCE_CONTROL_VERTICAL_TEXT_PADDING);
-    padded_view->SetBorder(views::CreateEmptyBorder(
-        current_profile_vertical_margin, kMenuEdgeMargin,
-        2 + kMenuEdgeMargin - current_profile_vertical_margin,
-        kMenuEdgeMargin));
-    padded_view->AddChildView(sync_error_button_);
-    menu_items.push_back(std::move(padded_view));
   }
-
-  AddMenuItems(menu_items, true);
 }
 
 void ProfileChooserView::AddCurrentProfileView(
@@ -601,8 +580,7 @@
     return;
   }
 
-  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
-  ProfileMenuViewBase::MenuItems menu_items;
+  AddMenuGroup();
 
   auto current_profile_photo = std::make_unique<BadgedProfilePhoto>(
       GetProfileBadgeType(profile), avatar_item.icon);
@@ -616,17 +594,17 @@
       dice_enabled_ && profile->IsSyncAllowed() && show_email
           ? l10n_util::GetStringUTF16(IDS_PROFILES_SYNC_COMPLETE_TITLE)
           : profile_name;
-  std::unique_ptr<HoverButton> profile_card = std::make_unique<HoverButton>(
-      this, std::move(current_profile_photo), hover_button_title,
+
+  current_profile_card_ = CreateAndAddTitleCard(
+      std::move(current_profile_photo), hover_button_title,
       show_email ? avatar_item.username : base::string16());
   // TODO(crbug.com/815047): Sometimes, |avatar_item.username| is empty when
   // |show_email| is true, which should never happen. This causes a crash when
   // setting the elision behavior, so until this bug is fixed, avoid the crash
   // by checking that the username is not empty.
   if (show_email && !avatar_item.username.empty())
-    profile_card->SetSubtitleElideBehavior(gfx::ELIDE_EMAIL);
-  current_profile_card_ = profile_card.get();
-  menu_items.push_back(std::move(profile_card));
+    static_cast<HoverButton*>(current_profile_card_)
+        ->SetSubtitleElideBehavior(gfx::ELIDE_EMAIL);
 
   // The available links depend on the type of profile that is active.
   if (is_guest) {
@@ -636,50 +614,25 @@
         IDS_PROFILES_EDIT_SIGNED_IN_PROFILE_ACCESSIBLE_NAME, profile_name,
         avatar_item.username));
   } else {
+    AddMenuGroup(false /* add_separator */);
     bool is_signin_allowed =
         profile->GetPrefs()->GetBoolean(prefs::kSigninAllowed);
     if (!dice_enabled_ && is_signin_allowed) {
-      // TODO(https://crbug.com/934689): Move layout management to
-      // ProfileMenuViewBase.
-      std::unique_ptr<views::View> extra_links_view =
-          std::make_unique<views::View>();
-      extra_links_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-          views::BoxLayout::kVertical,
-          gfx::Insets(provider->GetDistanceMetric(
-                          views::DISTANCE_RELATED_CONTROL_VERTICAL),
-                      kMenuEdgeMargin),
-          kMenuEdgeMargin));
-      views::Label* promo = new views::Label(
-          l10n_util::GetStringUTF16(IDS_PROFILES_SIGNIN_PROMO));
-      promo->SetMultiLine(true);
-      promo->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+      CreateAndAddLabel(l10n_util::GetStringUTF16(IDS_PROFILES_SIGNIN_PROMO));
 
-      // Provide a hint to the layout manager by giving the promo text a maximum
-      // width. This ensures it has the correct number of lines when determining
-      // the initial Widget size.
-      promo->SetMaximumWidth(menu_width());
-      extra_links_view->AddChildView(promo);
+      signin_current_profile_button_ = CreateAndAddBlueButton(
+          l10n_util::GetStringFUTF16(
+              IDS_SYNC_START_SYNC_BUTTON_LABEL,
+              l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)),
+          true /* md_style */);
 
-      signin_current_profile_button_ =
-          views::MdTextButton::CreateSecondaryUiBlueButton(
-              this, l10n_util::GetStringFUTF16(
-                        IDS_SYNC_START_SYNC_BUTTON_LABEL,
-                        l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)));
-      extra_links_view->AddChildView(signin_current_profile_button_);
       signin_metrics::RecordSigninImpressionUserActionForAccessPoint(
           signin_metrics::AccessPoint::ACCESS_POINT_AVATAR_BUBBLE_SIGN_IN);
-      extra_links_view->SetBorder(views::CreateEmptyBorder(
-          0, 0,
-          provider->GetDistanceMetric(DISTANCE_RELATED_CONTROL_VERTICAL_SMALL),
-          0));
-      menu_items.push_back(std::move(extra_links_view));
     }
 
     current_profile_card_->SetAccessibleName(l10n_util::GetStringFUTF16(
         IDS_PROFILES_EDIT_PROFILE_ACCESSIBLE_NAME, profile_name));
   }
-
-  AddMenuItems(menu_items, true);
 }
 
 void ProfileChooserView::AddDiceSigninView() {
@@ -687,14 +640,6 @@
   // Create a view that holds an illustration, a promo text and a button to turn
   // on Sync. The promo illustration is only shown the first 10 times per
   // profile.
-  // TODO(https://crbug.com/934689): Move layout management to
-  // ProfileMenuViewBase.
-  int promotext_top_spacing = 16;
-  const int small_vertical_spacing =
-      ChromeLayoutProvider::Get()->GetDistanceMetric(
-          DISTANCE_CONTENT_LIST_VERTICAL_SINGLE);
-  ProfileMenuViewBase::MenuItems menu_items;
-
   const bool promo_account_available = !dice_accounts_.empty();
 
   // Log sign-in impressions user metrics.
@@ -705,6 +650,8 @@
       promo_account_available);
 
   if (!promo_account_available) {
+    AddMenuGroup();
+
     // Show promo illustration+text when there is no promo account.
     if (GetDiceSigninPromoShowCount() <=
         kDiceSigninPromoIllustrationShowCountMax) {
@@ -715,35 +662,22 @@
       illustration->SetImage(
           *rb.GetNativeImageNamed(IDR_PROFILES_DICE_TURN_ON_SYNC)
                .ToImageSkia());
-      menu_items.push_back(std::move(illustration));
-      // Adjust the spacing between illustration and promo text.
-      promotext_top_spacing = 24;
+      AddViewItem(std::move(illustration));
     }
     // Add the promo text.
-    std::unique_ptr<views::Label> promo = std::make_unique<views::Label>(
-        l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PROMO));
-    promo->SetMultiLine(true);
-    promo->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-    promo->SetMaximumWidth(menu_width() - 2 * kMenuEdgeMargin);
-    promo->SetBorder(views::CreateEmptyBorder(
-        promotext_top_spacing, kMenuEdgeMargin, 0, kMenuEdgeMargin));
-    menu_items.push_back(std::move(promo));
+    CreateAndAddLabel(l10n_util::GetStringUTF16(IDS_PROFILES_DICE_SYNC_PROMO));
 
     // Create a sign-in button without account information.
     std::unique_ptr<DiceSigninButtonView> signin_button =
         std::make_unique<DiceSigninButtonView>(this);
-    signin_button->SetBorder(views::CreateEmptyBorder(gfx::Insets(
-        kMenuEdgeMargin, kMenuEdgeMargin,
-        kMenuEdgeMargin - small_vertical_spacing, kMenuEdgeMargin)));
-    dice_signin_button_view_ = signin_button.get();
-    menu_items.push_back(std::move(signin_button));
+    dice_signin_button_view_ = CreateAndAddDiceSigninButton();
     signin_current_profile_button_ = dice_signin_button_view_->signin_button();
 
-    AddMenuItems(menu_items, true);
     return;
   }
-  // Create a button to sign in the first account of
-  // |dice_accounts_|.
+
+  AddMenuGroup();
+  // Create a button to sign in the first account of |dice_accounts_|.
   AccountInfo dice_promo_default_account = dice_accounts_[0];
   gfx::Image account_icon = dice_promo_default_account.account_image;
   if (account_icon.IsEmpty()) {
@@ -751,32 +685,13 @@
         profiles::GetPlaceholderAvatarIconResourceID());
   }
   dice_signin_button_view_ =
-      new DiceSigninButtonView(dice_promo_default_account, account_icon, this,
-                               /*show_drop_down_arrow=*/false);
+      CreateAndAddDiceSigninButton(&dice_promo_default_account, &account_icon);
   signin_with_gaia_account_button_ = dice_signin_button_view_->signin_button();
 
-  // TODO(https://crbug.com/934689): Move layout management to
-  // ProfileMenuViewBase.
-  std::unique_ptr<views::View> promo_button_container =
-      std::make_unique<views::View>();
-  const int content_list_vert_spacing =
-      ChromeLayoutProvider::Get()->GetDistanceMetric(
-          DISTANCE_CONTENT_LIST_VERTICAL_MULTI);
-  const int bottom_spacing = kMenuEdgeMargin - content_list_vert_spacing;
-  promo_button_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kVertical,
-      gfx::Insets(kMenuEdgeMargin, kMenuEdgeMargin, bottom_spacing,
-                  kMenuEdgeMargin),
-      content_list_vert_spacing));
-  promo_button_container->AddChildView(dice_signin_button_view_);
-
   // Add sign out button.
-  signout_button_ = views::MdTextButton::Create(
-      this, l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT));
-  promo_button_container->AddChildView(signout_button_);
-
-  menu_items.push_back(std::move(promo_button_container));
-  AddMenuItems(menu_items, true);
+  signout_button_ = CreateAndAddBlueButton(
+      l10n_util::GetStringUTF16(IDS_SCREEN_LOCK_SIGN_OUT),
+      false /* md_style */);
 }
 
 void ProfileChooserView::AddGuestProfileView() {
@@ -794,7 +709,7 @@
 
 void ProfileChooserView::AddOptionsView(bool display_lock,
                                         AvatarMenu* avatar_menu) {
-  ProfileMenuViewBase::MenuItems menu_items;
+  AddMenuGroup();
 
   const bool is_guest = browser()->profile()->IsGuestSession();
   // Add the user switching buttons.
@@ -812,14 +727,12 @@
       gfx::Image image = profiles::GetSizedAvatarIcon(
           item.icon, true, GetDefaultIconSize(), GetDefaultIconSize(),
           profiles::SHAPE_CIRCLE);
-      std::unique_ptr<HoverButton> button = std::make_unique<HoverButton>(
-          this, *image.ToImageSkia(),
-          profiles::GetProfileSwitcherTextForItem(item));
-      open_other_profile_indexes_map_[button.get()] = i;
+      views::Button* button = CreateAndAddButton(
+          *image.ToImageSkia(), profiles::GetProfileSwitcherTextForItem(item));
+      open_other_profile_indexes_map_[button] = i;
 
       if (!first_profile_button_)
-        first_profile_button_ = button.get();
-      menu_items.push_back(std::move(button));
+        first_profile_button_ = button;
     }
   }
 
@@ -831,11 +744,9 @@
     PrefService* service = g_browser_process->local_state();
     DCHECK(service);
     if (service->GetBoolean(prefs::kBrowserGuestModeEnabled)) {
-      std::unique_ptr<HoverButton> guest_button = std::make_unique<HoverButton>(
-          this, CreateVectorIcon(kUserMenuGuestIcon),
+      guest_profile_button_ = CreateAndAddButton(
+          CreateVectorIcon(kUserMenuGuestIcon),
           l10n_util::GetStringUTF16(IDS_PROFILES_OPEN_GUEST_PROFILE_BUTTON));
-      guest_profile_button_ = guest_button.get();
-      menu_items.push_back(std::move(guest_button));
     }
   }
 
@@ -843,87 +754,60 @@
       is_guest ? IDS_PROFILES_EXIT_GUEST : IDS_PROFILES_MANAGE_USERS_BUTTON);
   const gfx::VectorIcon& settings_icon =
       is_guest ? kCloseAllIcon : kSettingsIcon;
-  std::unique_ptr<HoverButton> button = std::make_unique<HoverButton>(
-      this, CreateVectorIcon(settings_icon), text);
-  users_button_ = button.get();
-  menu_items.push_back(std::move(button));
+  users_button_ = CreateAndAddButton(CreateVectorIcon(settings_icon), text);
 
   if (display_lock) {
-    std::unique_ptr<HoverButton> button = std::make_unique<HoverButton>(
-        this,
+    lock_button_ = CreateAndAddButton(
         gfx::CreateVectorIcon(vector_icons::kLockIcon, GetDefaultIconSize(),
                               gfx::kChromeIconGrey),
         l10n_util::GetStringUTF16(IDS_PROFILES_PROFILE_SIGNOUT_BUTTON));
-    lock_button_ = button.get();
-    menu_items.push_back(std::move(button));
   } else if (!is_guest) {
     AvatarMenu::Item active_avatar_item =
         avatar_menu->GetItemAt(ordered_item_indices[0]);
-    std::unique_ptr<HoverButton> button = std::make_unique<HoverButton>(
-        this, CreateVectorIcon(kCloseAllIcon),
+    close_all_windows_button_ = CreateAndAddButton(
+        CreateVectorIcon(kCloseAllIcon),
         avatar_menu->GetNumberOfItems() >= 2
             ? l10n_util::GetStringFUTF16(IDS_PROFILES_EXIT_PROFILE_BUTTON,
                                          active_avatar_item.name)
             : l10n_util::GetStringUTF16(IDS_PROFILES_CLOSE_ALL_WINDOWS_BUTTON));
-    close_all_windows_button_ = button.get();
-    menu_items.push_back(std::move(button));
   }
-
-  AddMenuItems(menu_items, true);
 }
 
 void ProfileChooserView::AddSupervisedUserDisclaimerView() {
-  ProfileMenuViewBase::MenuItems menu_items;
-
-  std::unique_ptr<views::Label> disclaimer = std::make_unique<views::Label>(
+  AddMenuGroup();
+  auto* disclaimer = CreateAndAddLabel(
       avatar_menu_->GetSupervisedUserInformation(), CONTEXT_BODY_TEXT_SMALL);
-  disclaimer->SetMultiLine(true);
   disclaimer->SetAllowCharacterBreak(true);
-  disclaimer->SetHorizontalAlignment(gfx::ALIGN_LEFT);
-  menu_items.push_back(std::move(disclaimer));
-  AddMenuItems(menu_items, true);
 }
 
 void ProfileChooserView::AddAutofillHomeView() {
   if (browser()->profile()->IsGuestSession())
     return;
 
-  ProfileMenuViewBase::MenuItems menu_items;
+  AddMenuGroup();
 
   // Passwords.
-  std::unique_ptr<HoverButton> button = std::make_unique<HoverButton>(
-      this, CreateVectorIcon(kKeyIcon),
+  passwords_button_ = CreateAndAddButton(
+      CreateVectorIcon(kKeyIcon),
       l10n_util::GetStringUTF16(IDS_PROFILES_PASSWORDS_LINK));
-  passwords_button_ = button.get();
-  menu_items.push_back(std::move(button));
 
   // Credit cards.
-  button = std::make_unique<HoverButton>(
-      this, CreateVectorIcon(kCreditCardIcon),
+  credit_cards_button_ = CreateAndAddButton(
+      CreateVectorIcon(kCreditCardIcon),
       l10n_util::GetStringUTF16(IDS_PROFILES_CREDIT_CARDS_LINK));
-  credit_cards_button_ = button.get();
-  menu_items.push_back(std::move(button));
 
   // Addresses.
-  button = std::make_unique<HoverButton>(
-      this, CreateVectorIcon(vector_icons::kLocationOnIcon),
+  addresses_button_ = CreateAndAddButton(
+      CreateVectorIcon(vector_icons::kLocationOnIcon),
       l10n_util::GetStringUTF16(IDS_PROFILES_ADDRESSES_LINK));
-  addresses_button_ = button.get();
-  menu_items.push_back(std::move(button));
-
-  AddMenuItems(menu_items, /*new_group=*/true);
 }
 
 #if defined(GOOGLE_CHROME_BUILD)
 void ProfileChooserView::AddManageGoogleAccountButton() {
-  std::unique_ptr<HoverButton> button = std::make_unique<HoverButton>(
-      this, GetGoogleIconForUserMenu(GetDefaultIconSize()),
+  AddMenuGroup(false /* add_separator */);
+  manage_google_account_button_ = CreateAndAddButton(
+      GetGoogleIconForUserMenu(GetDefaultIconSize()),
       l10n_util::GetStringUTF16(IDS_SETTINGS_MANAGE_GOOGLE_ACCOUNT));
-  manage_google_account_button_ = button.get();
-
-  ProfileMenuViewBase::MenuItems menu_items;
-  menu_items.push_back(std::move(button));
-  AddMenuItems(menu_items, /*new_group=*/false);
 }
 #endif
 
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view.h b/chrome/browser/ui/views/profiles/profile_chooser_view.h
index b167526..73be6c1 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view.h
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view.h
@@ -23,7 +23,7 @@
 #include "services/identity/public/cpp/identity_manager.h"
 
 namespace views {
-class LabelButton;
+class Button;
 }
 
 class Browser;
@@ -38,7 +38,6 @@
 // It displays a list of profiles and allows users to switch between profiles.
 class ProfileChooserView : public ProfileMenuViewBase,
                            public AvatarMenuObserver,
-                           public views::ButtonListener,
                            public identity::IdentityManager::Observer {
  public:
   ProfileChooserView(views::Button* anchor_button,
@@ -136,30 +135,30 @@
   ButtonIndexes open_other_profile_indexes_map_;
 
   // Button in the signin/sync error header on top of the desktop user menu.
-  views::LabelButton* sync_error_button_;
+  views::Button* sync_error_button_;
 
   // Links and buttons displayed in the active profile card.
   views::Link* manage_accounts_link_;
-  views::LabelButton* manage_accounts_button_;
-  views::LabelButton* signin_current_profile_button_;
+  views::Button* manage_accounts_button_;
+  views::Button* signin_current_profile_button_;
   HoverButton* sync_to_another_account_button_;
-  views::LabelButton* signin_with_gaia_account_button_;
+  views::Button* signin_with_gaia_account_button_;
 
   // For material design user menu, the active profile card owns the profile
   // name and photo.
-  views::LabelButton* current_profile_card_;
+  views::Button* current_profile_card_;
 
   // Action buttons.
-  views::LabelButton* first_profile_button_;
-  views::LabelButton* guest_profile_button_;
-  views::LabelButton* users_button_;
-  views::LabelButton* lock_button_;
-  views::LabelButton* close_all_windows_button_;
-  views::LabelButton* passwords_button_;
-  views::LabelButton* credit_cards_button_;
-  views::LabelButton* addresses_button_;
-  views::LabelButton* signout_button_;
-  views::LabelButton* manage_google_account_button_;
+  views::Button* first_profile_button_;
+  views::Button* guest_profile_button_;
+  views::Button* users_button_;
+  views::Button* lock_button_;
+  views::Button* close_all_windows_button_;
+  views::Button* passwords_button_;
+  views::Button* credit_cards_button_;
+  views::Button* addresses_button_;
+  views::Button* signout_button_;
+  views::Button* manage_google_account_button_;
 
   // View for the signin/turn-on-sync button in the dice promo.
   DiceSigninButtonView* dice_signin_button_view_;
diff --git a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
index 4568f52..78f7906 100644
--- a/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
+++ b/chrome/browser/ui/views/profiles/profile_chooser_view_browsertest.cc
@@ -225,7 +225,7 @@
         ProfileChooserView::GetBubbleForTesting());
   }
 
-  views::LabelButton* signin_current_profile_button() {
+  views::Button* signin_current_profile_button() {
     return current_profile_bubble()->signin_current_profile_button_;
   }
 
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
index d0ddde5..b0e16dc 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.cc
@@ -14,16 +14,20 @@
 #include "chrome/browser/signin/account_consistency_mode_manager.h"
 #include "chrome/browser/ui/chrome_pages.h"
 #include "chrome/browser/ui/views/chrome_layout_provider.h"
+#include "chrome/browser/ui/views/hover_button.h"
 #include "chrome/browser/ui/views/profiles/incognito_menu_view.h"
 #include "chrome/grit/generated_resources.h"
 #include "ui/base/l10n/l10n_util.h"
 #include "ui/display/display.h"
 #include "ui/display/screen.h"
+#include "ui/views/controls/button/label_button.h"
+#include "ui/views/controls/button/md_text_button.h"
 #include "ui/views/controls/scroll_view.h"
 #include "ui/views/controls/separator.h"
 
 #if !defined(OS_CHROMEOS)
 #include "chrome/browser/ui/views/profiles/profile_chooser_view.h"
+#include "chrome/browser/ui/views/sync/dice_signin_button_view.h"
 #endif
 
 namespace {
@@ -40,8 +44,22 @@
 // least this tall to show one row.
 constexpr int kMinimumScrollableContentHeight = 40;
 
+// Spacing between the edge of the user menu and the top/bottom or left/right of
+// the menu items.
+constexpr int kMenuEdgeMargin = 16;
+
 }  // namespace
 
+// MenuItems--------------------------------------------------------------------
+
+ProfileMenuViewBase::MenuItems::MenuItems()
+    : first_item_type(ProfileMenuViewBase::MenuItems::kNone),
+      last_item_type(ProfileMenuViewBase::MenuItems::kNone),
+      different_item_types(false) {}
+
+ProfileMenuViewBase::MenuItems::MenuItems(MenuItems&&) = default;
+ProfileMenuViewBase::MenuItems::~MenuItems() = default;
+
 // ProfileMenuViewBase ---------------------------------------------------------
 
 // static
@@ -192,39 +210,188 @@
   menu_item_groups_.clear();
 }
 
-void ProfileMenuViewBase::AddMenuItems(MenuItems& menu_items, bool new_group) {
-  if (new_group || menu_item_groups_.empty())
-    menu_item_groups_.emplace_back();
+int ProfileMenuViewBase::GetMarginSize(GroupMarginSize margin_size) const {
+  switch (margin_size) {
+    case kNone:
+      return 0;
+    case kTiny:
+      return ChromeLayoutProvider::Get()->GetDistanceMetric(
+          DISTANCE_CONTENT_LIST_VERTICAL_SINGLE);
+    case kSmall:
+      return ChromeLayoutProvider::Get()->GetDistanceMetric(
+          DISTANCE_RELATED_CONTROL_VERTICAL_SMALL);
+    case kLarge:
+      return ChromeLayoutProvider::Get()->GetDistanceMetric(
+          DISTANCE_UNRELATED_CONTROL_VERTICAL_LARGE);
+  }
+}
 
-  auto& last_group = menu_item_groups_.back();
-  for (auto& item : menu_items)
-    last_group.push_back(std::move(item));
+int ProfileMenuViewBase::GetMenuEdgeMargin() const {
+  return kMenuEdgeMargin;
+}
+
+void ProfileMenuViewBase::AddMenuGroup(bool add_separator) {
+  if (add_separator && !menu_item_groups_.empty()) {
+    DCHECK(!menu_item_groups_.back().items.empty());
+    menu_item_groups_.emplace_back();
+  }
+
+  menu_item_groups_.emplace_back();
+}
+
+void ProfileMenuViewBase::AddMenuItemInternal(std::unique_ptr<views::View> view,
+                                              MenuItems::ItemType item_type) {
+  DCHECK(!menu_item_groups_.empty());
+  auto& current_group = menu_item_groups_.back();
+
+  current_group.items.push_back(std::move(view));
+  if (current_group.items.size() == 1) {
+    current_group.first_item_type = item_type;
+    current_group.last_item_type = item_type;
+  } else {
+    current_group.different_item_types |=
+        current_group.last_item_type != item_type;
+    current_group.last_item_type = item_type;
+  }
+}
+
+views::Button* ProfileMenuViewBase::CreateAndAddTitleCard(
+    std::unique_ptr<views::View> icon_view,
+    const base::string16& title,
+    const base::string16& subtitle,
+    bool enabled) {
+  std::unique_ptr<HoverButton> title_card = std::make_unique<HoverButton>(
+      enabled ? this : nullptr, std::move(icon_view), title, subtitle);
+  title_card->SetEnabled(enabled);
+  views::Button* pointer = title_card.get();
+  AddMenuItemInternal(std::move(title_card), MenuItems::kTitleCard);
+  return pointer;
+}
+
+views::Button* ProfileMenuViewBase::CreateAndAddButton(
+    const gfx::ImageSkia& icon,
+    const base::string16& title) {
+  std::unique_ptr<HoverButton> button =
+      std::make_unique<HoverButton>(this, icon, title);
+  views::Button* pointer = button.get();
+  AddMenuItemInternal(std::move(button), MenuItems::kButton);
+  return pointer;
+}
+
+views::Button* ProfileMenuViewBase::CreateAndAddBlueButton(
+    const base::string16& text,
+    bool md_style) {
+  std::unique_ptr<views::LabelButton> button = base::WrapUnique(
+      md_style ? views::MdTextButton::CreateSecondaryUiBlueButton(this, text)
+               : views::MdTextButton::Create(this, text));
+  views::Button* pointer = button.get();
+
+  // Add margins.
+  std::unique_ptr<views::View> margined_view = std::make_unique<views::View>();
+  margined_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kVertical, gfx::Insets(0, kMenuEdgeMargin)));
+  margined_view->AddChildView(std::move(button));
+
+  AddMenuItemInternal(std::move(margined_view), MenuItems::kStyledButton);
+  return pointer;
+}
+
+#if !defined(OS_CHROMEOS)
+DiceSigninButtonView* ProfileMenuViewBase::CreateAndAddDiceSigninButton(
+    AccountInfo* account_info,
+    gfx::Image* account_icon) {
+  std::unique_ptr<DiceSigninButtonView> button =
+      account_info ? std::make_unique<DiceSigninButtonView>(
+                         *account_info, *account_icon, this,
+                         false /* show_drop_down_arrow */)
+                   : std::make_unique<DiceSigninButtonView>(this);
+  DiceSigninButtonView* pointer = button.get();
+
+  // Add margins.
+  std::unique_ptr<views::View> margined_view = std::make_unique<views::View>();
+  margined_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kVertical,
+      gfx::Insets(GetMarginSize(kSmall), kMenuEdgeMargin)));
+  margined_view->AddChildView(std::move(button));
+
+  AddMenuItemInternal(std::move(margined_view), MenuItems::kStyledButton);
+  return pointer;
+}
+#endif
+
+views::Label* ProfileMenuViewBase::CreateAndAddLabel(const base::string16& text,
+                                                     int text_context) {
+  std::unique_ptr<views::Label> label =
+      std::make_unique<views::Label>(text, text_context);
+  label->SetMultiLine(true);
+  label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  label->SetMaximumWidth(menu_width_ - 2 * kMenuEdgeMargin);
+  views::Label* pointer = label.get();
+
+  // Add margins.
+  std::unique_ptr<views::View> margined_view = std::make_unique<views::View>();
+  margined_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kVertical, gfx::Insets(0, kMenuEdgeMargin)));
+  margined_view->AddChildView(std::move(label));
+
+  AddMenuItemInternal(std::move(margined_view), MenuItems::kLabel);
+  return pointer;
+}
+
+void ProfileMenuViewBase::AddViewItem(std::unique_ptr<views::View> view) {
+  // Add margins.
+  std::unique_ptr<views::View> margined_view = std::make_unique<views::View>();
+  margined_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+      views::BoxLayout::kVertical, gfx::Insets(0, kMenuEdgeMargin)));
+  margined_view->AddChildView(std::move(view));
+  AddMenuItemInternal(std::move(margined_view), MenuItems::kGeneral);
 }
 
 void ProfileMenuViewBase::RepopulateViewFromMenuItems() {
   RemoveAllChildViews(true);
 
-  ChromeLayoutProvider* provider = ChromeLayoutProvider::Get();
-  int border_insets =
-      provider->GetDistanceMetric(DISTANCE_CONTENT_LIST_VERTICAL_SINGLE);
-
   std::unique_ptr<views::View> main_view = std::make_unique<views::View>();
   main_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-      views::BoxLayout::kVertical, gfx::Insets(border_insets, 0),
-      border_insets));
+      views::BoxLayout::kVertical, gfx::Insets()));
 
-  for (size_t i = 0; i < menu_item_groups_.size(); i++) {
-    if (i > 0)
+  for (MenuItems& group : menu_item_groups_) {
+    if (group.items.empty()) {
+      // An empty group represents a separator.
       main_view->AddChildView(new views::Separator());
+    } else {
+      views::View* sub_view = new views::View();
+      GroupMarginSize top_margin;
+      GroupMarginSize bottom_margin;
+      GroupMarginSize child_spacing;
 
-    views::View* sub_view = new views::View();
-    sub_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
-        views::BoxLayout::kVertical, gfx::Insets(i ? border_insets : 0, 0)));
+      if (group.first_item_type == MenuItems::kTitleCard ||
+          group.first_item_type == MenuItems::kLabel) {
+        top_margin = kTiny;
+      } else {
+        top_margin = kSmall;
+      }
 
-    for (std::unique_ptr<views::View>& item : menu_item_groups_[i])
-      sub_view->AddChildView(std::move(item));
+      if (group.last_item_type == MenuItems::kTitleCard) {
+        bottom_margin = kTiny;
+      } else if (group.last_item_type == MenuItems::kButton) {
+        bottom_margin = kSmall;
+      } else {
+        bottom_margin = kLarge;
+      }
 
-    main_view->AddChildView(sub_view);
+      child_spacing = group.different_item_types ? kLarge : kNone;
+
+      sub_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
+          views::BoxLayout::kVertical,
+          gfx::Insets(GetMarginSize(top_margin), 0,
+                      GetMarginSize(bottom_margin), 0),
+          GetMarginSize(child_spacing)));
+
+      for (std::unique_ptr<views::View>& item : group.items)
+        sub_view->AddChildView(std::move(item));
+
+      main_view->AddChildView(sub_view);
+    }
   }
 
   menu_item_groups_.clear();
diff --git a/chrome/browser/ui/views/profiles/profile_menu_view_base.h b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
index 7c837e64..f8e4e4db 100644
--- a/chrome/browser/ui/views/profiles/profile_menu_view_base.h
+++ b/chrome/browser/ui/views/profiles/profile_menu_view_base.h
@@ -22,19 +22,53 @@
 #include "ui/views/controls/link_listener.h"
 #include "ui/views/controls/styled_label_listener.h"
 #include "ui/views/layout/box_layout.h"
+#include "ui/views/style/typography.h"
 
 class Browser;
 
-// TODO(https://crbug.com/934689): Separation of providing content for different
-// menus and the UI effort to view it between this class and
-// |ProfileChooserView| is in progress.
+namespace views {
+class Button;
+class Label;
+}  // namespace views
+
+struct AccountInfo;
+class DiceSigninButtonView;
 
 // This class provides the UI for different menus that are created by user
 // clicking the avatar button.
 class ProfileMenuViewBase : public content::WebContentsDelegate,
                             public views::BubbleDialogDelegateView,
+                            public views::ButtonListener,
                             public views::StyledLabelListener {
  public:
+  // MenuItems struct keeps the menu items and meta data for a group of items in
+  // a menu. It takes the ownership of views and passes it to the menu when menu
+  // is constructed.
+  struct MenuItems {
+    MenuItems();
+    MenuItems(MenuItems&&);
+    ~MenuItems();
+
+    enum ItemType {
+      kNone,
+      kTitleCard,
+      kLabel,
+      kButton,
+      kStyledButton,
+      kGeneral
+    };
+
+    std::vector<std::unique_ptr<views::View>> items;
+
+    ItemType first_item_type;
+    ItemType last_item_type;
+    bool different_item_types;
+
+    DISALLOW_COPY_AND_ASSIGN(MenuItems);
+  };
+
+  enum GroupMarginSize { kNone, kTiny, kSmall, kLarge };
+
   // Shows the bubble if one is not already showing.  This allows us to easily
   // make a button toggle the bubble on and off when clicked: we unconditionally
   // call this function when the button is clicked and if the bubble isn't
@@ -58,7 +92,10 @@
 
   static ProfileMenuViewBase* GetBubbleForTesting();
 
-  typedef std::vector<std::unique_ptr<views::View>> MenuItems;
+  // Spacing between the edge of the user menu and the top/bottom or left/right
+  // of the menu items.
+  // TODO(https://crbug.com/934689): Remove after refactoring.
+  int GetMenuEdgeMargin() const;
 
  protected:
   ProfileMenuViewBase(views::Button* anchor_button,
@@ -68,11 +105,34 @@
   ~ProfileMenuViewBase() override;
 
   void Reset();
-  // Adds a set of menu items, either as a |new_group| (using a separator) or
-  // appended to the last added items. Takes ownership of the items and passes
-  // them to the underlying view when menu is built using
-  // |RepopulateViewFromMenuItems|.
-  void AddMenuItems(MenuItems& menu_items, bool new_group);
+
+  // Initializes a new group of menu items. A separator is added before them if
+  // |add_separator| is true.
+  void AddMenuGroup(bool add_separator = true);
+
+  // The following functions add different menu items to the latest menu group.
+  // They pass the ownership of the generated item to |menu_item_groups_| and
+  // return a raw pointer to the object. The ownership is transferred to the
+  // menu when view is repopulated from menu items.
+  // Please use |AddViewItem| only if none of the previous ones match.
+  views::Button* CreateAndAddTitleCard(std::unique_ptr<views::View> icon_view,
+                                       const base::string16& title,
+                                       const base::string16& subtitle,
+                                       bool enabled = true);
+  views::Button* CreateAndAddButton(const gfx::ImageSkia& icon,
+                                    const base::string16& title);
+  views::Button* CreateAndAddBlueButton(const base::string16& text,
+                                        bool md_style);
+#if !defined(OS_CHROMEOS)
+  DiceSigninButtonView* CreateAndAddDiceSigninButton(
+      AccountInfo* account_info = nullptr,
+      gfx::Image* account_icon = nullptr);
+#endif
+  views::Label* CreateAndAddLabel(
+      const base::string16& text,
+      int text_context = views::style::CONTEXT_LABEL);
+  void AddViewItem(std::unique_ptr<views::View> view);
+
   void RepopulateViewFromMenuItems();
 
   Browser* browser() const { return browser_; }
@@ -85,14 +145,8 @@
 
   bool ShouldProvideInitiallyFocusedView() const;
 
-  // TODO(https://crbug.com/934689): Remove menu_width function and make
-  // decisions inside this class.
-  int menu_width() { return menu_width_; }
-
   gfx::ImageSkia CreateVectorIcon(const gfx::VectorIcon& icon);
 
-  // TODO(https://crbug.com/934689): Remove function and make decisions inside
-  // this class.
   int GetDefaultIconSize();
 
  private:
@@ -116,6 +170,12 @@
                               const gfx::Range& range,
                               int event_flags) override;
 
+  // Returns the size of different margin types.
+  int GetMarginSize(GroupMarginSize margin_size) const;
+
+  void AddMenuItemInternal(std::unique_ptr<views::View> view,
+                           MenuItems::ItemType item_type);
+
   Browser* const browser_;
 
   int menu_width_;