Home | History | Annotate | Download | only in autocomplete
      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:
     79   OptInButtonBorder() {
     80     border_painter_.reset(CreatePainter(IDR_OPT_IN_BUTTON));
     81     border_pushed_painter_.reset(CreatePainter(IDR_OPT_IN_BUTTON_P));
     82   }
     83 
     84   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 
     95   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|.
    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:
    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 
    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 
    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.
    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 
    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 
    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 
    232 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 
    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 
    264 bool AutocompletePopupContentsView::IsOpen() const {
    265   return (popup_ != NULL);
    266 }
    267 
    268 void AutocompletePopupContentsView::InvalidateLine(size_t line) {
    269   GetChildViewAt(static_cast<int>(line))->SchedulePaint();
    270 }
    271 
    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 
    355 gfx::Rect AutocompletePopupContentsView::GetTargetBounds() {
    356   return target_bounds_;
    357 }
    358 
    359 void AutocompletePopupContentsView::PaintUpdatesNow() {
    360   // TODO(beng): remove this from the interface.
    361 }
    362 
    363 void AutocompletePopupContentsView::OnDragCanceled() {
    364   ignore_mouse_drag_ = true;
    365 }
    366 
    367 ////////////////////////////////////////////////////////////////////////////////
    368 // AutocompletePopupContentsView, AutocompleteResultViewModel implementation:
    369 
    370 bool AutocompletePopupContentsView::IsSelectedIndex(size_t index) const {
    371   return HasMatchAt(index) ? index == model_->selected_line() : false;
    372 }
    373 
    374 bool AutocompletePopupContentsView::IsHoveredIndex(size_t index) const {
    375   return HasMatchAt(index) ? index == model_->hovered_line() : false;
    376 }
    377 
    378 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 
    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 
    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 
    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 
    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 
    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 
    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 
    460 void AutocompletePopupContentsView::OnMouseCaptureLost() {
    461   ignore_mouse_drag_ = false;
    462 }
    463 
    464 void AutocompletePopupContentsView::OnMouseMoved(
    465     const views::MouseEvent& event) {
    466   model_->SetHoveredLine(GetIndexForPoint(event.location()));
    467 }
    468 
    469 void AutocompletePopupContentsView::OnMouseEntered(
    470     const views::MouseEvent& event) {
    471   model_->SetHoveredLine(GetIndexForPoint(event.location()));
    472 }
    473 
    474 void AutocompletePopupContentsView::OnMouseExited(
    475     const views::MouseEvent& event) {
    476   model_->SetHoveredLine(AutocompletePopupModel::kNoMatch);
    477 }
    478 
    479 ////////////////////////////////////////////////////////////////////////////////
    480 // AutocompletePopupContentsView, protected:
    481 
    482 void AutocompletePopupContentsView::PaintResultViews(gfx::CanvasSkia* canvas) {
    483   canvas->drawColor(AutocompleteResultView::GetColor(
    484       AutocompleteResultView::NORMAL, AutocompleteResultView::BACKGROUND));
    485   View::PaintChildren(canvas);
    486 }
    487 
    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 
    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 
    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 
    549 void AutocompletePopupContentsView::PaintChildren(gfx::Canvas* canvas) {
    550   // We paint our children inside OnPaint().
    551 }
    552 
    553 ////////////////////////////////////////////////////////////////////////////////
    554 // AutocompletePopupContentsView, private:
    555 
    556 bool AutocompletePopupContentsView::HasMatchAt(size_t index) const {
    557   return index < model_->result().size();
    558 }
    559 
    560 const AutocompleteMatch& AutocompletePopupContentsView::GetMatchAtIndex(
    561     size_t index) const {
    562   return model_->result().match_at(index);
    563 }
    564 
    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 
    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 
    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 
    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 
    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 
    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 
    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