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