• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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/autocomplete/autocomplete_popup_contents_view.h"
6 
7 #include "base/compiler_specific.h"
8 #include "base/utf_string_conversions.h"
9 #include "chrome/browser/autocomplete/autocomplete_edit_view.h"
10 #include "chrome/browser/autocomplete/autocomplete_popup_model.h"
11 #include "chrome/browser/instant/instant_confirm_dialog.h"
12 #include "chrome/browser/instant/promo_counter.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/browser/ui/views/autocomplete/autocomplete_result_view.h"
15 #include "chrome/browser/ui/views/bubble/bubble_border.h"
16 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
17 #include "grit/chromium_strings.h"
18 #include "grit/generated_resources.h"
19 #include "grit/theme_resources.h"
20 #include "third_party/skia/include/core/SkShader.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/base/theme_provider.h"
24 #include "ui/gfx/canvas_skia.h"
25 #include "ui/gfx/insets.h"
26 #include "ui/gfx/path.h"
27 #include "unicode/ubidi.h"
28 #include "views/controls/button/text_button.h"
29 #include "views/controls/label.h"
30 #include "views/layout/grid_layout.h"
31 #include "views/layout/layout_constants.h"
32 #include "views/painter.h"
33 #include "views/widget/widget.h"
34 #include "views/window/window.h"
35 
36 #if defined(OS_WIN)
37 #include <commctrl.h>
38 #include <dwmapi.h>
39 #include <objidl.h>
40 
41 #include "base/win/scoped_gdi_object.h"
42 #include "views/widget/widget_win.h"
43 #endif
44 
45 #if defined(OS_LINUX)
46 #include "ui/gfx/skia_utils_gtk.h"
47 #endif
48 
49 namespace {
50 
51 const SkAlpha kGlassPopupAlpha = 240;
52 const SkAlpha kOpaquePopupAlpha = 255;
53 // The size delta between the font used for the edit and the result rows. Passed
54 // to gfx::Font::DeriveFont.
55 #if defined(OS_CHROMEOS)
56 // Don't adjust the size on Chrome OS (http://crbug.com/61433).
57 const int kEditFontAdjust = 0;
58 #else
59 const int kEditFontAdjust = -1;
60 #endif
61 
62 // Horizontal padding between the buttons on the opt in promo.
63 const int kOptInButtonPadding = 2;
64 
65 // Padding around the opt in view.
66 const int kOptInLeftPadding = 12;
67 const int kOptInRightPadding = 10;
68 const int kOptInTopPadding = 6;
69 const int kOptInBottomPadding = 5;
70 
71 // Horizontal/Vertical inset of the promo background.
72 const int kOptInBackgroundHInset = 6;
73 const int kOptInBackgroundVInset = 2;
74 
75 // Border for instant opt-in buttons. Consists of two 9 patch painters: one for
76 // the normal state, the other for the pressed state.
77 class OptInButtonBorder : public views::Border {
78  public:
OptInButtonBorder()79   OptInButtonBorder() {
80     border_painter_.reset(CreatePainter(IDR_OPT_IN_BUTTON));
81     border_pushed_painter_.reset(CreatePainter(IDR_OPT_IN_BUTTON_P));
82   }
83 
Paint(const views::View & view,gfx::Canvas * canvas) const84   virtual void Paint(const views::View& view, gfx::Canvas* canvas) const {
85     views::Painter* painter;
86     if (static_cast<const views::CustomButton&>(view).state() ==
87         views::CustomButton::BS_PUSHED) {
88       painter = border_pushed_painter_.get();
89     } else {
90       painter = border_painter_.get();
91     }
92     painter->Paint(view.width(), view.height(), canvas);
93   }
94 
GetInsets(gfx::Insets * insets) const95   virtual void GetInsets(gfx::Insets* insets) const {
96     insets->Set(3, 8, 3, 8);
97   }
98 
99  private:
100   // Creates 9 patch painter from the image with the id |image_id|.
CreatePainter(int image_id)101   views::Painter* CreatePainter(int image_id) {
102     SkBitmap* image =
103         ResourceBundle::GetSharedInstance().GetBitmapNamed(image_id);
104     int w = image->width() / 2;
105     if (image->width() % 2 == 0)
106       w--;
107     int h = image->height() / 2;
108     if (image->height() % 2 == 0)
109       h--;
110     gfx::Insets insets(h, w, h, w);
111     return views::Painter::CreateImagePainter(*image, insets, true);
112   }
113 
114   scoped_ptr<views::Painter> border_painter_;
115   scoped_ptr<views::Painter> border_pushed_painter_;
116 
117   DISALLOW_COPY_AND_ASSIGN(OptInButtonBorder);
118 };
119 
120 }  // namespace
121 
122 class AutocompletePopupContentsView::InstantOptInView
123     : public views::View,
124       public views::ButtonListener {
125  public:
InstantOptInView(AutocompletePopupContentsView * contents_view,const gfx::Font & label_font,const gfx::Font & button_font)126   InstantOptInView(AutocompletePopupContentsView* contents_view,
127                    const gfx::Font& label_font,
128                    const gfx::Font& button_font)
129       : contents_view_(contents_view),
130         bg_painter_(views::Painter::CreateVerticalGradient(
131                         SkColorSetRGB(255, 242, 183),
132                         SkColorSetRGB(250, 230, 145))) {
133     views::Label* label = new views::Label(
134         UTF16ToWide(l10n_util::GetStringUTF16(IDS_INSTANT_OPT_IN_LABEL)));
135     label->SetFont(label_font);
136 
137     views::GridLayout* layout = new views::GridLayout(this);
138     layout->SetInsets(kOptInTopPadding, kOptInLeftPadding,
139                       kOptInBottomPadding, kOptInRightPadding);
140     SetLayoutManager(layout);
141 
142     const int first_column_set = 1;
143     views::GridLayout::Alignment v_align = views::GridLayout::CENTER;
144     views::ColumnSet* column_set = layout->AddColumnSet(first_column_set);
145     column_set->AddColumn(views::GridLayout::TRAILING, v_align, 1,
146                           views::GridLayout::USE_PREF, 0, 0);
147     column_set->AddPaddingColumn(0, views::kRelatedControlHorizontalSpacing);
148     column_set->AddColumn(views::GridLayout::CENTER, v_align, 0,
149                           views::GridLayout::USE_PREF, 0, 0);
150     column_set->AddPaddingColumn(0, kOptInButtonPadding);
151     column_set->AddColumn(views::GridLayout::CENTER, v_align, 0,
152                           views::GridLayout::USE_PREF, 0, 0);
153     column_set->LinkColumnSizes(2, 4, -1);
154     layout->StartRow(0, first_column_set);
155     layout->AddView(label);
156     layout->AddView(CreateButton(IDS_INSTANT_OPT_IN_ENABLE, button_font));
157     layout->AddView(CreateButton(IDS_INSTANT_OPT_IN_NO_THANKS, button_font));
158   }
159 
ButtonPressed(views::Button * sender,const views::Event & event)160   virtual void ButtonPressed(views::Button* sender, const views::Event& event) {
161     contents_view_->UserPressedOptIn(
162         sender->tag() == IDS_INSTANT_OPT_IN_ENABLE);
163     // WARNING: we've been deleted.
164   }
165 
OnPaint(gfx::Canvas * canvas)166   virtual void OnPaint(gfx::Canvas* canvas) {
167     canvas->Save();
168     canvas->TranslateInt(kOptInBackgroundHInset, kOptInBackgroundVInset);
169     bg_painter_->Paint(width() - kOptInBackgroundHInset * 2,
170                        height() - kOptInBackgroundVInset * 2, canvas);
171     canvas->DrawRectInt(ResourceBundle::toolbar_separator_color, 0, 0,
172                         width() - kOptInBackgroundHInset * 2,
173                         height() - kOptInBackgroundVInset * 2);
174     canvas->Restore();
175   }
176 
177  private:
178   // Creates and returns a button configured for the opt-in promo.
CreateButton(int id,const gfx::Font & font)179   views::View* CreateButton(int id, const gfx::Font& font) {
180     // NOTE: we can't use NativeButton as the popup is a layered window and
181     // native buttons don't draw  in layered windows.
182     // TODO: these buttons look crap. Figure out the right border/background to
183     // use.
184     views::TextButton* button =
185         new views::TextButton(this, UTF16ToWide(l10n_util::GetStringUTF16(id)));
186     button->set_border(new OptInButtonBorder());
187     button->SetNormalHasBorder(true);
188     button->set_tag(id);
189     button->SetFont(font);
190     button->set_animate_on_state_change(false);
191     return button;
192   }
193 
194   AutocompletePopupContentsView* contents_view_;
195   scoped_ptr<views::Painter> bg_painter_;
196 
197   DISALLOW_COPY_AND_ASSIGN(InstantOptInView);
198 };
199 
200 ////////////////////////////////////////////////////////////////////////////////
201 // AutocompletePopupContentsView, public:
202 
AutocompletePopupContentsView(const gfx::Font & font,AutocompleteEditView * edit_view,AutocompleteEditModel * edit_model,Profile * profile,const views::View * location_bar)203 AutocompletePopupContentsView::AutocompletePopupContentsView(
204     const gfx::Font& font,
205     AutocompleteEditView* edit_view,
206     AutocompleteEditModel* edit_model,
207     Profile* profile,
208     const views::View* location_bar)
209     : model_(new AutocompletePopupModel(this, edit_model, profile)),
210       opt_in_view_(NULL),
211       edit_view_(edit_view),
212       location_bar_(location_bar),
213       result_font_(font.DeriveFont(kEditFontAdjust)),
214       result_bold_font_(result_font_.DeriveFont(0, gfx::Font::BOLD)),
215       ignore_mouse_drag_(false),
216       ALLOW_THIS_IN_INITIALIZER_LIST(size_animation_(this)) {
217   // The following little dance is required because set_border() requires a
218   // pointer to a non-const object.
219   BubbleBorder* bubble_border = new BubbleBorder(BubbleBorder::NONE);
220   bubble_border_ = bubble_border;
221   set_border(bubble_border);
222   // The contents is owned by the LocationBarView.
223   set_parent_owned(false);
224 }
225 
~AutocompletePopupContentsView()226 AutocompletePopupContentsView::~AutocompletePopupContentsView() {
227   // We don't need to do anything with |popup_| here.  The OS either has already
228   // closed the window, in which case it's been deleted, or it will soon, in
229   // which case there's nothing we need to do.
230 }
231 
GetPopupBounds() const232 gfx::Rect AutocompletePopupContentsView::GetPopupBounds() const {
233   if (!size_animation_.is_animating())
234     return target_bounds_;
235 
236   gfx::Rect current_frame_bounds = start_bounds_;
237   int total_height_delta = target_bounds_.height() - start_bounds_.height();
238   // Round |current_height_delta| instead of truncating so we won't leave single
239   // white pixels at the bottom of the popup as long when animating very small
240   // height differences.
241   int current_height_delta = static_cast<int>(
242       size_animation_.GetCurrentValue() * total_height_delta - 0.5);
243   current_frame_bounds.set_height(
244       current_frame_bounds.height() + current_height_delta);
245   return current_frame_bounds;
246 }
247 
LayoutChildren()248 void AutocompletePopupContentsView::LayoutChildren() {
249   gfx::Rect contents_rect = GetContentsBounds();
250   int top = contents_rect.y();
251   for (int i = 0; i < child_count(); ++i) {
252     View* v = GetChildViewAt(i);
253     if (v->IsVisible()) {
254       v->SetBounds(contents_rect.x(), top, contents_rect.width(),
255                    v->GetPreferredSize().height());
256       top = v->bounds().bottom();
257     }
258   }
259 }
260 
261 ////////////////////////////////////////////////////////////////////////////////
262 // AutocompletePopupContentsView, AutocompletePopupView overrides:
263 
IsOpen() const264 bool AutocompletePopupContentsView::IsOpen() const {
265   return (popup_ != NULL);
266 }
267 
InvalidateLine(size_t line)268 void AutocompletePopupContentsView::InvalidateLine(size_t line) {
269   GetChildViewAt(static_cast<int>(line))->SchedulePaint();
270 }
271 
UpdatePopupAppearance()272 void AutocompletePopupContentsView::UpdatePopupAppearance() {
273   if (model_->result().empty()) {
274     // No matches, close any existing popup.
275     if (popup_ != NULL) {
276       size_animation_.Stop();
277       // NOTE: Do NOT use CloseNow() here, as we may be deep in a callstack
278       // triggered by the popup receiving a message (e.g. LBUTTONUP), and
279       // destroying the popup would cause us to read garbage when we unwind back
280       // to that level.
281       popup_->Close();  // This will eventually delete the popup.
282       popup_.reset();
283     }
284     return;
285   }
286 
287   // Update the match cached by each row, in the process of doing so make sure
288   // we have enough row views.
289   size_t child_rv_count = child_count();
290   if (opt_in_view_) {
291     DCHECK(child_rv_count > 0);
292     child_rv_count--;
293   }
294   for (size_t i = 0; i < model_->result().size(); ++i) {
295     AutocompleteResultView* result_view;
296     if (i >= child_rv_count) {
297       result_view =
298           CreateResultView(this, i, result_font_, result_bold_font_);
299       AddChildViewAt(result_view, static_cast<int>(i));
300     } else {
301       result_view = static_cast<AutocompleteResultView*>(GetChildViewAt(i));
302       result_view->SetVisible(true);
303     }
304     result_view->SetMatch(GetMatchAtIndex(i));
305   }
306   for (size_t i = model_->result().size(); i < child_rv_count; ++i)
307     GetChildViewAt(i)->SetVisible(false);
308 
309   PromoCounter* counter = model_->profile()->GetInstantPromoCounter();
310   if (!opt_in_view_ && counter && counter->ShouldShow(base::Time::Now())) {
311     opt_in_view_ = new InstantOptInView(this, result_bold_font_, result_font_);
312     AddChildView(opt_in_view_);
313   } else if (opt_in_view_ && (!counter ||
314                               !counter->ShouldShow(base::Time::Now()))) {
315     delete opt_in_view_;
316     opt_in_view_ = NULL;
317   }
318 
319   gfx::Rect new_target_bounds = CalculateTargetBounds(CalculatePopupHeight());
320 
321   // If we're animating and our target height changes, reset the animation.
322   // NOTE: If we just reset blindly on _every_ update, then when the user types
323   // rapidly we could get "stuck" trying repeatedly to animate shrinking by the
324   // last few pixels to get to one visible result.
325   if (new_target_bounds.height() != target_bounds_.height())
326     size_animation_.Reset();
327   target_bounds_ = new_target_bounds;
328 
329   if (popup_ == NULL) {
330     // If the popup is currently closed, we need to create it.
331     popup_ = (new AutocompletePopupClass)->AsWeakPtr();
332     views::Widget::CreateParams params(views::Widget::CreateParams::TYPE_POPUP);
333     params.can_activate = false;
334     params.transparent = true;
335     popup_->SetCreateParams(params);
336     popup_->Init(location_bar_->GetWidget()->GetNativeView(), GetPopupBounds());
337     popup_->SetContentsView(this);
338     popup_->MoveAbove(popup_->GetRelativeWindowForPopup(
339         edit_view_->GetNativeView()));
340     popup_->Show();
341   } else {
342     // Animate the popup shrinking, but don't animate growing larger since that
343     // would make the popup feel less responsive.
344     start_bounds_ = GetWidget()->GetWindowScreenBounds();
345     if (target_bounds_.height() < start_bounds_.height())
346       size_animation_.Show();
347     else
348       start_bounds_ = target_bounds_;
349     popup_->SetBounds(GetPopupBounds());
350   }
351 
352   SchedulePaint();
353 }
354 
GetTargetBounds()355 gfx::Rect AutocompletePopupContentsView::GetTargetBounds() {
356   return target_bounds_;
357 }
358 
PaintUpdatesNow()359 void AutocompletePopupContentsView::PaintUpdatesNow() {
360   // TODO(beng): remove this from the interface.
361 }
362 
OnDragCanceled()363 void AutocompletePopupContentsView::OnDragCanceled() {
364   ignore_mouse_drag_ = true;
365 }
366 
367 ////////////////////////////////////////////////////////////////////////////////
368 // AutocompletePopupContentsView, AutocompleteResultViewModel implementation:
369 
IsSelectedIndex(size_t index) const370 bool AutocompletePopupContentsView::IsSelectedIndex(size_t index) const {
371   return HasMatchAt(index) ? index == model_->selected_line() : false;
372 }
373 
IsHoveredIndex(size_t index) const374 bool AutocompletePopupContentsView::IsHoveredIndex(size_t index) const {
375   return HasMatchAt(index) ? index == model_->hovered_line() : false;
376 }
377 
GetIconIfExtensionMatch(size_t index) const378 const SkBitmap* AutocompletePopupContentsView::GetIconIfExtensionMatch(
379     size_t index) const {
380   if (!HasMatchAt(index))
381     return NULL;
382   return model_->GetIconIfExtensionMatch(GetMatchAtIndex(index));
383 }
384 
385 ////////////////////////////////////////////////////////////////////////////////
386 // AutocompletePopupContentsView, AnimationDelegate implementation:
387 
AnimationProgressed(const ui::Animation * animation)388 void AutocompletePopupContentsView::AnimationProgressed(
389     const ui::Animation* animation) {
390   // We should only be running the animation when the popup is already visible.
391   DCHECK(popup_ != NULL);
392   popup_->SetBounds(GetPopupBounds());
393 }
394 
395 ////////////////////////////////////////////////////////////////////////////////
396 // AutocompletePopupContentsView, views::View overrides:
397 
Layout()398 void AutocompletePopupContentsView::Layout() {
399   UpdateBlurRegion();
400 
401   // Size our children to the available content area.
402   LayoutChildren();
403 
404   // We need to manually schedule a paint here since we are a layered window and
405   // won't implicitly require painting until we ask for one.
406   SchedulePaint();
407 }
408 
GetEventHandlerForPoint(const gfx::Point & point)409 views::View* AutocompletePopupContentsView::GetEventHandlerForPoint(
410     const gfx::Point& point) {
411   // If there is no opt in view, then we want all mouse events. Otherwise let
412   // any descendants of the opt-in view get mouse events.
413   if (!opt_in_view_)
414     return this;
415 
416   views::View* child = views::View::GetEventHandlerForPoint(point);
417   views::View* ancestor = child;
418   while (ancestor && ancestor != opt_in_view_)
419     ancestor = ancestor->parent();
420   return ancestor ? child : this;
421 }
422 
OnMousePressed(const views::MouseEvent & event)423 bool AutocompletePopupContentsView::OnMousePressed(
424     const views::MouseEvent& event) {
425   ignore_mouse_drag_ = false;  // See comment on |ignore_mouse_drag_| in header.
426   if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) {
427     size_t index = GetIndexForPoint(event.location());
428     model_->SetHoveredLine(index);
429     if (HasMatchAt(index) && event.IsLeftMouseButton())
430       model_->SetSelectedLine(index, false, false);
431   }
432   return true;
433 }
434 
OnMouseDragged(const views::MouseEvent & event)435 bool AutocompletePopupContentsView::OnMouseDragged(
436     const views::MouseEvent& event) {
437   if (event.IsLeftMouseButton() || event.IsMiddleMouseButton()) {
438     size_t index = GetIndexForPoint(event.location());
439     model_->SetHoveredLine(index);
440     if (!ignore_mouse_drag_ && HasMatchAt(index) && event.IsLeftMouseButton())
441       model_->SetSelectedLine(index, false, false);
442   }
443   return true;
444 }
445 
OnMouseReleased(const views::MouseEvent & event)446 void AutocompletePopupContentsView::OnMouseReleased(
447     const views::MouseEvent& event) {
448   if (ignore_mouse_drag_) {
449     OnMouseCaptureLost();
450     return;
451   }
452 
453   size_t index = GetIndexForPoint(event.location());
454   if (event.IsOnlyMiddleMouseButton())
455     OpenIndex(index, NEW_BACKGROUND_TAB);
456   else if (event.IsOnlyLeftMouseButton())
457     OpenIndex(index, CURRENT_TAB);
458 }
459 
OnMouseCaptureLost()460 void AutocompletePopupContentsView::OnMouseCaptureLost() {
461   ignore_mouse_drag_ = false;
462 }
463 
OnMouseMoved(const views::MouseEvent & event)464 void AutocompletePopupContentsView::OnMouseMoved(
465     const views::MouseEvent& event) {
466   model_->SetHoveredLine(GetIndexForPoint(event.location()));
467 }
468 
OnMouseEntered(const views::MouseEvent & event)469 void AutocompletePopupContentsView::OnMouseEntered(
470     const views::MouseEvent& event) {
471   model_->SetHoveredLine(GetIndexForPoint(event.location()));
472 }
473 
OnMouseExited(const views::MouseEvent & event)474 void AutocompletePopupContentsView::OnMouseExited(
475     const views::MouseEvent& event) {
476   model_->SetHoveredLine(AutocompletePopupModel::kNoMatch);
477 }
478 
479 ////////////////////////////////////////////////////////////////////////////////
480 // AutocompletePopupContentsView, protected:
481 
PaintResultViews(gfx::CanvasSkia * canvas)482 void AutocompletePopupContentsView::PaintResultViews(gfx::CanvasSkia* canvas) {
483   canvas->drawColor(AutocompleteResultView::GetColor(
484       AutocompleteResultView::NORMAL, AutocompleteResultView::BACKGROUND));
485   View::PaintChildren(canvas);
486 }
487 
CalculatePopupHeight()488 int AutocompletePopupContentsView::CalculatePopupHeight() {
489   DCHECK_GE(static_cast<size_t>(child_count()), model_->result().size());
490   int popup_height = 0;
491   for (size_t i = 0; i < model_->result().size(); ++i)
492     popup_height += GetChildViewAt(i)->GetPreferredSize().height();
493   return popup_height +
494       (opt_in_view_ ? opt_in_view_->GetPreferredSize().height() : 0);
495 }
496 
CreateResultView(AutocompleteResultViewModel * model,int model_index,const gfx::Font & font,const gfx::Font & bold_font)497 AutocompleteResultView* AutocompletePopupContentsView::CreateResultView(
498     AutocompleteResultViewModel* model,
499     int model_index,
500     const gfx::Font& font,
501     const gfx::Font& bold_font) {
502   return new AutocompleteResultView(model, model_index, font, bold_font);
503 }
504 
505 ////////////////////////////////////////////////////////////////////////////////
506 // AutocompletePopupContentsView, views::View overrides, protected:
507 
OnPaint(gfx::Canvas * canvas)508 void AutocompletePopupContentsView::OnPaint(gfx::Canvas* canvas) {
509   // We paint our children in an unconventional way.
510   //
511   // Because the border of this view creates an anti-aliased round-rect region
512   // for the contents, we need to render our rectangular result child views into
513   // this round rect region. We can't use a simple clip because clipping is
514   // 1-bit and we get nasty jagged edges.
515   //
516   // Instead, we paint all our children into a second canvas and use that as a
517   // shader to fill a path representing the round-rect clipping region. This
518   // yields a nice anti-aliased edge.
519   gfx::CanvasSkia contents_canvas(width(), height(), true);
520   PaintResultViews(&contents_canvas);
521 
522   // We want the contents background to be slightly transparent so we can see
523   // the blurry glass effect on DWM systems behind. We do this _after_ we paint
524   // the children since they paint text, and GDI will reset this alpha data if
525   // we paint text after this call.
526   MakeCanvasTransparent(&contents_canvas);
527 
528   // Now paint the contents of the contents canvas into the actual canvas.
529   SkPaint paint;
530   paint.setAntiAlias(true);
531 
532   SkShader* shader = SkShader::CreateBitmapShader(
533       contents_canvas.getDevice()->accessBitmap(false),
534       SkShader::kClamp_TileMode,
535       SkShader::kClamp_TileMode);
536   paint.setShader(shader);
537   shader->unref();
538 
539   gfx::Path path;
540   MakeContentsPath(&path, GetContentsBounds());
541   canvas->AsCanvasSkia()->drawPath(path, paint);
542 
543   // Now we paint the border, so it will be alpha-blended atop the contents.
544   // This looks slightly better in the corners than drawing the contents atop
545   // the border.
546   OnPaintBorder(canvas);
547 }
548 
PaintChildren(gfx::Canvas * canvas)549 void AutocompletePopupContentsView::PaintChildren(gfx::Canvas* canvas) {
550   // We paint our children inside OnPaint().
551 }
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 // AutocompletePopupContentsView, private:
555 
HasMatchAt(size_t index) const556 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const {
557   return index < model_->result().size();
558 }
559 
GetMatchAtIndex(size_t index) const560 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex(
561     size_t index) const {
562   return model_->result().match_at(index);
563 }
564 
MakeContentsPath(gfx::Path * path,const gfx::Rect & bounding_rect)565 void AutocompletePopupContentsView::MakeContentsPath(
566     gfx::Path* path,
567     const gfx::Rect& bounding_rect) {
568   SkRect rect;
569   rect.set(SkIntToScalar(bounding_rect.x()),
570            SkIntToScalar(bounding_rect.y()),
571            SkIntToScalar(bounding_rect.right()),
572            SkIntToScalar(bounding_rect.bottom()));
573 
574   SkScalar radius = SkIntToScalar(BubbleBorder::GetCornerRadius());
575   path->addRoundRect(rect, radius, radius);
576 }
577 
UpdateBlurRegion()578 void AutocompletePopupContentsView::UpdateBlurRegion() {
579 #if defined(OS_WIN)
580   // We only support background blurring on Vista with Aero-Glass enabled.
581   if (!views::WidgetWin::IsAeroGlassEnabled() || !GetWidget())
582     return;
583 
584   // Provide a blurred background effect within the contents region of the
585   // popup.
586   DWM_BLURBEHIND bb = {0};
587   bb.dwFlags = DWM_BB_ENABLE | DWM_BB_BLURREGION;
588   bb.fEnable = true;
589 
590   // Translate the contents rect into widget coordinates, since that's what
591   // DwmEnableBlurBehindWindow expects a region in.
592   gfx::Rect contents_rect = GetContentsBounds();
593   gfx::Point origin(contents_rect.origin());
594   views::View::ConvertPointToWidget(this, &origin);
595   contents_rect.set_origin(origin);
596 
597   gfx::Path contents_path;
598   MakeContentsPath(&contents_path, contents_rect);
599   base::win::ScopedGDIObject<HRGN> popup_region;
600   popup_region.Set(contents_path.CreateNativeRegion());
601   bb.hRgnBlur = popup_region.Get();
602   DwmEnableBlurBehindWindow(GetWidget()->GetNativeView(), &bb);
603 #endif
604 }
605 
MakeCanvasTransparent(gfx::Canvas * canvas)606 void AutocompletePopupContentsView::MakeCanvasTransparent(
607     gfx::Canvas* canvas) {
608   // Allow the window blur effect to show through the popup background.
609   SkAlpha alpha = GetThemeProvider()->ShouldUseNativeFrame() ?
610       kGlassPopupAlpha : kOpaquePopupAlpha;
611   canvas->AsCanvasSkia()->drawColor(SkColorSetA(
612       AutocompleteResultView::GetColor(AutocompleteResultView::NORMAL,
613       AutocompleteResultView::BACKGROUND), alpha), SkXfermode::kDstIn_Mode);
614 }
615 
OpenIndex(size_t index,WindowOpenDisposition disposition)616 void AutocompletePopupContentsView::OpenIndex(
617     size_t index,
618     WindowOpenDisposition disposition) {
619   if (!HasMatchAt(index))
620     return;
621 
622   const AutocompleteMatch& match = model_->result().match_at(index);
623   // OpenURL() may close the popup, which will clear the result set and, by
624   // extension, |match| and its contents.  So copy the relevant strings out to
625   // make sure they stay alive until the call completes.
626   const GURL url(match.destination_url);
627   string16 keyword;
628   const bool is_keyword_hint = model_->GetKeywordForMatch(match, &keyword);
629   edit_view_->OpenURL(url, disposition, match.transition, GURL(), index,
630                       is_keyword_hint ? string16() : keyword);
631 }
632 
GetIndexForPoint(const gfx::Point & point)633 size_t AutocompletePopupContentsView::GetIndexForPoint(
634     const gfx::Point& point) {
635   if (!HitTest(point))
636     return AutocompletePopupModel::kNoMatch;
637 
638   int nb_match = model_->result().size();
639   DCHECK(nb_match <= child_count());
640   for (int i = 0; i < nb_match; ++i) {
641     views::View* child = GetChildViewAt(i);
642     gfx::Point point_in_child_coords(point);
643     View::ConvertPointToView(this, child, &point_in_child_coords);
644     if (child->HitTest(point_in_child_coords))
645       return i;
646   }
647   return AutocompletePopupModel::kNoMatch;
648 }
649 
CalculateTargetBounds(int h)650 gfx::Rect AutocompletePopupContentsView::CalculateTargetBounds(int h) {
651   gfx::Rect location_bar_bounds(location_bar_->GetContentsBounds());
652   const views::Border* border = location_bar_->border();
653   if (border) {
654     // Adjust for the border so that the bubble and location bar borders are
655     // aligned.
656     gfx::Insets insets;
657     border->GetInsets(&insets);
658     location_bar_bounds.Inset(insets.left(), 0, insets.right(), 0);
659   } else {
660     // The normal location bar is drawn using a background graphic that includes
661     // the border, so we inset by enough to make the edges line up, and the
662     // bubble appear at the same height as the Star bubble.
663     location_bar_bounds.Inset(LocationBarView::kNormalHorizontalEdgeThickness,
664                               0);
665   }
666   gfx::Point location_bar_origin(location_bar_bounds.origin());
667   views::View::ConvertPointToScreen(location_bar_, &location_bar_origin);
668   location_bar_bounds.set_origin(location_bar_origin);
669   return bubble_border_->GetBounds(
670       location_bar_bounds, gfx::Size(location_bar_bounds.width(), h));
671 }
672 
UserPressedOptIn(bool opt_in)673 void AutocompletePopupContentsView::UserPressedOptIn(bool opt_in) {
674   delete opt_in_view_;
675   opt_in_view_ = NULL;
676   PromoCounter* counter = model_->profile()->GetInstantPromoCounter();
677   DCHECK(counter);
678   counter->Hide();
679   if (opt_in) {
680     browser::ShowInstantConfirmDialogIfNecessary(
681         location_bar_->GetWindow()->GetNativeWindow(), model_->profile());
682   }
683   UpdatePopupAppearance();
684 }
685