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