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