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