Home | History | Annotate | Download | only in autofill
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/views/autofill/autofill_dialog_views.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/location.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/profiles/profile.h"
     13 #include "chrome/browser/ui/autofill/autofill_dialog_sign_in_delegate.h"
     14 #include "chrome/browser/ui/autofill/autofill_dialog_view_delegate.h"
     15 #include "chrome/browser/ui/autofill/loading_animation.h"
     16 #include "chrome/browser/ui/views/autofill/decorated_textfield.h"
     17 #include "chrome/browser/ui/views/autofill/info_bubble.h"
     18 #include "chrome/browser/ui/views/autofill/tooltip_icon.h"
     19 #include "chrome/browser/ui/views/constrained_window_views.h"
     20 #include "components/autofill/content/browser/wallet/wallet_service_url.h"
     21 #include "components/autofill/core/browser/autofill_type.h"
     22 #include "components/web_modal/web_contents_modal_dialog_host.h"
     23 #include "components/web_modal/web_contents_modal_dialog_manager.h"
     24 #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
     25 #include "content/public/browser/native_web_keyboard_event.h"
     26 #include "content/public/browser/navigation_controller.h"
     27 #include "content/public/browser/web_contents.h"
     28 #include "content/public/browser/web_contents_view.h"
     29 #include "grit/theme_resources.h"
     30 #include "grit/ui_resources.h"
     31 #include "third_party/skia/include/core/SkColor.h"
     32 #include "ui/base/l10n/l10n_util.h"
     33 #include "ui/base/models/combobox_model.h"
     34 #include "ui/base/models/menu_model.h"
     35 #include "ui/base/resource/resource_bundle.h"
     36 #include "ui/gfx/animation/animation_delegate.h"
     37 #include "ui/gfx/canvas.h"
     38 #include "ui/gfx/path.h"
     39 #include "ui/gfx/point.h"
     40 #include "ui/gfx/skia_util.h"
     41 #include "ui/views/background.h"
     42 #include "ui/views/border.h"
     43 #include "ui/views/bubble/bubble_border.h"
     44 #include "ui/views/bubble/bubble_frame_view.h"
     45 #include "ui/views/controls/button/blue_button.h"
     46 #include "ui/views/controls/button/checkbox.h"
     47 #include "ui/views/controls/button/label_button.h"
     48 #include "ui/views/controls/button/label_button_border.h"
     49 #include "ui/views/controls/button/menu_button.h"
     50 #include "ui/views/controls/combobox/combobox.h"
     51 #include "ui/views/controls/image_view.h"
     52 #include "ui/views/controls/label.h"
     53 #include "ui/views/controls/link.h"
     54 #include "ui/views/controls/menu/menu_runner.h"
     55 #include "ui/views/controls/separator.h"
     56 #include "ui/views/controls/styled_label.h"
     57 #include "ui/views/controls/textfield/textfield.h"
     58 #include "ui/views/controls/webview/webview.h"
     59 #include "ui/views/layout/box_layout.h"
     60 #include "ui/views/layout/fill_layout.h"
     61 #include "ui/views/layout/grid_layout.h"
     62 #include "ui/views/layout/layout_constants.h"
     63 #include "ui/views/painter.h"
     64 #include "ui/views/widget/widget.h"
     65 #include "ui/views/window/dialog_client_view.h"
     66 
     67 using web_modal::WebContentsModalDialogManager;
     68 using web_modal::WebContentsModalDialogManagerDelegate;
     69 
     70 namespace autofill {
     71 
     72 namespace {
     73 
     74 // The width for the section container.
     75 const int kSectionContainerWidth = 440;
     76 
     77 // The minimum useful height of the contents area of the dialog.
     78 const int kMinimumContentsHeight = 101;
     79 
     80 // The default height of the loading shield, also its minimum size.
     81 const int kInitialLoadingShieldHeight = 150;
     82 
     83 // Horizontal padding between text and other elements (in pixels).
     84 const int kAroundTextPadding = 4;
     85 
     86 // The space between the edges of a notification bar and the text within (in
     87 // pixels).
     88 const int kNotificationPadding = 17;
     89 
     90 // Vertical padding above and below each detail section (in pixels).
     91 const int kDetailSectionVerticalPadding = 10;
     92 
     93 const int kArrowHeight = 7;
     94 const int kArrowWidth = 2 * kArrowHeight;
     95 
     96 // The padding inside the edges of the dialog, in pixels.
     97 const int kDialogEdgePadding = 20;
     98 
     99 // The vertical padding between rows of manual inputs (in pixels).
    100 const int kManualInputRowPadding = 10;
    101 
    102 // Slight shading for mouse hover and legal document background.
    103 SkColor kShadingColor = SkColorSetARGB(7, 0, 0, 0);
    104 
    105 // A border color for the legal document view.
    106 SkColor kSubtleBorderColor = SkColorSetARGB(10, 0, 0, 0);
    107 
    108 // The top and bottom padding, in pixels, for the suggestions menu dropdown
    109 // arrows.
    110 const int kMenuButtonTopInset = 3;
    111 const int kMenuButtonBottomInset = 6;
    112 
    113 // The height in pixels of the padding above and below the overlay message view.
    114 const int kOverlayMessageVerticalPadding = 34;
    115 
    116 // Spacing below image and above text messages in overlay view.
    117 const int kOverlayImageBottomMargin = 100;
    118 
    119 const char kNotificationAreaClassName[] = "autofill/NotificationArea";
    120 const char kOverlayViewClassName[] = "autofill/OverlayView";
    121 const char kSectionContainerClassName[] = "autofill/SectionContainer";
    122 const char kSuggestedButtonClassName[] = "autofill/SuggestedButton";
    123 
    124 // Draws an arrow at the top of |canvas| pointing to |tip_x|.
    125 void DrawArrow(gfx::Canvas* canvas,
    126                int tip_x,
    127                const SkColor& fill_color,
    128                const SkColor& stroke_color) {
    129   const int arrow_half_width = kArrowWidth / 2.0f;
    130 
    131   SkPath arrow;
    132   arrow.moveTo(tip_x - arrow_half_width, kArrowHeight);
    133   arrow.lineTo(tip_x, 0);
    134   arrow.lineTo(tip_x + arrow_half_width, kArrowHeight);
    135 
    136   SkPaint fill_paint;
    137   fill_paint.setColor(fill_color);
    138   canvas->DrawPath(arrow, fill_paint);
    139 
    140   if (stroke_color != SK_ColorTRANSPARENT) {
    141     SkPaint stroke_paint;
    142     stroke_paint.setColor(stroke_color);
    143     stroke_paint.setStyle(SkPaint::kStroke_Style);
    144     canvas->DrawPath(arrow, stroke_paint);
    145   }
    146 }
    147 
    148 // Returns whether |view| is an input (e.g. textfield, combobox).
    149 bool IsInput(views::View* view) {
    150   return view->GetClassName() == DecoratedTextfield::kViewClassName ||
    151          view->GetClassName() == views::Combobox::kViewClassName;
    152 }
    153 
    154 void SelectComboboxValueOrSetToDefault(views::Combobox* combobox,
    155                                        const base::string16& value) {
    156   if (!combobox->SelectValue(value))
    157     combobox->SetSelectedIndex(combobox->model()->GetDefaultIndex());
    158 }
    159 
    160 // This class handles layout for the first row of a SuggestionView.
    161 // It exists to circumvent shortcomings of GridLayout and BoxLayout (namely that
    162 // the former doesn't fully respect child visibility, and that the latter won't
    163 // expand a single child).
    164 class SectionRowView : public views::View {
    165  public:
    166   SectionRowView() {
    167     set_border(views::Border::CreateEmptyBorder(10, 0, 0, 0));
    168   }
    169 
    170   virtual ~SectionRowView() {}
    171 
    172   // views::View implementation:
    173   virtual gfx::Size GetPreferredSize() OVERRIDE {
    174     int height = 0;
    175     int width = 0;
    176     for (int i = 0; i < child_count(); ++i) {
    177       if (child_at(i)->visible()) {
    178         if (width > 0)
    179           width += kAroundTextPadding;
    180 
    181         gfx::Size size = child_at(i)->GetPreferredSize();
    182         height = std::max(height, size.height());
    183         width += size.width();
    184       }
    185     }
    186 
    187     gfx::Insets insets = GetInsets();
    188     return gfx::Size(width + insets.width(), height + insets.height());
    189   }
    190 
    191   virtual void Layout() OVERRIDE {
    192     const gfx::Rect bounds = GetContentsBounds();
    193 
    194     // Icon is left aligned.
    195     int start_x = bounds.x();
    196     views::View* icon = child_at(0);
    197     if (icon->visible()) {
    198       icon->SizeToPreferredSize();
    199       icon->SetX(start_x);
    200       icon->SetY(bounds.y() +
    201           (bounds.height() - icon->bounds().height()) / 2);
    202       start_x += icon->bounds().width() + kAroundTextPadding;
    203     }
    204 
    205     // Textfield is right aligned.
    206     int end_x = bounds.width();
    207     views::View* decorated = child_at(2);
    208     if (decorated->visible()) {
    209       decorated->SizeToPreferredSize();
    210       decorated->SetX(bounds.width() - decorated->bounds().width());
    211       decorated->SetY(bounds.y());
    212       end_x = decorated->bounds().x() - kAroundTextPadding;
    213     }
    214 
    215     // Label takes up all the space in between.
    216     views::View* label = child_at(1);
    217     if (label->visible())
    218       label->SetBounds(start_x, bounds.y(), end_x - start_x, bounds.height());
    219 
    220     views::View::Layout();
    221   }
    222 
    223  private:
    224   DISALLOW_COPY_AND_ASSIGN(SectionRowView);
    225 };
    226 
    227 // A view that propagates visibility and preferred size changes.
    228 class LayoutPropagationView : public views::View {
    229  public:
    230   LayoutPropagationView() {}
    231   virtual ~LayoutPropagationView() {}
    232 
    233  protected:
    234   virtual void ChildVisibilityChanged(views::View* child) OVERRIDE {
    235     PreferredSizeChanged();
    236   }
    237   virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE {
    238     PreferredSizeChanged();
    239   }
    240 
    241  private:
    242   DISALLOW_COPY_AND_ASSIGN(LayoutPropagationView);
    243 };
    244 
    245 // A View for a single notification banner.
    246 class NotificationView : public views::View,
    247                          public views::ButtonListener,
    248                          public views::StyledLabelListener {
    249  public:
    250   NotificationView(const DialogNotification& data,
    251                    AutofillDialogViewDelegate* delegate)
    252       : data_(data),
    253         delegate_(delegate),
    254         checkbox_(NULL) {
    255     scoped_ptr<views::View> label_view;
    256     if (data.HasCheckbox()) {
    257       scoped_ptr<views::Checkbox> checkbox(
    258           new views::Checkbox(base::string16()));
    259       checkbox->SetText(data.display_text());
    260       checkbox->SetTextMultiLine(true);
    261       checkbox->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    262       checkbox->SetTextColor(views::Button::STATE_NORMAL,
    263                              data.GetTextColor());
    264       checkbox->SetTextColor(views::Button::STATE_HOVERED,
    265                              data.GetTextColor());
    266       checkbox->SetChecked(data.checked());
    267       checkbox->set_listener(this);
    268       checkbox_ = checkbox.get();
    269       label_view.reset(checkbox.release());
    270     } else {
    271       scoped_ptr<views::StyledLabel> label(new views::StyledLabel(
    272           data.display_text(), this));
    273       label->set_auto_color_readability_enabled(false);
    274 
    275       views::StyledLabel::RangeStyleInfo text_style;
    276       text_style.color = data.GetTextColor();
    277 
    278       if (data.link_range().is_empty()) {
    279         label->AddStyleRange(gfx::Range(0, data.display_text().size()),
    280                              text_style);
    281       } else {
    282         gfx::Range prefix_range(0, data.link_range().start());
    283         if (!prefix_range.is_empty())
    284           label->AddStyleRange(prefix_range, text_style);
    285 
    286         label->AddStyleRange(
    287             data.link_range(),
    288             views::StyledLabel::RangeStyleInfo::CreateForLink());
    289 
    290         gfx::Range suffix_range(data.link_range().end(),
    291                                 data.display_text().size());
    292         if (!suffix_range.is_empty())
    293           label->AddStyleRange(suffix_range, text_style);
    294       }
    295 
    296       label_view.reset(label.release());
    297     }
    298 
    299     AddChildView(label_view.release());
    300 
    301     if (!data.tooltip_text().empty())
    302       AddChildView(new TooltipIcon(data.tooltip_text()));
    303 
    304     set_background(
    305        views::Background::CreateSolidBackground(data.GetBackgroundColor()));
    306     set_border(views::Border::CreateSolidSidedBorder(1, 0, 1, 0,
    307                                                      data.GetBorderColor()));
    308   }
    309 
    310   virtual ~NotificationView() {}
    311 
    312   views::Checkbox* checkbox() {
    313     return checkbox_;
    314   }
    315 
    316   // views::View implementation.
    317   virtual gfx::Insets GetInsets() const OVERRIDE {
    318     int vertical_padding = kNotificationPadding;
    319     if (checkbox_)
    320       vertical_padding -= 3;
    321     return gfx::Insets(vertical_padding, kDialogEdgePadding,
    322                        vertical_padding, kDialogEdgePadding);
    323   }
    324 
    325   virtual int GetHeightForWidth(int width) OVERRIDE {
    326     int label_width = width - GetInsets().width();
    327     if (child_count() > 1) {
    328       views::View* tooltip_icon = child_at(1);
    329       label_width -= tooltip_icon->GetPreferredSize().width() +
    330           kDialogEdgePadding;
    331     }
    332 
    333     return child_at(0)->GetHeightForWidth(label_width) + GetInsets().height();
    334   }
    335 
    336   virtual void Layout() OVERRIDE {
    337     // Surprisingly, GetContentsBounds() doesn't consult GetInsets().
    338     gfx::Rect bounds = GetLocalBounds();
    339     bounds.Inset(GetInsets());
    340     int right_bound = bounds.right();
    341 
    342     if (child_count() > 1) {
    343       // The icon takes up the entire vertical space and an extra 20px on
    344       // each side. This increases the hover target for the tooltip.
    345       views::View* tooltip_icon = child_at(1);
    346       gfx::Size icon_size = tooltip_icon->GetPreferredSize();
    347       int icon_width = icon_size.width() + kDialogEdgePadding;
    348       right_bound -= icon_width;
    349       tooltip_icon->SetBounds(
    350           right_bound, 0,
    351           icon_width + kDialogEdgePadding, GetLocalBounds().height());
    352     }
    353 
    354     child_at(0)->SetBounds(bounds.x(), bounds.y(),
    355                            right_bound - bounds.x(), bounds.height());
    356   }
    357 
    358   // views::ButtonListener implementation.
    359   virtual void ButtonPressed(views::Button* sender,
    360                              const ui::Event& event) OVERRIDE {
    361     DCHECK_EQ(sender, checkbox_);
    362     delegate_->NotificationCheckboxStateChanged(data_.type(),
    363                                                 checkbox_->checked());
    364   }
    365 
    366   // views::StyledLabelListener implementation.
    367   virtual void StyledLabelLinkClicked(const gfx::Range& range, int event_flags)
    368       OVERRIDE {
    369     delegate_->LinkClicked(data_.link_url());
    370   }
    371 
    372  private:
    373   // The model data for this notification.
    374   DialogNotification data_;
    375 
    376   // The delegate that handles interaction with |this|.
    377   AutofillDialogViewDelegate* delegate_;
    378 
    379   // The checkbox associated with this notification, or NULL if there is none.
    380   views::Checkbox* checkbox_;
    381 
    382   DISALLOW_COPY_AND_ASSIGN(NotificationView);
    383 };
    384 
    385 // A view that displays a loading message with some dancing dots.
    386 class LoadingAnimationView : public views::View,
    387                              public gfx::AnimationDelegate {
    388  public:
    389   explicit LoadingAnimationView(const base::string16& text) :
    390       container_(new views::View()) {
    391     AddChildView(container_);
    392     container_->SetLayoutManager(
    393         new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
    394 
    395     gfx::Font font = ui::ResourceBundle::GetSharedInstance().GetFont(
    396         ui::ResourceBundle::BaseFont).DeriveFont(8);
    397     animation_.reset(new LoadingAnimation(this, font.GetHeight()));
    398 
    399     views::Label* label = new views::Label();
    400     label->SetText(text);
    401     label->SetFont(font);
    402     container_->AddChildView(label);
    403 
    404     for (size_t i = 0; i < 3; ++i) {
    405       views::Label* dot = new views::Label();
    406       dot->SetText(ASCIIToUTF16("."));
    407       dot->SetFont(font);
    408       container_->AddChildView(dot);
    409     }
    410 
    411     OnNativeThemeChanged(GetNativeTheme());
    412   }
    413 
    414   virtual ~LoadingAnimationView() {}
    415 
    416   // views::View implementation.
    417   virtual void SetVisible(bool visible) OVERRIDE {
    418     if (visible)
    419       animation_->Start();
    420     else
    421       animation_->Reset();
    422 
    423     views::View::SetVisible(visible);
    424   }
    425 
    426   virtual void Layout() OVERRIDE {
    427     gfx::Size container_size = container_->GetPreferredSize();
    428     gfx::Rect container_bounds((width() - container_size.width()) / 2,
    429                                (height() - container_size.height()) / 2,
    430                                container_size.width(),
    431                                container_size.height());
    432     container_->SetBoundsRect(container_bounds);
    433     container_->Layout();
    434 
    435     for (size_t i = 0; i < 3; ++i) {
    436       views::View* dot = container_->child_at(i + 1);
    437       dot->SetY(dot->y() + animation_->GetCurrentValueForDot(i));
    438     }
    439   }
    440 
    441   virtual void OnNativeThemeChanged(const ui::NativeTheme* theme) OVERRIDE {
    442     set_background(views::Background::CreateSolidBackground(
    443         theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
    444   }
    445 
    446   // gfx::AnimationDelegate implementation.
    447   virtual void AnimationProgressed(const gfx::Animation* animation) OVERRIDE {
    448     DCHECK_EQ(animation, animation_.get());
    449     Layout();
    450   }
    451 
    452  private:
    453   // Contains the "Loading" label and the dots.
    454   views::View* container_;
    455 
    456   scoped_ptr<LoadingAnimation> animation_;
    457 
    458   DISALLOW_COPY_AND_ASSIGN(LoadingAnimationView);
    459 };
    460 
    461 }  // namespace
    462 
    463 // AutofillDialogViews::AccountChooser -----------------------------------------
    464 
    465 AutofillDialogViews::AccountChooser::AccountChooser(
    466     AutofillDialogViewDelegate* delegate)
    467     : image_(new views::ImageView()),
    468       menu_button_(new views::MenuButton(NULL, base::string16(), this, true)),
    469       link_(new views::Link()),
    470       delegate_(delegate) {
    471   set_border(views::Border::CreateEmptyBorder(0, 0, 0, 10));
    472   SetLayoutManager(
    473       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0,
    474                            kAroundTextPadding));
    475   AddChildView(image_);
    476 
    477   menu_button_->set_background(NULL);
    478   menu_button_->set_border(NULL);
    479   gfx::Insets insets = GetInsets();
    480   menu_button_->SetFocusPainter(
    481       views::Painter::CreateDashedFocusPainterWithInsets(insets));
    482   menu_button_->SetFocusable(true);
    483   AddChildView(menu_button_);
    484 
    485   link_->set_listener(this);
    486   AddChildView(link_);
    487 }
    488 
    489 AutofillDialogViews::AccountChooser::~AccountChooser() {}
    490 
    491 void AutofillDialogViews::AccountChooser::Update() {
    492   SetVisible(delegate_->ShouldShowAccountChooser());
    493 
    494   gfx::Image icon = delegate_->AccountChooserImage();
    495   image_->SetImage(icon.AsImageSkia());
    496   menu_button_->SetText(delegate_->AccountChooserText());
    497   // This allows the button to shrink if the new text is smaller.
    498   menu_button_->ClearMaxTextSize();
    499 
    500   bool show_link = !delegate_->MenuModelForAccountChooser();
    501   menu_button_->SetVisible(!show_link);
    502   link_->SetText(delegate_->SignInLinkText());
    503   link_->SetVisible(show_link);
    504 
    505   menu_runner_.reset();
    506 
    507   PreferredSizeChanged();
    508 }
    509 
    510 void AutofillDialogViews::AccountChooser::OnMenuButtonClicked(
    511     views::View* source,
    512     const gfx::Point& point) {
    513   DCHECK_EQ(menu_button_, source);
    514 
    515   ui::MenuModel* model = delegate_->MenuModelForAccountChooser();
    516   if (!model)
    517     return;
    518 
    519   menu_runner_.reset(new views::MenuRunner(model));
    520   if (menu_runner_->RunMenuAt(source->GetWidget(),
    521                               NULL,
    522                               source->GetBoundsInScreen(),
    523                               views::MenuItemView::TOPRIGHT,
    524                               ui::MENU_SOURCE_NONE,
    525                               0) == views::MenuRunner::MENU_DELETED) {
    526     return;
    527   }
    528 }
    529 
    530 views::View* AutofillDialogViews::GetLoadingShieldForTesting() {
    531   return loading_shield_;
    532 }
    533 
    534 views::WebView* AutofillDialogViews::GetSignInWebViewForTesting() {
    535   return sign_in_web_view_;
    536 }
    537 
    538 views::View* AutofillDialogViews::GetNotificationAreaForTesting() {
    539   return notification_area_;
    540 }
    541 
    542 views::View* AutofillDialogViews::GetScrollableAreaForTesting() {
    543   return scrollable_area_;
    544 }
    545 
    546 void AutofillDialogViews::AccountChooser::LinkClicked(views::Link* source,
    547                                                       int event_flags) {
    548   delegate_->SignInLinkClicked();
    549 }
    550 
    551 // AutofillDialogViews::OverlayView --------------------------------------------
    552 
    553 AutofillDialogViews::OverlayView::OverlayView(
    554     AutofillDialogViewDelegate* delegate)
    555     : delegate_(delegate),
    556       image_view_(new views::ImageView()),
    557       message_view_(new views::Label()) {
    558   message_view_->SetAutoColorReadabilityEnabled(false);
    559   message_view_->SetMultiLine(true);
    560 
    561   AddChildView(image_view_);
    562   AddChildView(message_view_);
    563 
    564   OnNativeThemeChanged(GetNativeTheme());
    565 }
    566 
    567 AutofillDialogViews::OverlayView::~OverlayView() {}
    568 
    569 int AutofillDialogViews::OverlayView::GetHeightForContentsForWidth(int width) {
    570   // In this case, 0 means "no preference".
    571   if (!message_view_->visible())
    572     return 0;
    573 
    574   return kOverlayImageBottomMargin +
    575       views::kButtonVEdgeMarginNew +
    576       message_view_->GetHeightForWidth(width) +
    577       image_view_->GetHeightForWidth(width);
    578 }
    579 
    580 void AutofillDialogViews::OverlayView::UpdateState() {
    581   const DialogOverlayState& state = delegate_->GetDialogOverlay();
    582 
    583   if (state.image.IsEmpty()) {
    584     SetVisible(false);
    585     return;
    586   }
    587 
    588   image_view_->SetImage(state.image.ToImageSkia());
    589 
    590   message_view_->SetVisible(!state.string.text.empty());
    591   message_view_->SetText(state.string.text);
    592   message_view_->SetFont(state.string.font);
    593   message_view_->SetEnabledColor(state.string.text_color);
    594   message_view_->set_border(
    595       views::Border::CreateEmptyBorder(kOverlayMessageVerticalPadding,
    596                                        kDialogEdgePadding,
    597                                        kOverlayMessageVerticalPadding,
    598                                        kDialogEdgePadding));
    599 
    600   SetVisible(true);
    601 }
    602 
    603 gfx::Insets AutofillDialogViews::OverlayView::GetInsets() const {
    604   return gfx::Insets(12, 12, 12, 12);
    605 }
    606 
    607 void AutofillDialogViews::OverlayView::Layout() {
    608   gfx::Rect bounds = ContentBoundsSansBubbleBorder();
    609   if (!message_view_->visible()) {
    610     image_view_->SetBoundsRect(bounds);
    611     return;
    612   }
    613 
    614   int message_height = message_view_->GetHeightForWidth(bounds.width());
    615   int y = bounds.bottom() - message_height;
    616   message_view_->SetBounds(bounds.x(), y, bounds.width(), message_height);
    617 
    618   gfx::Size image_size = image_view_->GetPreferredSize();
    619   y -= image_size.height() + kOverlayImageBottomMargin;
    620   image_view_->SetBounds(bounds.x(), y, bounds.width(), image_size.height());
    621 }
    622 
    623 const char* AutofillDialogViews::OverlayView::GetClassName() const {
    624   return kOverlayViewClassName;
    625 }
    626 
    627 void AutofillDialogViews::OverlayView::OnPaint(gfx::Canvas* canvas) {
    628   // BubbleFrameView doesn't mask the window, it just draws the border via
    629   // image assets. Match that rounding here.
    630   gfx::Rect rect = ContentBoundsSansBubbleBorder();
    631   const SkScalar kCornerRadius = SkIntToScalar(
    632       GetBubbleBorder() ? GetBubbleBorder()->GetBorderCornerRadius() : 2);
    633   gfx::Path window_mask;
    634   window_mask.addRoundRect(gfx::RectToSkRect(rect),
    635                            kCornerRadius, kCornerRadius);
    636   canvas->ClipPath(window_mask);
    637 
    638   OnPaintBackground(canvas);
    639 
    640   // Draw the arrow, border, and fill for the bottom area.
    641   if (message_view_->visible()) {
    642     const int arrow_half_width = kArrowWidth / 2.0f;
    643     SkPath arrow;
    644     int y = message_view_->y() - 1;
    645     // Note that we purposely draw slightly outside of |rect| so that the
    646     // stroke is hidden on the sides.
    647     arrow.moveTo(rect.x() - 1, y);
    648     arrow.rLineTo(rect.width() / 2 - arrow_half_width, 0);
    649     arrow.rLineTo(arrow_half_width, -kArrowHeight);
    650     arrow.rLineTo(arrow_half_width, kArrowHeight);
    651     arrow.lineTo(rect.right() + 1, y);
    652     arrow.lineTo(rect.right() + 1, rect.bottom() + 1);
    653     arrow.lineTo(rect.x() - 1, rect.bottom() + 1);
    654     arrow.close();
    655 
    656     SkPaint paint;
    657     paint.setColor(kShadingColor);
    658     paint.setStyle(SkPaint::kFill_Style);
    659     canvas->DrawPath(arrow, paint);
    660     paint.setColor(kSubtleBorderColor);
    661     paint.setStyle(SkPaint::kStroke_Style);
    662     canvas->DrawPath(arrow, paint);
    663   }
    664 
    665   PaintChildren(canvas);
    666 }
    667 
    668 void AutofillDialogViews::OverlayView::OnNativeThemeChanged(
    669     const ui::NativeTheme* theme) {
    670   set_background(views::Background::CreateSolidBackground(
    671       theme->GetSystemColor(ui::NativeTheme::kColorId_DialogBackground)));
    672 }
    673 
    674 views::BubbleBorder* AutofillDialogViews::OverlayView::GetBubbleBorder() {
    675   views::View* frame = GetWidget()->non_client_view()->frame_view();
    676   std::string bubble_frame_view_name(views::BubbleFrameView::kViewClassName);
    677   if (frame->GetClassName() == bubble_frame_view_name)
    678     return static_cast<views::BubbleFrameView*>(frame)->bubble_border();
    679   NOTREACHED();
    680   return NULL;
    681 }
    682 
    683 gfx::Rect AutofillDialogViews::OverlayView::ContentBoundsSansBubbleBorder() {
    684   gfx::Rect bounds = GetContentsBounds();
    685   int bubble_width = 5;
    686   if (GetBubbleBorder())
    687     bubble_width = GetBubbleBorder()->GetBorderThickness();
    688   bounds.Inset(bubble_width, bubble_width, bubble_width, bubble_width);
    689   return bounds;
    690 }
    691 
    692 // AutofillDialogViews::NotificationArea ---------------------------------------
    693 
    694 AutofillDialogViews::NotificationArea::NotificationArea(
    695     AutofillDialogViewDelegate* delegate)
    696     : delegate_(delegate) {
    697   // Reserve vertical space for the arrow (regardless of whether one exists).
    698   // The -1 accounts for the border.
    699   set_border(views::Border::CreateEmptyBorder(kArrowHeight - 1, 0, 0, 0));
    700 
    701   views::BoxLayout* box_layout =
    702       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0);
    703   SetLayoutManager(box_layout);
    704 }
    705 
    706 AutofillDialogViews::NotificationArea::~NotificationArea() {}
    707 
    708 void AutofillDialogViews::NotificationArea::SetNotifications(
    709     const std::vector<DialogNotification>& notifications) {
    710   notifications_ = notifications;
    711 
    712   RemoveAllChildViews(true);
    713 
    714   if (notifications_.empty())
    715     return;
    716 
    717   for (size_t i = 0; i < notifications_.size(); ++i) {
    718     const DialogNotification& notification = notifications_[i];
    719     scoped_ptr<NotificationView> view(new NotificationView(notification,
    720                                                            delegate_));
    721 
    722     AddChildView(view.release());
    723   }
    724 
    725   PreferredSizeChanged();
    726 }
    727 
    728 gfx::Size AutofillDialogViews::NotificationArea::GetPreferredSize() {
    729   gfx::Size size = views::View::GetPreferredSize();
    730   // Ensure that long notifications wrap and don't enlarge the dialog.
    731   size.set_width(1);
    732   return size;
    733 }
    734 
    735 const char* AutofillDialogViews::NotificationArea::GetClassName() const {
    736   return kNotificationAreaClassName;
    737 }
    738 
    739 void AutofillDialogViews::NotificationArea::PaintChildren(
    740     gfx::Canvas* canvas) {}
    741 
    742 void AutofillDialogViews::NotificationArea::OnPaint(gfx::Canvas* canvas) {
    743   views::View::OnPaint(canvas);
    744   views::View::PaintChildren(canvas);
    745 
    746   if (HasArrow()) {
    747     DrawArrow(
    748         canvas,
    749         GetMirroredXInView(width() - arrow_centering_anchor_->width() / 2.0f),
    750         notifications_[0].GetBackgroundColor(),
    751         notifications_[0].GetBorderColor());
    752   }
    753 }
    754 
    755 void AutofillDialogViews::OnWidgetClosing(views::Widget* widget) {
    756   observer_.Remove(widget);
    757   if (error_bubble_ && error_bubble_->GetWidget() == widget)
    758     error_bubble_ = NULL;
    759 }
    760 
    761 void AutofillDialogViews::OnWidgetBoundsChanged(views::Widget* widget,
    762                                                 const gfx::Rect& new_bounds) {
    763   // Notify the web contents of its new auto-resize limits.
    764   if (sign_in_delegate_ && sign_in_web_view_->visible()) {
    765     sign_in_delegate_->UpdateLimitsAndEnableAutoResize(
    766         GetMinimumSignInViewSize(), GetMaximumSignInViewSize());
    767   }
    768   HideErrorBubble();
    769 }
    770 
    771 bool AutofillDialogViews::NotificationArea::HasArrow() {
    772   return !notifications_.empty() && notifications_[0].HasArrow() &&
    773       arrow_centering_anchor_.get();
    774 }
    775 
    776 // AutofillDialogViews::SectionContainer ---------------------------------------
    777 
    778 AutofillDialogViews::SectionContainer::SectionContainer(
    779     const base::string16& label,
    780     views::View* controls,
    781     views::Button* proxy_button)
    782     : proxy_button_(proxy_button),
    783       forward_mouse_events_(false) {
    784   set_notify_enter_exit_on_child(true);
    785 
    786   set_border(views::Border::CreateEmptyBorder(kDetailSectionVerticalPadding,
    787                                               kDialogEdgePadding,
    788                                               kDetailSectionVerticalPadding,
    789                                               kDialogEdgePadding));
    790 
    791   // TODO(estade): this label should be semi-bold.
    792   views::Label* label_view = new views::Label(label);
    793   label_view->SetHorizontalAlignment(gfx::ALIGN_LEFT);
    794 
    795   views::View* label_bar = new views::View();
    796   views::GridLayout* label_bar_layout = new views::GridLayout(label_bar);
    797   label_bar->SetLayoutManager(label_bar_layout);
    798   const int kColumnSetId = 0;
    799   views::ColumnSet* columns = label_bar_layout->AddColumnSet(kColumnSetId);
    800   columns->AddColumn(
    801       views::GridLayout::LEADING,
    802       views::GridLayout::LEADING,
    803       0,
    804       views::GridLayout::FIXED,
    805       kSectionContainerWidth - proxy_button->GetPreferredSize().width(),
    806       0);
    807   columns->AddColumn(views::GridLayout::LEADING,
    808                      views::GridLayout::LEADING,
    809                      0,
    810                      views::GridLayout::USE_PREF,
    811                      0,
    812                      0);
    813   label_bar_layout->StartRow(0, kColumnSetId);
    814   label_bar_layout->AddView(label_view);
    815   label_bar_layout->AddView(proxy_button);
    816 
    817   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
    818   AddChildView(label_bar);
    819   AddChildView(controls);
    820 }
    821 
    822 AutofillDialogViews::SectionContainer::~SectionContainer() {}
    823 
    824 void AutofillDialogViews::SectionContainer::SetActive(bool active) {
    825   bool is_active = active && proxy_button_->visible();
    826   if (is_active == !!background())
    827     return;
    828 
    829   set_background(is_active ?
    830       views::Background::CreateSolidBackground(kShadingColor) :
    831       NULL);
    832   SchedulePaint();
    833 }
    834 
    835 void AutofillDialogViews::SectionContainer::SetForwardMouseEvents(
    836     bool forward) {
    837   forward_mouse_events_ = forward;
    838   if (!forward)
    839     set_background(NULL);
    840 }
    841 
    842 const char* AutofillDialogViews::SectionContainer::GetClassName() const {
    843   return kSectionContainerClassName;
    844 }
    845 
    846 void AutofillDialogViews::SectionContainer::OnMouseMoved(
    847     const ui::MouseEvent& event) {
    848   SetActive(ShouldForwardEvent(event));
    849 }
    850 
    851 void AutofillDialogViews::SectionContainer::OnMouseEntered(
    852     const ui::MouseEvent& event) {
    853   if (!ShouldForwardEvent(event))
    854     return;
    855 
    856   SetActive(true);
    857   proxy_button_->OnMouseEntered(ProxyEvent(event));
    858   SchedulePaint();
    859 }
    860 
    861 void AutofillDialogViews::SectionContainer::OnMouseExited(
    862     const ui::MouseEvent& event) {
    863   SetActive(false);
    864   if (!ShouldForwardEvent(event))
    865     return;
    866 
    867   proxy_button_->OnMouseExited(ProxyEvent(event));
    868   SchedulePaint();
    869 }
    870 
    871 bool AutofillDialogViews::SectionContainer::OnMousePressed(
    872     const ui::MouseEvent& event) {
    873   if (!ShouldForwardEvent(event))
    874     return false;
    875 
    876   return proxy_button_->OnMousePressed(ProxyEvent(event));
    877 }
    878 
    879 void AutofillDialogViews::SectionContainer::OnMouseReleased(
    880     const ui::MouseEvent& event) {
    881   if (!ShouldForwardEvent(event))
    882     return;
    883 
    884   proxy_button_->OnMouseReleased(ProxyEvent(event));
    885 }
    886 
    887 void AutofillDialogViews::SectionContainer::OnGestureEvent(
    888     ui::GestureEvent* event) {
    889   if (!ShouldForwardEvent(*event))
    890     return;
    891 
    892   proxy_button_->OnGestureEvent(event);
    893 }
    894 
    895 views::View* AutofillDialogViews::SectionContainer::GetEventHandlerForRect(
    896     const gfx::Rect& rect) {
    897   // TODO(tdanderson): Modify this function to support rect-based event
    898   // targeting.
    899 
    900   views::View* handler = views::View::GetEventHandlerForRect(rect);
    901   // If the event is not in the label bar and there's no background to be
    902   // cleared, let normal event handling take place.
    903   if (!background() &&
    904       rect.CenterPoint().y() > child_at(0)->bounds().bottom()) {
    905     return handler;
    906   }
    907 
    908   // Special case for (CVC) inputs in the suggestion view.
    909   if (forward_mouse_events_ &&
    910       handler->GetAncestorWithClassName(DecoratedTextfield::kViewClassName)) {
    911     return handler;
    912   }
    913 
    914   // Special case for the proxy button itself.
    915   if (handler == proxy_button_)
    916     return handler;
    917 
    918   return this;
    919 }
    920 
    921 // static
    922 ui::MouseEvent AutofillDialogViews::SectionContainer::ProxyEvent(
    923     const ui::MouseEvent& event) {
    924   ui::MouseEvent event_copy = event;
    925   event_copy.set_location(gfx::Point());
    926   return event_copy;
    927 }
    928 
    929 bool AutofillDialogViews::SectionContainer::ShouldForwardEvent(
    930     const ui::LocatedEvent& event) {
    931   // Always forward events on the label bar.
    932   return forward_mouse_events_ || event.y() <= child_at(0)->bounds().bottom();
    933 }
    934 
    935 // AutofillDialogViews::SuggestedButton ----------------------------------------
    936 
    937 AutofillDialogViews::SuggestedButton::SuggestedButton(
    938     views::MenuButtonListener* listener)
    939     : views::MenuButton(NULL, base::string16(), listener, false) {
    940   const int kFocusBorderWidth = 1;
    941   set_border(views::Border::CreateEmptyBorder(kMenuButtonTopInset,
    942                                               kDialogEdgePadding,
    943                                               kMenuButtonBottomInset,
    944                                               kFocusBorderWidth));
    945   gfx::Insets insets = GetInsets();
    946   insets += gfx::Insets(-kFocusBorderWidth, -kFocusBorderWidth,
    947                         -kFocusBorderWidth, -kFocusBorderWidth);
    948   SetFocusPainter(
    949       views::Painter::CreateDashedFocusPainterWithInsets(insets));
    950   SetFocusable(true);
    951 }
    952 
    953 AutofillDialogViews::SuggestedButton::~SuggestedButton() {}
    954 
    955 gfx::Size AutofillDialogViews::SuggestedButton::GetPreferredSize() {
    956   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    957   gfx::Size size = rb.GetImageNamed(ResourceIDForState()).Size();
    958   const gfx::Insets insets = GetInsets();
    959   size.Enlarge(insets.width(), insets.height());
    960   return size;
    961 }
    962 
    963 const char* AutofillDialogViews::SuggestedButton::GetClassName() const {
    964   return kSuggestedButtonClassName;
    965 }
    966 
    967 void AutofillDialogViews::SuggestedButton::PaintChildren(gfx::Canvas* canvas) {}
    968 
    969 void AutofillDialogViews::SuggestedButton::OnPaint(gfx::Canvas* canvas) {
    970   ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
    971   const gfx::Insets insets = GetInsets();
    972   canvas->DrawImageInt(*rb.GetImageSkiaNamed(ResourceIDForState()),
    973                        insets.left(), insets.top());
    974   views::Painter::PaintFocusPainter(this, canvas, focus_painter());
    975 }
    976 
    977 int AutofillDialogViews::SuggestedButton::ResourceIDForState() const {
    978   views::Button::ButtonState button_state = state();
    979   if (button_state == views::Button::STATE_PRESSED)
    980     return IDR_AUTOFILL_DIALOG_MENU_BUTTON_P;
    981   else if (button_state == views::Button::STATE_HOVERED)
    982     return IDR_AUTOFILL_DIALOG_MENU_BUTTON_H;
    983   else if (button_state == views::Button::STATE_DISABLED)
    984     return IDR_AUTOFILL_DIALOG_MENU_BUTTON_D;
    985   DCHECK_EQ(views::Button::STATE_NORMAL, button_state);
    986   return IDR_AUTOFILL_DIALOG_MENU_BUTTON;
    987 }
    988 
    989 // AutofillDialogViews::DetailsContainerView -----------------------------------
    990 
    991 AutofillDialogViews::DetailsContainerView::DetailsContainerView(
    992     const base::Closure& callback)
    993     : bounds_changed_callback_(callback),
    994       ignore_layouts_(false) {}
    995 
    996 AutofillDialogViews::DetailsContainerView::~DetailsContainerView() {}
    997 
    998 void AutofillDialogViews::DetailsContainerView::OnBoundsChanged(
    999     const gfx::Rect& previous_bounds) {
   1000   bounds_changed_callback_.Run();
   1001 }
   1002 
   1003 void AutofillDialogViews::DetailsContainerView::Layout() {
   1004   if (!ignore_layouts_)
   1005     views::View::Layout();
   1006 }
   1007 
   1008 // AutofillDialogViews::SuggestionView -----------------------------------------
   1009 
   1010 AutofillDialogViews::SuggestionView::SuggestionView(
   1011     AutofillDialogViews* autofill_dialog)
   1012     : label_(new views::Label()),
   1013       label_line_2_(new views::Label()),
   1014       icon_(new views::ImageView()),
   1015       decorated_(
   1016           new DecoratedTextfield(base::string16(),
   1017                                  base::string16(),
   1018                                  autofill_dialog)) {
   1019   // TODO(estade): Make this the correct color.
   1020   set_border(
   1021       views::Border::CreateSolidSidedBorder(1, 0, 0, 0, SK_ColorLTGRAY));
   1022 
   1023   SectionRowView* label_container = new SectionRowView();
   1024   AddChildView(label_container);
   1025 
   1026   // Label and icon.
   1027   label_container->AddChildView(icon_);
   1028   label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   1029   label_container->AddChildView(label_);
   1030 
   1031   // TODO(estade): get the sizing and spacing right on this textfield.
   1032   decorated_->SetVisible(false);
   1033   decorated_->set_default_width_in_chars(15);
   1034   label_container->AddChildView(decorated_);
   1035 
   1036   label_line_2_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
   1037   label_line_2_->SetVisible(false);
   1038   label_line_2_->SetLineHeight(22);
   1039   label_line_2_->SetMultiLine(true);
   1040   AddChildView(label_line_2_);
   1041 
   1042   SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 7));
   1043 }
   1044 
   1045 AutofillDialogViews::SuggestionView::~SuggestionView() {}
   1046 
   1047 gfx::Size AutofillDialogViews::SuggestionView::GetPreferredSize() {
   1048   // There's no preferred width. The parent's layout should get the preferred
   1049   // height from GetHeightForWidth().
   1050   return gfx::Size();
   1051 }
   1052 
   1053 int AutofillDialogViews::SuggestionView::GetHeightForWidth(int width) {
   1054   int height = 0;
   1055   CanUseVerticallyCompactText(width, &height);
   1056   return height;
   1057 }
   1058 
   1059 bool AutofillDialogViews::SuggestionView::CanUseVerticallyCompactText(
   1060     int available_width,
   1061     int* resulting_height) {
   1062   // This calculation may be costly, avoid doing it more than once per width.
   1063   if (!calculated_heights_.count(available_width)) {
   1064     // Changing the state of |this| now will lead to extra layouts and
   1065     // paints we don't want, so create another SuggestionView to calculate
   1066     // which label we have room to show.
   1067     SuggestionView sizing_view(NULL);
   1068     sizing_view.SetLabelText(state_.vertically_compact_text);
   1069     sizing_view.SetIcon(state_.icon);
   1070     sizing_view.SetTextfield(state_.extra_text, state_.extra_icon);
   1071     sizing_view.label_->SetSize(gfx::Size(available_width, 0));
   1072     sizing_view.label_line_2_->SetSize(gfx::Size(available_width, 0));
   1073 
   1074     // Shortcut |sizing_view|'s GetHeightForWidth() to avoid an infinite loop.
   1075     // Its BoxLayout must do these calculations for us.
   1076     views::LayoutManager* layout = sizing_view.GetLayoutManager();
   1077     if (layout->GetPreferredSize(&sizing_view).width() <= available_width) {
   1078       calculated_heights_[available_width] = std::make_pair(
   1079           true,
   1080           layout->GetPreferredHeightForWidth(&sizing_view, available_width));
   1081     } else {
   1082       sizing_view.SetLabelText(state_.horizontally_compact_text);
   1083       calculated_heights_[available_width] = std::make_pair(
   1084           false,
   1085           layout->GetPreferredHeightForWidth(&sizing_view, available_width));
   1086     }
   1087   }
   1088 
   1089   const std::pair<bool, int>& values = calculated_heights_[available_width];
   1090   *resulting_height = values.second;
   1091   return values.first;
   1092 }
   1093 
   1094 void AutofillDialogViews::SuggestionView::OnBoundsChanged(
   1095     const gfx::Rect& previous_bounds) {
   1096   int unused;
   1097   SetLabelText(CanUseVerticallyCompactText(width(), &unused) ?
   1098       state_.vertically_compact_text :
   1099       state_.horizontally_compact_text);
   1100 }
   1101 
   1102 void AutofillDialogViews::SuggestionView::SetState(
   1103     const SuggestionState& state) {
   1104   calculated_heights_.clear();
   1105   state_ = state;
   1106   SetVisible(state_.visible);
   1107   // Set to the more compact text for now. |this| will optionally switch to
   1108   // the more vertically expanded view when the bounds are set.
   1109   SetLabelText(state_.vertically_compact_text);
   1110   SetIcon(state_.icon);
   1111   SetTextfield(state_.extra_text, state_.extra_icon);
   1112   PreferredSizeChanged();
   1113 }
   1114 
   1115 void AutofillDialogViews::SuggestionView::SetLabelText(
   1116     const base::string16& text) {
   1117   // TODO(estade): does this localize well?
   1118   base::string16 line_return(ASCIIToUTF16("\n"));
   1119   size_t position = text.find(line_return);
   1120   if (position == base::string16::npos) {
   1121     label_->SetText(text);
   1122     label_line_2_->SetVisible(false);
   1123   } else {
   1124     label_->SetText(text.substr(0, position));
   1125     label_line_2_->SetText(text.substr(position + line_return.length()));
   1126     label_line_2_->SetVisible(true);
   1127   }
   1128 }
   1129 
   1130 void AutofillDialogViews::SuggestionView::SetIcon(
   1131     const gfx::Image& image) {
   1132   icon_->SetVisible(!image.IsEmpty());
   1133   icon_->SetImage(image.AsImageSkia());
   1134 }
   1135 
   1136 void AutofillDialogViews::SuggestionView::SetTextfield(
   1137     const base::string16& placeholder_text,
   1138     const gfx::Image& icon) {
   1139   decorated_->set_placeholder_text(placeholder_text);
   1140   decorated_->SetIcon(icon);
   1141   decorated_->SetVisible(!placeholder_text.empty());
   1142 }
   1143 
   1144 // AutofillDialogView ----------------------------------------------------------
   1145 
   1146 // static
   1147 AutofillDialogView* AutofillDialogView::Create(
   1148     AutofillDialogViewDelegate* delegate) {
   1149   return new AutofillDialogViews(delegate);
   1150 }
   1151 
   1152 // AutofillDialogViews ---------------------------------------------------------
   1153 
   1154 AutofillDialogViews::AutofillDialogViews(AutofillDialogViewDelegate* delegate)
   1155     : delegate_(delegate),
   1156       updates_scope_(0),
   1157       needs_update_(false),
   1158       window_(NULL),
   1159       notification_area_(NULL),
   1160       account_chooser_(NULL),
   1161       sign_in_web_view_(NULL),
   1162       scrollable_area_(NULL),
   1163       details_container_(NULL),
   1164       loading_shield_(NULL),
   1165       loading_shield_height_(0),
   1166       overlay_view_(NULL),
   1167       button_strip_extra_view_(NULL),
   1168       save_in_chrome_checkbox_(NULL),
   1169       save_in_chrome_checkbox_container_(NULL),
   1170       button_strip_image_(NULL),
   1171       footnote_view_(NULL),
   1172       legal_document_view_(NULL),
   1173       focus_manager_(NULL),
   1174       error_bubble_(NULL),
   1175       observer_(this) {
   1176   DCHECK(delegate);
   1177   detail_groups_.insert(std::make_pair(SECTION_CC,
   1178                                        DetailsGroup(SECTION_CC)));
   1179   detail_groups_.insert(std::make_pair(SECTION_BILLING,
   1180                                        DetailsGroup(SECTION_BILLING)));
   1181   detail_groups_.insert(std::make_pair(SECTION_CC_BILLING,
   1182                                        DetailsGroup(SECTION_CC_BILLING)));
   1183   detail_groups_.insert(std::make_pair(SECTION_SHIPPING,
   1184                                        DetailsGroup(SECTION_SHIPPING)));
   1185 }
   1186 
   1187 AutofillDialogViews::~AutofillDialogViews() {
   1188   HideErrorBubble();
   1189   DCHECK(!window_);
   1190 }
   1191 
   1192 void AutofillDialogViews::Show() {
   1193   InitChildViews();
   1194   UpdateAccountChooser();
   1195   UpdateNotificationArea();
   1196   UpdateButtonStripExtraView();
   1197 
   1198   // Ownership of |contents_| is handed off by this call. The widget will take
   1199   // care of deleting itself after calling DeleteDelegate().
   1200   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
   1201       WebContentsModalDialogManager::FromWebContents(
   1202           delegate_->GetWebContents());
   1203   WebContentsModalDialogManagerDelegate* modal_delegate =
   1204       web_contents_modal_dialog_manager->delegate();
   1205   DCHECK(modal_delegate);
   1206   window_ = views::Widget::CreateWindowAsFramelessChild(
   1207       this, modal_delegate->GetWebContentsModalDialogHost()->GetHostView());
   1208   web_contents_modal_dialog_manager->ShowDialog(window_->GetNativeView());
   1209   focus_manager_ = window_->GetFocusManager();
   1210   focus_manager_->AddFocusChangeListener(this);
   1211 
   1212   ShowDialogInMode(DETAIL_INPUT);
   1213 
   1214   // Listen for size changes on the browser.
   1215   views::Widget* browser_widget =
   1216       views::Widget::GetTopLevelWidgetForNativeView(
   1217           delegate_->GetWebContents()->GetView()->GetNativeView());
   1218   observer_.Add(browser_widget);
   1219 }
   1220 
   1221 void AutofillDialogViews::Hide() {
   1222   if (window_)
   1223     window_->Close();
   1224 }
   1225 
   1226 void AutofillDialogViews::UpdatesStarted() {
   1227   updates_scope_++;
   1228 }
   1229 
   1230 void AutofillDialogViews::UpdatesFinished() {
   1231   updates_scope_--;
   1232   DCHECK_GE(updates_scope_, 0);
   1233   if (updates_scope_ == 0 && needs_update_) {
   1234     needs_update_ = false;
   1235     ContentsPreferredSizeChanged();
   1236   }
   1237 }
   1238 
   1239 void AutofillDialogViews::UpdateAccountChooser() {
   1240   account_chooser_->Update();
   1241 
   1242   bool show_loading = delegate_->ShouldShowSpinner();
   1243   if (show_loading != loading_shield_->visible()) {
   1244     if (show_loading) {
   1245       loading_shield_height_ = std::max(kInitialLoadingShieldHeight,
   1246                                         GetContentsBounds().height());
   1247       ShowDialogInMode(LOADING);
   1248     } else {
   1249       bool show_sign_in = delegate_->ShouldShowSignInWebView();
   1250       ShowDialogInMode(show_sign_in ? SIGN_IN : DETAIL_INPUT);
   1251     }
   1252 
   1253     InvalidateLayout();
   1254     ContentsPreferredSizeChanged();
   1255   }
   1256 
   1257   // Update legal documents for the account.
   1258   if (footnote_view_) {
   1259     const base::string16 text = delegate_->LegalDocumentsText();
   1260     legal_document_view_->SetText(text);
   1261 
   1262     if (!text.empty()) {
   1263       const std::vector<gfx::Range>& link_ranges =
   1264           delegate_->LegalDocumentLinks();
   1265       for (size_t i = 0; i < link_ranges.size(); ++i) {
   1266         views::StyledLabel::RangeStyleInfo link_range_info =
   1267             views::StyledLabel::RangeStyleInfo::CreateForLink();
   1268         link_range_info.disable_line_wrapping = false;
   1269         legal_document_view_->AddStyleRange(link_ranges[i], link_range_info);
   1270       }
   1271     }
   1272 
   1273     footnote_view_->SetVisible(!text.empty());
   1274     ContentsPreferredSizeChanged();
   1275   }
   1276 
   1277   if (GetWidget())
   1278     GetWidget()->UpdateWindowTitle();
   1279 }
   1280 
   1281 void AutofillDialogViews::UpdateButtonStrip() {
   1282   button_strip_extra_view_->SetVisible(
   1283       GetDialogButtons() != ui::DIALOG_BUTTON_NONE);
   1284   UpdateButtonStripExtraView();
   1285   GetDialogClientView()->UpdateDialogButtons();
   1286 
   1287   ContentsPreferredSizeChanged();
   1288 }
   1289 
   1290 void AutofillDialogViews::UpdateOverlay() {
   1291   overlay_view_->UpdateState();
   1292   ContentsPreferredSizeChanged();
   1293 }
   1294 
   1295 void AutofillDialogViews::UpdateDetailArea() {
   1296   scrollable_area_->SetVisible(true);
   1297   ContentsPreferredSizeChanged();
   1298 }
   1299 
   1300 void AutofillDialogViews::UpdateForErrors() {
   1301   ValidateForm();
   1302 }
   1303 
   1304 void AutofillDialogViews::UpdateNotificationArea() {
   1305   DCHECK(notification_area_);
   1306   notification_area_->SetNotifications(delegate_->CurrentNotifications());
   1307   ContentsPreferredSizeChanged();
   1308 }
   1309 
   1310 void AutofillDialogViews::UpdateSection(DialogSection section) {
   1311   UpdateSectionImpl(section, true);
   1312 }
   1313 
   1314 void AutofillDialogViews::UpdateErrorBubble() {
   1315   if (!delegate_->ShouldShowErrorBubble())
   1316     HideErrorBubble();
   1317 }
   1318 
   1319 void AutofillDialogViews::FillSection(DialogSection section,
   1320                                       const DetailInput& originating_input) {
   1321   DetailsGroup* group = GroupForSection(section);
   1322   // Make sure to overwrite the originating input.
   1323   TextfieldMap::iterator text_mapping =
   1324       group->textfields.find(originating_input.type);
   1325   if (text_mapping != group->textfields.end())
   1326     text_mapping->second->SetText(base::string16());
   1327 
   1328   // If the Autofill data comes from a credit card, make sure to overwrite the
   1329   // CC comboboxes (even if they already have something in them). If the
   1330   // Autofill data comes from an AutofillProfile, leave the comboboxes alone.
   1331   if (section == GetCreditCardSection() &&
   1332       AutofillType(originating_input.type).group() == CREDIT_CARD) {
   1333     for (ComboboxMap::const_iterator it = group->comboboxes.begin();
   1334          it != group->comboboxes.end(); ++it) {
   1335       if (AutofillType(it->first).group() == CREDIT_CARD)
   1336         it->second->SetSelectedIndex(it->second->model()->GetDefaultIndex());
   1337     }
   1338   }
   1339 
   1340   UpdateSectionImpl(section, false);
   1341 }
   1342 
   1343 void AutofillDialogViews::GetUserInput(DialogSection section,
   1344                                        FieldValueMap* output) {
   1345   DetailsGroup* group = GroupForSection(section);
   1346   for (TextfieldMap::const_iterator it = group->textfields.begin();
   1347        it != group->textfields.end(); ++it) {
   1348     output->insert(std::make_pair(it->first, it->second->text()));
   1349   }
   1350   for (ComboboxMap::const_iterator it = group->comboboxes.begin();
   1351        it != group->comboboxes.end(); ++it) {
   1352     output->insert(std::make_pair(it->first,
   1353         it->second->model()->GetItemAt(it->second->selected_index())));
   1354   }
   1355 }
   1356 
   1357 base::string16 AutofillDialogViews::GetCvc() {
   1358   return GroupForSection(GetCreditCardSection())->suggested_info->
   1359       decorated_textfield()->text();
   1360 }
   1361 
   1362 bool AutofillDialogViews::HitTestInput(const DetailInput& input,
   1363                                        const gfx::Point& screen_point) {
   1364   views::View* view = TextfieldForInput(input);
   1365   if (!view)
   1366     view = ComboboxForInput(input);
   1367 
   1368   if (view) {
   1369     gfx::Point target_point(screen_point);
   1370     views::View::ConvertPointFromScreen(view, &target_point);
   1371     return view->HitTestPoint(target_point);
   1372   }
   1373 
   1374   NOTREACHED();
   1375   return false;
   1376 }
   1377 
   1378 bool AutofillDialogViews::SaveDetailsLocally() {
   1379   DCHECK(save_in_chrome_checkbox_->visible());
   1380   return save_in_chrome_checkbox_->checked();
   1381 }
   1382 
   1383 const content::NavigationController* AutofillDialogViews::ShowSignIn() {
   1384   // The initial minimum width and height are set such that the dialog
   1385   // won't change size before the page is loaded.
   1386   int min_width = GetContentsBounds().width();
   1387   // The height has to include the button strip.
   1388   int min_height = GetDialogClientView()->GetContentsBounds().height();
   1389 
   1390   // TODO(abodenha): We should be able to use the WebContents of the WebView
   1391   // to navigate instead of LoadInitialURL. Figure out why it doesn't work.
   1392   sign_in_delegate_.reset(
   1393       new AutofillDialogSignInDelegate(
   1394           this, sign_in_web_view_->GetWebContents(),
   1395           delegate_->GetWebContents()->GetDelegate(),
   1396           gfx::Size(min_width, min_height), GetMaximumSignInViewSize()));
   1397   sign_in_web_view_->LoadInitialURL(delegate_->SignInUrl());
   1398 
   1399   ShowDialogInMode(SIGN_IN);
   1400 
   1401   UpdateButtonStrip();
   1402   ContentsPreferredSizeChanged();
   1403 
   1404   return &sign_in_web_view_->web_contents()->GetController();
   1405 }
   1406 
   1407 void AutofillDialogViews::HideSignIn() {
   1408   sign_in_web_view_->SetWebContents(NULL);
   1409 
   1410   if (delegate_->ShouldShowSpinner()) {
   1411     UpdateAccountChooser();
   1412   } else {
   1413     ShowDialogInMode(DETAIL_INPUT);
   1414     InvalidateLayout();
   1415   }
   1416   DCHECK(!sign_in_web_view_->visible());
   1417 
   1418   UpdateButtonStrip();
   1419   ContentsPreferredSizeChanged();
   1420 }
   1421 
   1422 void AutofillDialogViews::ModelChanged() {
   1423   menu_runner_.reset();
   1424 
   1425   for (DetailGroupMap::const_iterator iter = detail_groups_.begin();
   1426        iter != detail_groups_.end(); ++iter) {
   1427     UpdateDetailsGroupState(iter->second);
   1428   }
   1429 }
   1430 
   1431 TestableAutofillDialogView* AutofillDialogViews::GetTestableView() {
   1432   return this;
   1433 }
   1434 
   1435 void AutofillDialogViews::OnSignInResize(const gfx::Size& pref_size) {
   1436   sign_in_web_view_->SetPreferredSize(pref_size);
   1437   ContentsPreferredSizeChanged();
   1438 }
   1439 
   1440 void AutofillDialogViews::SubmitForTesting() {
   1441   Accept();
   1442 }
   1443 
   1444 void AutofillDialogViews::CancelForTesting() {
   1445   GetDialogClientView()->CancelWindow();
   1446 }
   1447 
   1448 base::string16 AutofillDialogViews::GetTextContentsOfInput(
   1449     const DetailInput& input) {
   1450   views::Textfield* textfield = TextfieldForInput(input);
   1451   if (textfield)
   1452     return textfield->text();
   1453 
   1454   views::Combobox* combobox = ComboboxForInput(input);
   1455   if (combobox)
   1456     return combobox->model()->GetItemAt(combobox->selected_index());
   1457 
   1458   NOTREACHED();
   1459   return base::string16();
   1460 }
   1461 
   1462 void AutofillDialogViews::SetTextContentsOfInput(
   1463     const DetailInput& input,
   1464     const base::string16& contents) {
   1465   views::Textfield* textfield = TextfieldForInput(input);
   1466   if (textfield) {
   1467     textfield->SetText(contents);
   1468     return;
   1469   }
   1470 
   1471   views::Combobox* combobox = ComboboxForInput(input);
   1472   if (combobox) {
   1473     SelectComboboxValueOrSetToDefault(combobox, input.initial_value);
   1474     return;
   1475   }
   1476 
   1477   NOTREACHED();
   1478 }
   1479 
   1480 void AutofillDialogViews::SetTextContentsOfSuggestionInput(
   1481     DialogSection section,
   1482     const base::string16& text) {
   1483   GroupForSection(section)->suggested_info->decorated_textfield()->
   1484       SetText(text);
   1485 }
   1486 
   1487 void AutofillDialogViews::ActivateInput(const DetailInput& input) {
   1488   TextfieldEditedOrActivated(TextfieldForInput(input), false);
   1489 }
   1490 
   1491 gfx::Size AutofillDialogViews::GetSize() const {
   1492   return GetWidget() ? GetWidget()->GetRootView()->size() : gfx::Size();
   1493 }
   1494 
   1495 content::WebContents* AutofillDialogViews::GetSignInWebContents() {
   1496   return sign_in_web_view_->web_contents();
   1497 }
   1498 
   1499 bool AutofillDialogViews::IsShowingOverlay() const {
   1500   return overlay_view_->visible();
   1501 }
   1502 
   1503 gfx::Size AutofillDialogViews::GetPreferredSize() {
   1504   if (preferred_size_.IsEmpty())
   1505     preferred_size_ = CalculatePreferredSize(false);
   1506 
   1507   return preferred_size_;
   1508 }
   1509 
   1510 gfx::Size AutofillDialogViews::GetMinimumSize() {
   1511   return CalculatePreferredSize(true);
   1512 }
   1513 
   1514 void AutofillDialogViews::Layout() {
   1515   const gfx::Rect content_bounds = GetContentsBounds();
   1516   if (sign_in_web_view_->visible()) {
   1517     sign_in_web_view_->SetBoundsRect(content_bounds);
   1518     return;
   1519   }
   1520 
   1521   if (loading_shield_->visible()) {
   1522     loading_shield_->SetBoundsRect(bounds());
   1523     return;
   1524   }
   1525 
   1526   const int x = content_bounds.x();
   1527   const int y = content_bounds.y();
   1528   const int width = content_bounds.width();
   1529   // Layout notification area at top of dialog.
   1530   int notification_height = notification_area_->GetHeightForWidth(width);
   1531   notification_area_->SetBounds(x, y, width, notification_height);
   1532 
   1533   // The rest (the |scrollable_area_|) takes up whatever's left.
   1534   if (scrollable_area_->visible()) {
   1535     int scroll_y = y;
   1536     if (notification_height > notification_area_->GetInsets().height())
   1537       scroll_y += notification_height + views::kRelatedControlVerticalSpacing;
   1538 
   1539     int scroll_bottom = content_bounds.bottom();
   1540     DCHECK_EQ(scrollable_area_->contents(), details_container_);
   1541     details_container_->SizeToPreferredSize();
   1542     // TODO(estade): remove this hack. See crbug.com/285996
   1543     details_container_->set_ignore_layouts(true);
   1544     scrollable_area_->SetBounds(x, scroll_y, width, scroll_bottom - scroll_y);
   1545     details_container_->set_ignore_layouts(false);
   1546   }
   1547 
   1548   if (error_bubble_)
   1549     error_bubble_->UpdatePosition();
   1550 }
   1551 
   1552 void AutofillDialogViews::OnNativeThemeChanged(
   1553     const ui::NativeTheme* theme) {
   1554   if (!legal_document_view_)
   1555     return;
   1556 
   1557   // NOTE: This color may change because of |auto_color_readability|, set on
   1558   // |legal_document_view_|.
   1559   views::StyledLabel::RangeStyleInfo default_style;
   1560   default_style.color =
   1561       theme->GetSystemColor(ui::NativeTheme::kColorId_LabelDisabledColor);
   1562 
   1563   legal_document_view_->SetDefaultStyle(default_style);
   1564 }
   1565 
   1566 base::string16 AutofillDialogViews::GetWindowTitle() const {
   1567   base::string16 title = delegate_->DialogTitle();
   1568   // Hack alert: we don't want the dialog to jiggle when a title is added or
   1569   // removed. Setting a non-empty string here keeps the dialog's title bar the
   1570   // same size.
   1571   return title.empty() ? ASCIIToUTF16(" ") : title;
   1572 }
   1573 
   1574 void AutofillDialogViews::WindowClosing() {
   1575   focus_manager_->RemoveFocusChangeListener(this);
   1576 }
   1577 
   1578 void AutofillDialogViews::DeleteDelegate() {
   1579   window_ = NULL;
   1580   // |this| belongs to the controller (|delegate_|).
   1581   delegate_->ViewClosed();
   1582 }
   1583 
   1584 int AutofillDialogViews::GetDialogButtons() const {
   1585   return delegate_->GetDialogButtons();
   1586 }
   1587 
   1588 int AutofillDialogViews::GetDefaultDialogButton() const {
   1589   if (GetDialogButtons() & ui::DIALOG_BUTTON_OK)
   1590     return ui::DIALOG_BUTTON_OK;
   1591 
   1592   return ui::DIALOG_BUTTON_NONE;
   1593 }
   1594 
   1595 base::string16 AutofillDialogViews::GetDialogButtonLabel(
   1596     ui::DialogButton button) const {
   1597   return button == ui::DIALOG_BUTTON_OK ?
   1598       delegate_->ConfirmButtonText() : delegate_->CancelButtonText();
   1599 }
   1600 
   1601 bool AutofillDialogViews::ShouldDefaultButtonBeBlue() const {
   1602   return true;
   1603 }
   1604 
   1605 bool AutofillDialogViews::IsDialogButtonEnabled(ui::DialogButton button) const {
   1606   return delegate_->IsDialogButtonEnabled(button);
   1607 }
   1608 
   1609 views::View* AutofillDialogViews::GetInitiallyFocusedView() {
   1610   if (!window_ || !focus_manager_)
   1611     return NULL;
   1612 
   1613   if (sign_in_web_view_->visible())
   1614     return sign_in_web_view_;
   1615 
   1616   if (loading_shield_->visible())
   1617     return views::DialogDelegateView::GetInitiallyFocusedView();
   1618 
   1619   DCHECK(scrollable_area_->visible());
   1620 
   1621   views::FocusManager* manager = focus_manager_;
   1622   for (views::View* next = scrollable_area_;
   1623        next;
   1624        next = manager->GetNextFocusableView(next, window_, false, true)) {
   1625     if (!IsInput(next))
   1626       continue;
   1627 
   1628     // If there are no invalid inputs, return the first input found. Otherwise,
   1629     // return the first invalid input found.
   1630     if (validity_map_.empty() ||
   1631         validity_map_.find(next) != validity_map_.end()) {
   1632       return next;
   1633     }
   1634   }
   1635 
   1636   return views::DialogDelegateView::GetInitiallyFocusedView();
   1637 }
   1638 
   1639 views::View* AutofillDialogViews::CreateExtraView() {
   1640   return button_strip_extra_view_;
   1641 }
   1642 
   1643 views::View* AutofillDialogViews::CreateTitlebarExtraView() {
   1644   return account_chooser_;
   1645 }
   1646 
   1647 views::View* AutofillDialogViews::CreateFootnoteView() {
   1648   footnote_view_ = new LayoutPropagationView();
   1649   footnote_view_->SetLayoutManager(
   1650       new views::BoxLayout(views::BoxLayout::kVertical,
   1651                            kDialogEdgePadding,
   1652                            kDialogEdgePadding,
   1653                            0));
   1654   footnote_view_->set_border(
   1655       views::Border::CreateSolidSidedBorder(1, 0, 0, 0, kSubtleBorderColor));
   1656   footnote_view_->set_background(
   1657       views::Background::CreateSolidBackground(kShadingColor));
   1658 
   1659   legal_document_view_ = new views::StyledLabel(base::string16(), this);
   1660   OnNativeThemeChanged(GetNativeTheme());
   1661 
   1662   footnote_view_->AddChildView(legal_document_view_);
   1663   footnote_view_->SetVisible(false);
   1664 
   1665   return footnote_view_;
   1666 }
   1667 
   1668 views::View* AutofillDialogViews::CreateOverlayView() {
   1669   return overlay_view_;
   1670 }
   1671 
   1672 bool AutofillDialogViews::Cancel() {
   1673   return delegate_->OnCancel();
   1674 }
   1675 
   1676 bool AutofillDialogViews::Accept() {
   1677   if (ValidateForm())
   1678     return delegate_->OnAccept();
   1679 
   1680   // |ValidateForm()| failed; there should be invalid views in |validity_map_|.
   1681   DCHECK(!validity_map_.empty());
   1682   FocusInitialView();
   1683 
   1684   return false;
   1685 }
   1686 
   1687 // TODO(wittman): Remove this override once we move to the new style frame view
   1688 // on all dialogs.
   1689 views::NonClientFrameView* AutofillDialogViews::CreateNonClientFrameView(
   1690     views::Widget* widget) {
   1691   return CreateConstrainedStyleNonClientFrameView(
   1692       widget,
   1693       delegate_->GetWebContents()->GetBrowserContext());
   1694 }
   1695 
   1696 void AutofillDialogViews::ContentsChanged(views::Textfield* sender,
   1697                                           const base::string16& new_contents) {
   1698   TextfieldEditedOrActivated(sender, true);
   1699 }
   1700 
   1701 bool AutofillDialogViews::HandleKeyEvent(views::Textfield* sender,
   1702                                          const ui::KeyEvent& key_event) {
   1703   ui::KeyEvent copy(key_event);
   1704 #if defined(OS_WIN) && !defined(USE_AURA)
   1705   content::NativeWebKeyboardEvent event(copy.native_event());
   1706 #else
   1707   content::NativeWebKeyboardEvent event(&copy);
   1708 #endif
   1709   return delegate_->HandleKeyPressEventInInput(event);
   1710 }
   1711 
   1712 bool AutofillDialogViews::HandleMouseEvent(views::Textfield* sender,
   1713                                            const ui::MouseEvent& mouse_event) {
   1714   if (mouse_event.IsLeftMouseButton() && sender->HasFocus()) {
   1715     TextfieldEditedOrActivated(sender, false);
   1716     // Show an error bubble if a user clicks on an input that's already focused
   1717     // (and invalid).
   1718     ShowErrorBubbleForViewIfNecessary(sender);
   1719   }
   1720 
   1721   return false;
   1722 }
   1723 
   1724 void AutofillDialogViews::OnWillChangeFocus(
   1725     views::View* focused_before,
   1726     views::View* focused_now) {
   1727   delegate_->FocusMoved();
   1728   HideErrorBubble();
   1729 }
   1730 
   1731 void AutofillDialogViews::OnDidChangeFocus(
   1732     views::View* focused_before,
   1733     views::View* focused_now) {
   1734   // If user leaves an edit-field, revalidate the group it belongs to.
   1735   if (focused_before) {
   1736     DetailsGroup* group = GroupForView(focused_before);
   1737     if (group && group->container->visible())
   1738       ValidateGroup(*group, VALIDATE_EDIT);
   1739   }
   1740 
   1741   // Show an error bubble when the user focuses the input.
   1742   if (focused_now) {
   1743     focused_now->ScrollRectToVisible(focused_now->GetLocalBounds());
   1744     ShowErrorBubbleForViewIfNecessary(focused_now);
   1745   }
   1746 }
   1747 
   1748 void AutofillDialogViews::OnSelectedIndexChanged(views::Combobox* combobox) {
   1749   DetailsGroup* group = GroupForView(combobox);
   1750   ValidateGroup(*group, VALIDATE_EDIT);
   1751   SetEditabilityForSection(group->section);
   1752 }
   1753 
   1754 void AutofillDialogViews::StyledLabelLinkClicked(const gfx::Range& range,
   1755                                                  int event_flags) {
   1756   delegate_->LegalDocumentLinkClicked(range);
   1757 }
   1758 
   1759 void AutofillDialogViews::OnMenuButtonClicked(views::View* source,
   1760                                               const gfx::Point& point) {
   1761   DCHECK_EQ(kSuggestedButtonClassName, source->GetClassName());
   1762 
   1763   DetailsGroup* group = NULL;
   1764   for (DetailGroupMap::iterator iter = detail_groups_.begin();
   1765        iter != detail_groups_.end(); ++iter) {
   1766     if (source == iter->second.suggested_button) {
   1767       group = &iter->second;
   1768       break;
   1769     }
   1770   }
   1771   DCHECK(group);
   1772 
   1773   if (!group->suggested_button->visible())
   1774     return;
   1775 
   1776   menu_runner_.reset(new views::MenuRunner(
   1777                          delegate_->MenuModelForSection(group->section)));
   1778 
   1779   group->container->SetActive(true);
   1780   views::Button::ButtonState state = group->suggested_button->state();
   1781   group->suggested_button->SetState(views::Button::STATE_PRESSED);
   1782 
   1783   gfx::Rect screen_bounds = source->GetBoundsInScreen();
   1784   screen_bounds.Inset(source->GetInsets());
   1785   if (menu_runner_->RunMenuAt(source->GetWidget(),
   1786                               NULL,
   1787                               screen_bounds,
   1788                               views::MenuItemView::TOPRIGHT,
   1789                               ui::MENU_SOURCE_NONE,
   1790                               0) == views::MenuRunner::MENU_DELETED) {
   1791     return;
   1792   }
   1793 
   1794   group->container->SetActive(false);
   1795   group->suggested_button->SetState(state);
   1796 }
   1797 
   1798 gfx::Size AutofillDialogViews::CalculatePreferredSize(bool get_minimum_size) {
   1799   gfx::Insets insets = GetInsets();
   1800   gfx::Size scroll_size = scrollable_area_->contents()->GetPreferredSize();
   1801   // The width is always set by the scroll area.
   1802   const int width = scroll_size.width();
   1803 
   1804   if (sign_in_web_view_->visible()) {
   1805     const gfx::Size size = static_cast<views::View*>(sign_in_web_view_)->
   1806         GetPreferredSize();
   1807     return gfx::Size(width + insets.width(), size.height() + insets.height());
   1808   }
   1809 
   1810   if (overlay_view_->visible()) {
   1811     const int height = overlay_view_->GetHeightForContentsForWidth(width);
   1812     if (height != 0)
   1813       return gfx::Size(width + insets.width(), height + insets.height());
   1814   }
   1815 
   1816   if (loading_shield_->visible()) {
   1817     return gfx::Size(width + insets.width(),
   1818                      loading_shield_height_ + insets.height());
   1819   }
   1820 
   1821   int height = 0;
   1822   const int notification_height = notification_area_->GetHeightForWidth(width);
   1823   if (notification_height > notification_area_->GetInsets().height())
   1824     height += notification_height + views::kRelatedControlVerticalSpacing;
   1825 
   1826   if (scrollable_area_->visible())
   1827     height += get_minimum_size ? kMinimumContentsHeight : scroll_size.height();
   1828 
   1829   return gfx::Size(width + insets.width(), height + insets.height());
   1830 }
   1831 
   1832 gfx::Size AutofillDialogViews::GetMinimumSignInViewSize() const {
   1833   return gfx::Size(GetDialogClientView()->size().width() - GetInsets().width(),
   1834                    kMinimumContentsHeight);
   1835 }
   1836 
   1837 gfx::Size AutofillDialogViews::GetMaximumSignInViewSize() const {
   1838   web_modal::WebContentsModalDialogHost* dialog_host =
   1839       WebContentsModalDialogManager::FromWebContents(
   1840           delegate_->GetWebContents())->delegate()->
   1841               GetWebContentsModalDialogHost();
   1842 
   1843   // Inset the maximum dialog height to get the maximum content height.
   1844   int height = dialog_host->GetMaximumDialogSize().height();
   1845   const int non_client_height = GetWidget()->non_client_view()->height();
   1846   const int client_height = GetWidget()->client_view()->height();
   1847   // TODO(msw): Resolve the 12 pixel discrepancy; is that the bubble border?
   1848   height -= non_client_height - client_height - 12;
   1849   height = std::max(height, kMinimumContentsHeight);
   1850 
   1851   // The dialog's width never changes.
   1852   const int width = GetDialogClientView()->size().width() - GetInsets().width();
   1853   return gfx::Size(width, height);
   1854 }
   1855 
   1856 DialogSection AutofillDialogViews::GetCreditCardSection() const {
   1857   if (delegate_->SectionIsActive(SECTION_CC))
   1858     return SECTION_CC;
   1859 
   1860   DCHECK(delegate_->SectionIsActive(SECTION_CC_BILLING));
   1861   return SECTION_CC_BILLING;
   1862 }
   1863 
   1864 void AutofillDialogViews::InitChildViews() {
   1865   button_strip_extra_view_ = new LayoutPropagationView();
   1866   button_strip_extra_view_->SetLayoutManager(
   1867       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 0));
   1868 
   1869   save_in_chrome_checkbox_container_ = new views::View();
   1870   save_in_chrome_checkbox_container_->SetLayoutManager(
   1871       new views::BoxLayout(views::BoxLayout::kHorizontal, 0, 0, 7));
   1872   button_strip_extra_view_->AddChildView(save_in_chrome_checkbox_container_);
   1873 
   1874   save_in_chrome_checkbox_ =
   1875       new views::Checkbox(delegate_->SaveLocallyText());
   1876   save_in_chrome_checkbox_->SetChecked(delegate_->ShouldSaveInChrome());
   1877   save_in_chrome_checkbox_container_->AddChildView(save_in_chrome_checkbox_);
   1878 
   1879   save_in_chrome_checkbox_container_->AddChildView(
   1880       new TooltipIcon(delegate_->SaveLocallyTooltip()));
   1881 
   1882   button_strip_image_ = new views::ImageView();
   1883   button_strip_extra_view_->AddChildView(button_strip_image_);
   1884 
   1885   account_chooser_ = new AccountChooser(delegate_);
   1886   notification_area_ = new NotificationArea(delegate_);
   1887   notification_area_->set_arrow_centering_anchor(account_chooser_->AsWeakPtr());
   1888   AddChildView(notification_area_);
   1889 
   1890   scrollable_area_ = new views::ScrollView();
   1891   scrollable_area_->set_hide_horizontal_scrollbar(true);
   1892   scrollable_area_->SetContents(CreateDetailsContainer());
   1893   AddChildView(scrollable_area_);
   1894 
   1895   loading_shield_ = new LoadingAnimationView(delegate_->SpinnerText());
   1896   AddChildView(loading_shield_);
   1897 
   1898   sign_in_web_view_ = new views::WebView(delegate_->profile());
   1899   AddChildView(sign_in_web_view_);
   1900 
   1901   overlay_view_ = new OverlayView(delegate_);
   1902   overlay_view_->SetVisible(false);
   1903 }
   1904 
   1905 views::View* AutofillDialogViews::CreateDetailsContainer() {
   1906   details_container_ = new DetailsContainerView(
   1907       base::Bind(&AutofillDialogViews::DetailsContainerBoundsChanged,
   1908                  base::Unretained(this)));
   1909   // A box layout is used because it respects widget visibility.
   1910   details_container_->SetLayoutManager(
   1911       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
   1912   for (DetailGroupMap::iterator iter = detail_groups_.begin();
   1913        iter != detail_groups_.end(); ++iter) {
   1914     CreateDetailsSection(iter->second.section);
   1915     details_container_->AddChildView(iter->second.container);
   1916   }
   1917 
   1918   return details_container_;
   1919 }
   1920 
   1921 void AutofillDialogViews::CreateDetailsSection(DialogSection section) {
   1922   // Inputs container (manual inputs + combobox).
   1923   views::View* inputs_container = CreateInputsContainer(section);
   1924 
   1925   DetailsGroup* group = GroupForSection(section);
   1926   // Container (holds label + inputs).
   1927   group->container = new SectionContainer(
   1928       delegate_->LabelForSection(section),
   1929       inputs_container,
   1930       group->suggested_button);
   1931   DCHECK(group->suggested_button->parent());
   1932   UpdateDetailsGroupState(*group);
   1933 }
   1934 
   1935 views::View* AutofillDialogViews::CreateInputsContainer(DialogSection section) {
   1936   // The |info_view| holds |manual_inputs| and |suggested_info|, allowing the
   1937   // dialog to toggle which is shown.
   1938   views::View* info_view = new views::View();
   1939   info_view->SetLayoutManager(
   1940       new views::BoxLayout(views::BoxLayout::kVertical, 0, 0, 0));
   1941 
   1942   views::View* manual_inputs = InitInputsView(section);
   1943   info_view->AddChildView(manual_inputs);
   1944   SuggestionView* suggested_info = new SuggestionView(this);
   1945   info_view->AddChildView(suggested_info);
   1946 
   1947   DetailsGroup* group = GroupForSection(section);
   1948   // TODO(estade): It might be slightly more OO if this button were created
   1949   // and listened to by the section container.
   1950   group->suggested_button = new SuggestedButton(this);
   1951   group->manual_input = manual_inputs;
   1952   group->suggested_info = suggested_info;
   1953 
   1954   return info_view;
   1955 }
   1956 
   1957 // TODO(estade): we should be using Chrome-style constrained window padding
   1958 // values.
   1959 views::View* AutofillDialogViews::InitInputsView(DialogSection section) {
   1960   const DetailInputs& inputs = delegate_->RequestedFieldsForSection(section);
   1961   TextfieldMap* textfields = &GroupForSection(section)->textfields;
   1962   ComboboxMap* comboboxes = &GroupForSection(section)->comboboxes;
   1963 
   1964   views::View* view = new views::View();
   1965   views::GridLayout* layout = new views::GridLayout(view);
   1966   view->SetLayoutManager(layout);
   1967 
   1968   int column_set_id = 0;
   1969   for (DetailInputs::const_iterator it = inputs.begin();
   1970        it != inputs.end(); ++it) {
   1971     const DetailInput& input = *it;
   1972     ui::ComboboxModel* input_model =
   1973         delegate_->ComboboxModelForAutofillType(input.type);
   1974     scoped_ptr<views::View> view_to_add;
   1975     if (input_model) {
   1976       views::Combobox* combobox = new views::Combobox(input_model);
   1977       combobox->set_listener(this);
   1978       comboboxes->insert(std::make_pair(input.type, combobox));
   1979       SelectComboboxValueOrSetToDefault(combobox, input.initial_value);
   1980       view_to_add.reset(combobox);
   1981     } else {
   1982       DecoratedTextfield* field = new DecoratedTextfield(
   1983           input.initial_value,
   1984           l10n_util::GetStringUTF16(input.placeholder_text_rid),
   1985           this);
   1986 
   1987       textfields->insert(std::make_pair(input.type, field));
   1988       view_to_add.reset(field);
   1989     }
   1990 
   1991     if (input.length == DetailInput::NONE) {
   1992       other_owned_views_.push_back(view_to_add.release());
   1993       continue;
   1994     }
   1995 
   1996     if (input.length == DetailInput::LONG)
   1997       ++column_set_id;
   1998 
   1999     views::ColumnSet* column_set = layout->GetColumnSet(column_set_id);
   2000     if (!column_set) {
   2001       // Create a new column set and row.
   2002       column_set = layout->AddColumnSet(column_set_id);
   2003       if (it != inputs.begin())
   2004         layout->AddPaddingRow(0, kManualInputRowPadding);
   2005       layout->StartRow(0, column_set_id);
   2006     } else {
   2007       // Add a new column to existing row.
   2008       column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
   2009       // Must explicitly skip the padding column since we've already started
   2010       // adding views.
   2011       layout->SkipColumns(1);
   2012     }
   2013 
   2014     float expand = input.expand_weight;
   2015     column_set->AddColumn(views::GridLayout::FILL,
   2016                           views::GridLayout::FILL,
   2017                           expand ? expand : 1.0,
   2018                           views::GridLayout::USE_PREF,
   2019                           0,
   2020                           0);
   2021 
   2022     // This is the same as AddView(view_to_add), except that 1 is used for the
   2023     // view's preferred width. Thus the width of the column completely depends
   2024     // on |expand|.
   2025     layout->AddView(view_to_add.release(), 1, 1,
   2026                     views::GridLayout::FILL, views::GridLayout::FILL,
   2027                     1, 0);
   2028 
   2029     if (input.length == DetailInput::LONG)
   2030       ++column_set_id;
   2031   }
   2032 
   2033   SetIconsForSection(section);
   2034 
   2035   return view;
   2036 }
   2037 
   2038 void AutofillDialogViews::ShowDialogInMode(DialogMode dialog_mode) {
   2039   loading_shield_->SetVisible(dialog_mode == LOADING);
   2040   sign_in_web_view_->SetVisible(dialog_mode == SIGN_IN);
   2041   notification_area_->SetVisible(dialog_mode == DETAIL_INPUT);
   2042   scrollable_area_->SetVisible(dialog_mode == DETAIL_INPUT);
   2043   FocusInitialView();
   2044 }
   2045 
   2046 void AutofillDialogViews::UpdateSectionImpl(
   2047     DialogSection section,
   2048     bool clobber_inputs) {
   2049   // Reset all validity marks for this section.
   2050   if (clobber_inputs)
   2051     MarkInputsInvalid(section, ValidityMessages(), true);
   2052 
   2053   const DetailInputs& updated_inputs =
   2054       delegate_->RequestedFieldsForSection(section);
   2055   DetailsGroup* group = GroupForSection(section);
   2056 
   2057   for (DetailInputs::const_iterator iter = updated_inputs.begin();
   2058        iter != updated_inputs.end(); ++iter) {
   2059     const DetailInput& input = *iter;
   2060     TextfieldMap::iterator text_mapping = group->textfields.find(input.type);
   2061 
   2062     if (text_mapping != group->textfields.end()) {
   2063       DecoratedTextfield* decorated = text_mapping->second;
   2064       if (decorated->text().empty() || clobber_inputs)
   2065         decorated->SetText(input.initial_value);
   2066     }
   2067 
   2068     ComboboxMap::iterator combo_mapping = group->comboboxes.find(input.type);
   2069     if (combo_mapping != group->comboboxes.end()) {
   2070       views::Combobox* combobox = combo_mapping->second;
   2071       if (combobox->selected_index() == combobox->model()->GetDefaultIndex() ||
   2072           clobber_inputs) {
   2073         SelectComboboxValueOrSetToDefault(combobox, input.initial_value);
   2074       }
   2075     }
   2076   }
   2077 
   2078   SetIconsForSection(section);
   2079   SetEditabilityForSection(section);
   2080   UpdateDetailsGroupState(*group);
   2081 }
   2082 
   2083 void AutofillDialogViews::UpdateDetailsGroupState(const DetailsGroup& group) {
   2084   const SuggestionState& suggestion_state =
   2085       delegate_->SuggestionStateForSection(group.section);
   2086   group.suggested_info->SetState(suggestion_state);
   2087   group.manual_input->SetVisible(!suggestion_state.visible);
   2088 
   2089   UpdateButtonStripExtraView();
   2090 
   2091   const bool has_menu = !!delegate_->MenuModelForSection(group.section);
   2092 
   2093   if (group.suggested_button)
   2094     group.suggested_button->SetVisible(has_menu);
   2095 
   2096   if (group.container) {
   2097     group.container->SetForwardMouseEvents(
   2098         has_menu && suggestion_state.visible);
   2099     group.container->SetVisible(delegate_->SectionIsActive(group.section));
   2100     if (group.container->visible())
   2101       ValidateGroup(group, VALIDATE_EDIT);
   2102   }
   2103 
   2104   ContentsPreferredSizeChanged();
   2105 }
   2106 
   2107 void AutofillDialogViews::FocusInitialView() {
   2108   views::View* to_focus = GetInitiallyFocusedView();
   2109   if (to_focus && !to_focus->HasFocus())
   2110     to_focus->RequestFocus();
   2111 }
   2112 
   2113 template<class T>
   2114 void AutofillDialogViews::SetValidityForInput(
   2115     T* input,
   2116     const base::string16& message) {
   2117   bool invalid = !message.empty();
   2118   input->SetInvalid(invalid);
   2119 
   2120   if (invalid) {
   2121     validity_map_[input] = message;
   2122   } else {
   2123     validity_map_.erase(input);
   2124 
   2125     if (error_bubble_ && error_bubble_->anchor() == input) {
   2126       validity_map_.erase(input);
   2127       HideErrorBubble();
   2128     }
   2129   }
   2130 }
   2131 
   2132 void AutofillDialogViews::ShowErrorBubbleForViewIfNecessary(views::View* view) {
   2133   if (!view->GetWidget())
   2134     return;
   2135 
   2136   if (!delegate_->ShouldShowErrorBubble()) {
   2137     DCHECK(!error_bubble_);
   2138     return;
   2139   }
   2140 
   2141   std::map<views::View*, base::string16>::iterator error_message =
   2142       validity_map_.find(view);
   2143   if (error_message != validity_map_.end()) {
   2144     view->ScrollRectToVisible(view->GetLocalBounds());
   2145 
   2146     if (!error_bubble_ || error_bubble_->anchor() != view) {
   2147       HideErrorBubble();
   2148       error_bubble_ = new InfoBubble(view, error_message->second);
   2149       error_bubble_->set_align_to_anchor_edge(true);
   2150       error_bubble_->set_preferred_width(
   2151           (kSectionContainerWidth - views::kRelatedControlVerticalSpacing) / 2);
   2152       bool show_above = view->GetClassName() == views::Combobox::kViewClassName;
   2153       error_bubble_->set_show_above_anchor(show_above);
   2154       error_bubble_->Show();
   2155       observer_.Add(error_bubble_->GetWidget());
   2156     }
   2157   }
   2158 }
   2159 
   2160 void AutofillDialogViews::HideErrorBubble() {
   2161   if (error_bubble_)
   2162     error_bubble_->Hide();
   2163 }
   2164 
   2165 void AutofillDialogViews::MarkInputsInvalid(
   2166     DialogSection section,
   2167     const ValidityMessages& messages,
   2168     bool overwrite_unsure) {
   2169   DetailsGroup* group = GroupForSection(section);
   2170   DCHECK(group->container->visible());
   2171 
   2172   if (group->manual_input->visible()) {
   2173     for (TextfieldMap::const_iterator iter = group->textfields.begin();
   2174          iter != group->textfields.end(); ++iter) {
   2175       const ValidityMessage& message =
   2176           messages.GetMessageOrDefault(iter->first);
   2177       if (overwrite_unsure || message.sure)
   2178         SetValidityForInput(iter->second, message.text);
   2179     }
   2180     for (ComboboxMap::const_iterator iter = group->comboboxes.begin();
   2181          iter != group->comboboxes.end(); ++iter) {
   2182       const ValidityMessage& message =
   2183           messages.GetMessageOrDefault(iter->first);
   2184       if (overwrite_unsure || message.sure)
   2185         SetValidityForInput(iter->second, message.text);
   2186     }
   2187   } else {
   2188     // Purge invisible views from |validity_map_|.
   2189     std::map<views::View*, base::string16>::iterator it;
   2190     for (it = validity_map_.begin(); it != validity_map_.end();) {
   2191       DCHECK(GroupForView(it->first));
   2192       if (GroupForView(it->first) == group)
   2193         validity_map_.erase(it++);
   2194       else
   2195         ++it;
   2196     }
   2197 
   2198     if (section == GetCreditCardSection()) {
   2199       // Special case CVC as it's not part of |group->manual_input|.
   2200       const ValidityMessage& message =
   2201           messages.GetMessageOrDefault(CREDIT_CARD_VERIFICATION_CODE);
   2202       if (overwrite_unsure || message.sure) {
   2203         SetValidityForInput(group->suggested_info->decorated_textfield(),
   2204                             message.text);
   2205       }
   2206     }
   2207   }
   2208 }
   2209 
   2210 bool AutofillDialogViews::ValidateGroup(const DetailsGroup& group,
   2211                                         ValidationType validation_type) {
   2212   DCHECK(group.container->visible());
   2213 
   2214   FieldValueMap detail_outputs;
   2215 
   2216   if (group.manual_input->visible()) {
   2217     for (TextfieldMap::const_iterator iter = group.textfields.begin();
   2218          iter != group.textfields.end(); ++iter) {
   2219       if (!iter->second->editable())
   2220         continue;
   2221 
   2222       detail_outputs[iter->first] = iter->second->text();
   2223     }
   2224     for (ComboboxMap::const_iterator iter = group.comboboxes.begin();
   2225          iter != group.comboboxes.end(); ++iter) {
   2226       if (!iter->second->enabled())
   2227         continue;
   2228 
   2229       views::Combobox* combobox = iter->second;
   2230       base::string16 item =
   2231           combobox->model()->GetItemAt(combobox->selected_index());
   2232       detail_outputs[iter->first] = item;
   2233     }
   2234   } else if (group.section == GetCreditCardSection()) {
   2235     DecoratedTextfield* decorated_cvc =
   2236         group.suggested_info->decorated_textfield();
   2237     if (decorated_cvc->visible())
   2238       detail_outputs[CREDIT_CARD_VERIFICATION_CODE] = decorated_cvc->text();
   2239   }
   2240 
   2241   ValidityMessages validity = delegate_->InputsAreValid(group.section,
   2242                                                         detail_outputs);
   2243   MarkInputsInvalid(group.section, validity, validation_type == VALIDATE_FINAL);
   2244 
   2245   // If there are any validation errors, sure or unsure, the group is invalid.
   2246   return !validity.HasErrors();
   2247 }
   2248 
   2249 bool AutofillDialogViews::ValidateForm() {
   2250   bool all_valid = true;
   2251   validity_map_.clear();
   2252 
   2253   for (DetailGroupMap::iterator iter = detail_groups_.begin();
   2254        iter != detail_groups_.end(); ++iter) {
   2255     const DetailsGroup& group = iter->second;
   2256     if (!group.container->visible())
   2257       continue;
   2258 
   2259     if (!ValidateGroup(group, VALIDATE_FINAL))
   2260       all_valid = false;
   2261   }
   2262 
   2263   return all_valid;
   2264 }
   2265 
   2266 void AutofillDialogViews::TextfieldEditedOrActivated(
   2267     views::Textfield* textfield,
   2268     bool was_edit) {
   2269   DetailsGroup* group = GroupForView(textfield);
   2270   DCHECK(group);
   2271 
   2272   // Figure out the ServerFieldType this textfield represents.
   2273   ServerFieldType type = UNKNOWN_TYPE;
   2274   DecoratedTextfield* decorated = NULL;
   2275 
   2276   // Look for the input in the manual inputs.
   2277   for (TextfieldMap::const_iterator iter = group->textfields.begin();
   2278        iter != group->textfields.end();
   2279        ++iter) {
   2280     decorated = iter->second;
   2281     if (decorated == textfield) {
   2282       delegate_->UserEditedOrActivatedInput(group->section,
   2283                                             iter->first,
   2284                                             GetWidget()->GetNativeView(),
   2285                                             textfield->GetBoundsInScreen(),
   2286                                             textfield->text(),
   2287                                             was_edit);
   2288       type = iter->first;
   2289       break;
   2290     }
   2291   }
   2292 
   2293   if (textfield == group->suggested_info->decorated_textfield()) {
   2294     decorated = group->suggested_info->decorated_textfield();
   2295     type = CREDIT_CARD_VERIFICATION_CODE;
   2296   }
   2297   DCHECK_NE(UNKNOWN_TYPE, type);
   2298 
   2299   // If the field is marked as invalid, check if the text is now valid.
   2300   // Many fields (i.e. CC#) are invalid for most of the duration of editing,
   2301   // so flagging them as invalid prematurely is not helpful. However,
   2302   // correcting a minor mistake (i.e. a wrong CC digit) should immediately
   2303   // result in validation - positive user feedback.
   2304   if (decorated->invalid() && was_edit) {
   2305     SetValidityForInput(
   2306         decorated,
   2307         delegate_->InputValidityMessage(group->section, type,
   2308                                         textfield->text()));
   2309 
   2310     // If the field transitioned from invalid to valid, re-validate the group,
   2311     // since inter-field checks become meaningful with valid fields.
   2312     if (!decorated->invalid())
   2313       ValidateGroup(*group, VALIDATE_EDIT);
   2314   }
   2315 
   2316   if (delegate_->FieldControlsIcons(type))
   2317     SetIconsForSection(group->section);
   2318 
   2319   SetEditabilityForSection(group->section);
   2320 }
   2321 
   2322 void AutofillDialogViews::UpdateButtonStripExtraView() {
   2323   save_in_chrome_checkbox_container_->SetVisible(
   2324       delegate_->ShouldOfferToSaveInChrome());
   2325 
   2326   gfx::Image image = delegate_->ButtonStripImage();
   2327   button_strip_image_->SetVisible(!image.IsEmpty());
   2328   button_strip_image_->SetImage(image.AsImageSkia());
   2329 }
   2330 
   2331 void AutofillDialogViews::ContentsPreferredSizeChanged() {
   2332   if (updates_scope_ != 0) {
   2333     needs_update_ = true;
   2334     return;
   2335   }
   2336 
   2337   preferred_size_ = gfx::Size();
   2338 
   2339   if (GetWidget() && delegate_ && delegate_->GetWebContents()) {
   2340     UpdateWebContentsModalDialogPosition(
   2341         GetWidget(),
   2342         WebContentsModalDialogManager::FromWebContents(
   2343             delegate_->GetWebContents())->delegate()->
   2344                 GetWebContentsModalDialogHost());
   2345     SetBoundsRect(bounds());
   2346   }
   2347 }
   2348 
   2349 AutofillDialogViews::DetailsGroup* AutofillDialogViews::GroupForSection(
   2350     DialogSection section) {
   2351   return &detail_groups_.find(section)->second;
   2352 }
   2353 
   2354 AutofillDialogViews::DetailsGroup* AutofillDialogViews::GroupForView(
   2355     views::View* view) {
   2356   DCHECK(view);
   2357 
   2358   for (DetailGroupMap::iterator iter = detail_groups_.begin();
   2359        iter != detail_groups_.end(); ++iter) {
   2360     DetailsGroup* group = &iter->second;
   2361     if (view->parent() == group->manual_input)
   2362       return group;
   2363 
   2364     views::View* decorated =
   2365         view->GetAncestorWithClassName(DecoratedTextfield::kViewClassName);
   2366 
   2367     // Textfields need to check a second case, since they can be suggested
   2368     // inputs instead of directly editable inputs. Those are accessed via
   2369     // |suggested_info|.
   2370     if (decorated &&
   2371         decorated == group->suggested_info->decorated_textfield()) {
   2372       return group;
   2373     }
   2374   }
   2375   return NULL;
   2376 }
   2377 
   2378 views::Textfield* AutofillDialogViews::TextfieldForInput(
   2379     const DetailInput& input) {
   2380   for (DetailGroupMap::iterator iter = detail_groups_.begin();
   2381        iter != detail_groups_.end(); ++iter) {
   2382     const DetailsGroup& group = iter->second;
   2383     TextfieldMap::const_iterator text_mapping =
   2384         group.textfields.find(input.type);
   2385     if (text_mapping != group.textfields.end())
   2386       return text_mapping->second;
   2387   }
   2388 
   2389   return NULL;
   2390 }
   2391 
   2392 views::Combobox* AutofillDialogViews::ComboboxForInput(
   2393     const DetailInput& input) {
   2394   for (DetailGroupMap::iterator iter = detail_groups_.begin();
   2395        iter != detail_groups_.end(); ++iter) {
   2396     const DetailsGroup& group = iter->second;
   2397     ComboboxMap::const_iterator combo_mapping =
   2398         group.comboboxes.find(input.type);
   2399     if (combo_mapping != group.comboboxes.end())
   2400       return combo_mapping->second;
   2401   }
   2402 
   2403   return NULL;
   2404 }
   2405 
   2406 void AutofillDialogViews::DetailsContainerBoundsChanged() {
   2407   if (error_bubble_)
   2408     error_bubble_->UpdatePosition();
   2409 }
   2410 
   2411 void AutofillDialogViews::SetIconsForSection(DialogSection section) {
   2412   FieldValueMap user_input;
   2413   GetUserInput(section, &user_input);
   2414   FieldIconMap field_icons = delegate_->IconsForFields(user_input);
   2415   TextfieldMap* textfields = &GroupForSection(section)->textfields;
   2416   for (TextfieldMap::const_iterator textfield_it = textfields->begin();
   2417        textfield_it != textfields->end();
   2418        ++textfield_it) {
   2419     ServerFieldType field_type = textfield_it->first;
   2420     FieldIconMap::const_iterator field_icon_it = field_icons.find(field_type);
   2421     DecoratedTextfield* textfield = textfield_it->second;
   2422     if (field_icon_it != field_icons.end())
   2423       textfield->SetIcon(field_icon_it->second);
   2424     else
   2425       textfield->SetTooltipIcon(delegate_->TooltipForField(field_type));
   2426   }
   2427 }
   2428 
   2429 void AutofillDialogViews::SetEditabilityForSection(DialogSection section) {
   2430   const DetailInputs& inputs =
   2431       delegate_->RequestedFieldsForSection(section);
   2432   DetailsGroup* group = GroupForSection(section);
   2433 
   2434   for (DetailInputs::const_iterator iter = inputs.begin();
   2435        iter != inputs.end(); ++iter) {
   2436     const DetailInput& input = *iter;
   2437     bool editable = delegate_->InputIsEditable(input, section);
   2438 
   2439     TextfieldMap::iterator text_mapping = group->textfields.find(input.type);
   2440     if (text_mapping != group->textfields.end()) {
   2441       DecoratedTextfield* decorated = text_mapping->second;
   2442       decorated->SetEditable(editable);
   2443       continue;
   2444     }
   2445 
   2446     ComboboxMap::iterator combo_mapping = group->comboboxes.find(input.type);
   2447     if (combo_mapping != group->comboboxes.end()) {
   2448       views::Combobox* combobox = combo_mapping->second;
   2449       combobox->SetEnabled(editable);
   2450     }
   2451   }
   2452 }
   2453 
   2454 AutofillDialogViews::DetailsGroup::DetailsGroup(DialogSection section)
   2455     : section(section),
   2456       container(NULL),
   2457       manual_input(NULL),
   2458       suggested_info(NULL),
   2459       suggested_button(NULL) {}
   2460 
   2461 AutofillDialogViews::DetailsGroup::~DetailsGroup() {}
   2462 
   2463 }  // namespace autofill
   2464