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/location_bar/location_bar_view.h"
6
7#include <algorithm>
8#include <map>
9
10#include "base/command_line.h"
11#include "base/i18n/rtl.h"
12#include "base/prefs/pref_service.h"
13#include "base/stl_util.h"
14#include "base/strings/utf_string_conversions.h"
15#include "chrome/app/chrome_command_ids.h"
16#include "chrome/browser/chrome_notification_types.h"
17#include "chrome/browser/command_updater.h"
18#include "chrome/browser/defaults.h"
19#include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
20#include "chrome/browser/extensions/location_bar_controller.h"
21#include "chrome/browser/extensions/script_bubble_controller.h"
22#include "chrome/browser/extensions/tab_helper.h"
23#include "chrome/browser/favicon/favicon_tab_helper.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/search_engines/template_url.h"
26#include "chrome/browser/search_engines/template_url_service.h"
27#include "chrome/browser/search_engines/template_url_service_factory.h"
28#include "chrome/browser/ui/browser.h"
29#include "chrome/browser/ui/browser_finder.h"
30#include "chrome/browser/ui/browser_instant_controller.h"
31#include "chrome/browser/ui/browser_window.h"
32#include "chrome/browser/ui/omnibox/alternate_nav_url_fetcher.h"
33#include "chrome/browser/ui/omnibox/location_bar_util.h"
34#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
35#include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
36#include "chrome/browser/ui/tabs/tab_strip_model.h"
37#include "chrome/browser/ui/view_ids.h"
38#include "chrome/browser/ui/views/bookmarks/bookmark_prompt_view.h"
39#include "chrome/browser/ui/views/browser_dialogs.h"
40#include "chrome/browser/ui/views/extensions/extension_popup.h"
41#include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
42#include "chrome/browser/ui/views/location_bar/ev_bubble_view.h"
43#include "chrome/browser/ui/views/location_bar/generated_credit_card_view.h"
44#include "chrome/browser/ui/views/location_bar/keyword_hint_view.h"
45#include "chrome/browser/ui/views/location_bar/location_bar_layout.h"
46#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
47#include "chrome/browser/ui/views/location_bar/open_pdf_in_reader_view.h"
48#include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
49#include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h"
50#include "chrome/browser/ui/views/location_bar/script_bubble_icon_view.h"
51#include "chrome/browser/ui/views/location_bar/selected_keyword_view.h"
52#include "chrome/browser/ui/views/location_bar/star_view.h"
53#include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h"
54#include "chrome/browser/ui/views/location_bar/zoom_view.h"
55#include "chrome/browser/ui/views/omnibox/omnibox_view_views.h"
56#include "chrome/browser/ui/views/omnibox/omnibox_views.h"
57#include "chrome/browser/ui/zoom/zoom_controller.h"
58#include "chrome/common/extensions/feature_switch.h"
59#include "chrome/common/pref_names.h"
60#include "content/public/browser/notification_service.h"
61#include "content/public/browser/render_widget_host_view.h"
62#include "content/public/browser/web_contents.h"
63#include "grit/generated_resources.h"
64#include "grit/theme_resources.h"
65#include "ui/base/accessibility/accessible_view_state.h"
66#include "ui/base/dragdrop/drag_drop_types.h"
67#include "ui/base/events/event.h"
68#include "ui/base/l10n/l10n_util.h"
69#include "ui/base/layout.h"
70#include "ui/base/resource/resource_bundle.h"
71#include "ui/base/theme_provider.h"
72#include "ui/gfx/canvas.h"
73#include "ui/gfx/color_utils.h"
74#include "ui/gfx/image/image.h"
75#include "ui/gfx/image/image_skia_operations.h"
76#include "ui/gfx/skia_util.h"
77#include "ui/native_theme/native_theme.h"
78#include "ui/views/background.h"
79#include "ui/views/border.h"
80#include "ui/views/button_drag_utils.h"
81#include "ui/views/controls/button/image_button.h"
82#include "ui/views/controls/label.h"
83#include "ui/views/controls/textfield/textfield.h"
84#include "ui/views/widget/widget.h"
85#include "ui/views/window/non_client_view.h"
86
87#if defined(OS_WIN)
88#include "base/win/scoped_hdc.h"
89#include "base/win/scoped_select_object.h"
90#include "ui/native_theme/native_theme_win.h"
91#endif
92
93#if defined(OS_WIN) && !defined(USE_AURA)
94#include "chrome/browser/ui/views/omnibox/omnibox_view_win.h"
95#endif
96
97#if !defined(OS_CHROMEOS)
98#include "chrome/browser/ui/views/first_run_bubble.h"
99#endif
100
101#if defined(USE_AURA)
102#include "ui/compositor/layer.h"
103#include "ui/compositor/scoped_layer_animation_settings.h"
104#endif
105
106using content::WebContents;
107using views::View;
108
109
110namespace {
111
112Browser* GetBrowserFromDelegate(LocationBarView::Delegate* delegate) {
113  WebContents* contents = delegate->GetWebContents();
114  return contents ? chrome::FindBrowserWithWebContents(contents) : NULL;
115}
116
117// Given a containing |height| and a base |font_list|, shrinks the fonts until
118// the primary font will fit within |height| while having its cap height
119// vertically centered.  Returns the |font_y_offset| needed to produce this
120// centering.
121void CalculateFontAndOffsetForHeight(int height,
122                                     gfx::FontList* font_list,
123                                     int* font_y_offset) {
124#if defined(OS_WIN)
125  base::win::ScopedGetDC screen_dc(NULL);
126#endif
127
128  while (true) {
129    // TODO(pkasting): Expand the gfx::Font metrics (and underlying Skia
130    // metrics) enough to expose the cap height directly.
131#if defined(OS_WIN)
132    const gfx::Font& font = font_list->GetPrimaryFont();
133    base::win::ScopedSelectObject font_in_dc(screen_dc, font.GetNativeFont());
134    TEXTMETRIC tm = {0};
135    GetTextMetrics(screen_dc, &tm);
136    int cap_height = font.GetBaseline() - tm.tmInternalLeading;
137    *font_y_offset = ((height - cap_height) / 2) - tm.tmInternalLeading;
138#else
139    // Without cap height available, we fall back to centering the full height.
140    *font_y_offset = (height - font_list->GetHeight()) / 2;
141#endif
142
143    const int font_size = font_list->GetFontSize();
144    if (((*font_y_offset >= 0) &&
145         ((*font_y_offset + font_list->GetHeight()) <= height)) ||
146        (font_size <= 1))
147      return;
148    *font_list = font_list->DeriveFontListWithSize(font_size - 1);
149  }
150}
151
152}  // namespace
153
154
155// LocationBarView -----------------------------------------------------------
156
157// static
158const int LocationBarView::kNormalEdgeThickness = 2;
159const int LocationBarView::kPopupEdgeThickness = 1;
160const int LocationBarView::kIconInternalPadding = 2;
161const int LocationBarView::kBubblePadding = 1;
162const char LocationBarView::kViewClassName[] = "LocationBarView";
163
164LocationBarView::LocationBarView(Browser* browser,
165                                 Profile* profile,
166                                 CommandUpdater* command_updater,
167                                 ToolbarModel* model,
168                                 Delegate* delegate,
169                                 bool is_popup_mode)
170    : browser_(browser),
171      profile_(profile),
172      command_updater_(command_updater),
173      model_(model),
174      delegate_(delegate),
175      disposition_(CURRENT_TAB),
176      transition_(content::PageTransitionFromInt(
177          content::PAGE_TRANSITION_TYPED |
178          content::PAGE_TRANSITION_FROM_ADDRESS_BAR)),
179      location_icon_view_(NULL),
180      ev_bubble_view_(NULL),
181      location_entry_view_(NULL),
182      ime_inline_autocomplete_view_(NULL),
183      selected_keyword_view_(NULL),
184      suggested_text_view_(NULL),
185      keyword_hint_view_(NULL),
186      mic_search_view_(NULL),
187      zoom_view_(NULL),
188      generated_credit_card_view_(NULL),
189      open_pdf_in_reader_view_(NULL),
190      script_bubble_icon_view_(NULL),
191      star_view_(NULL),
192      is_popup_mode_(is_popup_mode),
193      show_focus_rect_(false),
194      template_url_service_(NULL),
195      animation_offset_(0),
196      weak_ptr_factory_(this) {
197  if (!views::Textfield::IsViewsTextfieldEnabled())
198    set_id(VIEW_ID_OMNIBOX);
199
200  const int kOmniboxBorderImages[] = IMAGE_GRID(IDR_OMNIBOX_BORDER);
201  const int kOmniboxPopupImages[] = IMAGE_GRID(IDR_OMNIBOX_POPUP_BORDER);
202  background_border_painter_.reset(
203      views::Painter::CreateImageGridPainter(
204          is_popup_mode_ ? kOmniboxPopupImages : kOmniboxBorderImages));
205#if defined(OS_CHROMEOS)
206  if (!is_popup_mode_) {
207    const int kOmniboxFillingImages[] = IMAGE_GRID(IDR_OMNIBOX_FILLING);
208    background_filling_painter_.reset(
209        views::Painter::CreateImageGridPainter(kOmniboxFillingImages));
210  }
211#endif
212
213  edit_bookmarks_enabled_.Init(
214      prefs::kEditBookmarksEnabled,
215      profile_->GetPrefs(),
216      base::Bind(&LocationBarView::Update,
217                 base::Unretained(this),
218                 static_cast<content::WebContents*>(NULL)));
219
220  if (browser_)
221    browser_->search_model()->AddObserver(this);
222}
223
224LocationBarView::~LocationBarView() {
225  if (template_url_service_)
226    template_url_service_->RemoveObserver(this);
227  if (browser_)
228    browser_->search_model()->RemoveObserver(this);
229}
230
231// static
232void LocationBarView::InitTouchableLocationBarChildView(views::View* view) {
233  int horizontal_padding = GetBuiltInHorizontalPaddingForChildViews();
234  if (horizontal_padding != 0) {
235    view->set_border(views::Border::CreateEmptyBorder(
236        3, horizontal_padding, 3, horizontal_padding));
237  }
238}
239
240void LocationBarView::Init() {
241  // We need to be in a Widget, otherwise GetNativeTheme() may change and we're
242  // not prepared for that.
243  DCHECK(GetWidget());
244
245  location_icon_view_ = new LocationIconView(this);
246  location_icon_view_->set_drag_controller(this);
247  AddChildView(location_icon_view_);
248
249  // Determine the main font.
250  gfx::FontList font_list = ResourceBundle::GetSharedInstance().GetFontList(
251      ResourceBundle::BaseFont);
252  const int current_font_size = font_list.GetFontSize();
253  const int desired_font_size = browser_defaults::kOmniboxFontPixelSize;
254  if (current_font_size < desired_font_size)
255    font_list = font_list.DeriveFontListWithSize(desired_font_size);
256  // Shrink large fonts to make them fit.
257  // TODO(pkasting): Stretch the location bar instead in this case.
258  int location_height = GetInternalHeight(true);
259  int font_y_offset;
260  CalculateFontAndOffsetForHeight(location_height, &font_list, &font_y_offset);
261  const gfx::Font& font = font_list.GetPrimaryFont();
262
263  // Determine the font for use inside the bubbles.
264  gfx::FontList bubble_font_list(font_list);
265  int bubble_font_y_offset;
266  // The bubble background images have 1 px thick edges, which we don't want to
267  // overlap.
268  const int kBubbleInteriorVerticalPadding = 1;
269  CalculateFontAndOffsetForHeight(
270      location_height - ((kBubblePadding + kBubbleInteriorVerticalPadding) * 2),
271      &bubble_font_list, &bubble_font_y_offset);
272  bubble_font_y_offset += kBubbleInteriorVerticalPadding;
273  const gfx::Font& bubble_font = font_list.GetPrimaryFont();
274
275  const SkColor background_color =
276      GetColor(ToolbarModel::NONE, LocationBarView::BACKGROUND);
277  ev_bubble_view_ = new EVBubbleView(
278      bubble_font, bubble_font_y_offset,
279      GetColor(ToolbarModel::EV_SECURE, SECURITY_TEXT), background_color, this);
280  ev_bubble_view_->set_drag_controller(this);
281  AddChildView(ev_bubble_view_);
282
283  // Initialize the Omnibox view.
284  location_entry_.reset(CreateOmniboxView(this, model_, profile_,
285      command_updater_, is_popup_mode_, this, font_list, font_y_offset));
286  SetLocationEntryFocusable(true);
287  location_entry_view_ = location_entry_->AddToView(this);
288
289  // Initialize the inline autocomplete view which is visible only when IME is
290  // turned on.  Use the same font with the omnibox and highlighted background.
291  ime_inline_autocomplete_view_ = new views::Label(string16(), font);
292  {
293    // views::Label (|ime_inline_autocomplete_view_|) supports only gfx::Font
294    // and ignores the rest of fonts but the first in |font_list| while
295    // views::Textfield (|location_entry_view_|) supports gfx::FontList and
296    // layouts text based on all fonts in the list.  Thus the font height and
297    // baseline can be different between them.  We add padding to align them
298    // on the same baseline.
299    // TODO(yukishiino): Remove this hack once views::Label supports
300    // gfx::FontList.
301    const int baseline_diff = location_entry_view_->GetBaseline() -
302        ime_inline_autocomplete_view_->GetBaseline();
303    ime_inline_autocomplete_view_->set_border(
304        views::Border::CreateEmptyBorder(
305            font_y_offset + baseline_diff, 0, 0, 0));
306  }
307  ime_inline_autocomplete_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
308  ime_inline_autocomplete_view_->SetAutoColorReadabilityEnabled(false);
309  ime_inline_autocomplete_view_->set_background(
310      views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
311          ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)));
312  ime_inline_autocomplete_view_->SetEnabledColor(
313      GetNativeTheme()->GetSystemColor(
314          ui::NativeTheme::kColorId_TextfieldSelectionColor));
315  ime_inline_autocomplete_view_->SetVisible(false);
316  AddChildView(ime_inline_autocomplete_view_);
317
318  const SkColor text_color = GetColor(ToolbarModel::NONE, TEXT);
319  selected_keyword_view_ = new SelectedKeywordView(
320      bubble_font, bubble_font_y_offset, text_color, background_color,
321      profile_);
322  AddChildView(selected_keyword_view_);
323
324  suggested_text_view_ = new views::Label(string16(), font);
325  suggested_text_view_->set_border(
326      views::Border::CreateEmptyBorder(font_y_offset, 0, 0, 0));
327  suggested_text_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
328  suggested_text_view_->SetAutoColorReadabilityEnabled(false);
329  suggested_text_view_->SetEnabledColor(GetColor(
330      ToolbarModel::NONE, LocationBarView::DEEMPHASIZED_TEXT));
331  suggested_text_view_->SetVisible(false);
332  AddChildView(suggested_text_view_);
333
334  keyword_hint_view_ = new KeywordHintView(
335      profile_, font, font_y_offset,
336      GetColor(ToolbarModel::NONE, LocationBarView::DEEMPHASIZED_TEXT),
337      background_color);
338  AddChildView(keyword_hint_view_);
339
340  mic_search_view_ = new views::ImageButton(this);
341  mic_search_view_->set_id(VIEW_ID_MIC_SEARCH_BUTTON);
342  mic_search_view_->set_accessibility_focusable(true);
343  mic_search_view_->SetTooltipText(
344      l10n_util::GetStringUTF16(IDS_TOOLTIP_MIC_SEARCH));
345  mic_search_view_->SetImage(
346      views::Button::STATE_NORMAL,
347      ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
348          IDR_OMNIBOX_MIC_SEARCH));
349  mic_search_view_->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
350                                      views::ImageButton::ALIGN_MIDDLE);
351  mic_search_view_->SetVisible(false);
352  InitTouchableLocationBarChildView(mic_search_view_);
353  AddChildView(mic_search_view_);
354
355  for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
356    ContentSettingImageView* content_blocked_view =
357        new ContentSettingImageView(static_cast<ContentSettingsType>(i), this,
358                                    bubble_font, bubble_font_y_offset,
359                                    text_color, background_color);
360    content_setting_views_.push_back(content_blocked_view);
361    content_blocked_view->SetVisible(false);
362    AddChildView(content_blocked_view);
363  }
364
365  generated_credit_card_view_ = new GeneratedCreditCardView(model_, delegate_);
366  AddChildView(generated_credit_card_view_);
367
368  zoom_view_ = new ZoomView(model_, delegate_);
369  zoom_view_->set_id(VIEW_ID_ZOOM_BUTTON);
370  AddChildView(zoom_view_);
371
372  open_pdf_in_reader_view_ = new OpenPDFInReaderView(this);
373  AddChildView(open_pdf_in_reader_view_);
374
375  script_bubble_icon_view_ = new ScriptBubbleIconView(delegate());
376  script_bubble_icon_view_->SetVisible(false);
377  AddChildView(script_bubble_icon_view_);
378
379  star_view_ = new StarView(command_updater_);
380  star_view_->SetVisible(false);
381  AddChildView(star_view_);
382
383  registrar_.Add(this,
384                 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED,
385                 content::Source<Profile>(profile_));
386
387  // Initialize the location entry. We do this to avoid a black flash which is
388  // visible when the location entry has just been initialized.
389  Update(NULL);
390
391  OnChanged();
392}
393
394bool LocationBarView::IsInitialized() const {
395  return location_entry_view_ != NULL;
396}
397
398SkColor LocationBarView::GetColor(ToolbarModel::SecurityLevel security_level,
399                                  ColorKind kind) const {
400  const ui::NativeTheme* native_theme = GetNativeTheme();
401  switch (kind) {
402    case BACKGROUND:
403#if defined(OS_CHROMEOS)
404      // Chrome OS requires a transparent omnibox background color.
405      return SkColorSetARGB(0, 255, 255, 255);
406#else
407      return native_theme->GetSystemColor(
408          ui::NativeTheme::kColorId_TextfieldDefaultBackground);
409#endif
410
411    case TEXT:
412      return native_theme->GetSystemColor(
413          ui::NativeTheme::kColorId_TextfieldDefaultColor);
414
415    case SELECTED_TEXT:
416      return native_theme->GetSystemColor(
417          ui::NativeTheme::kColorId_TextfieldSelectionColor);
418
419    case DEEMPHASIZED_TEXT:
420      return color_utils::AlphaBlend(
421          GetColor(security_level, TEXT),
422          GetColor(security_level, BACKGROUND),
423          128);
424
425    case SECURITY_TEXT: {
426      SkColor color;
427      switch (security_level) {
428        case ToolbarModel::EV_SECURE:
429        case ToolbarModel::SECURE:
430          color = SkColorSetRGB(7, 149, 0);
431          break;
432
433        case ToolbarModel::SECURITY_WARNING:
434        case ToolbarModel::SECURITY_POLICY_WARNING:
435          return GetColor(security_level, DEEMPHASIZED_TEXT);
436          break;
437
438        case ToolbarModel::SECURITY_ERROR:
439          color = SkColorSetRGB(162, 0, 0);
440          break;
441
442        default:
443          NOTREACHED();
444          return GetColor(security_level, TEXT);
445      }
446      return color_utils::GetReadableColor(
447          color, GetColor(security_level, BACKGROUND));
448    }
449
450    default:
451      NOTREACHED();
452      return GetColor(security_level, TEXT);
453  }
454}
455
456void LocationBarView::GetOmniboxPopupPositioningInfo(
457    gfx::Point* top_left_screen_coord,
458    int* popup_width,
459    int* left_margin,
460    int* right_margin) {
461  // Because the popup might appear atop the attached bookmark bar, there won't
462  // necessarily be a client edge separating it from the rest of the toolbar.
463  // Therefore we position the popup high enough so it can draw its own client
464  // edge at the top, in the same place the toolbar would normally draw the
465  // client edge.
466  *top_left_screen_coord = gfx::Point(
467      0,
468      parent()->height() - views::NonClientFrameView::kClientEdgeThickness);
469  views::View::ConvertPointToScreen(parent(), top_left_screen_coord);
470  *popup_width = parent()->width();
471
472  gfx::Rect location_bar_bounds(bounds());
473  location_bar_bounds.Inset(kNormalEdgeThickness, 0);
474  *left_margin = location_bar_bounds.x();
475  *right_margin = *popup_width - location_bar_bounds.right();
476}
477
478// static
479int LocationBarView::GetItemPadding() {
480  const int kTouchItemPadding = 8;
481  if (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH)
482    return kTouchItemPadding;
483
484  const int kDesktopScriptBadgeItemPadding = 9;
485  const int kDesktopItemPadding = 3;
486  return extensions::FeatureSwitch::script_badges()->IsEnabled() ?
487      kDesktopScriptBadgeItemPadding : kDesktopItemPadding;
488}
489
490// DropdownBarHostDelegate
491void LocationBarView::SetFocusAndSelection(bool select_all) {
492  FocusLocation(select_all);
493}
494
495void LocationBarView::SetAnimationOffset(int offset) {
496  animation_offset_ = offset;
497}
498
499void LocationBarView::Update(const WebContents* tab_for_state_restoring) {
500  mic_search_view_->SetVisible(
501      !model_->GetInputInProgress() && browser_ &&
502      browser_->search_model()->voice_search_supported());
503  RefreshContentSettingViews();
504  generated_credit_card_view_->Update();
505  ZoomBubbleView::CloseBubble();
506  RefreshZoomView();
507  RefreshPageActionViews();
508  RefreshScriptBubble();
509  open_pdf_in_reader_view_->Update(
510      model_->GetInputInProgress() ? NULL : GetWebContents());
511
512  bool star_enabled =
513      browser_defaults::bookmarks_enabled && !is_popup_mode_ && star_view_ &&
514      !model_->GetInputInProgress() && edit_bookmarks_enabled_.GetValue();
515
516  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled);
517  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE_FROM_STAR,
518                                         star_enabled);
519  if (star_view_)
520    star_view_->SetVisible(star_enabled);
521
522  location_entry_->Update(tab_for_state_restoring);
523
524  OnChanged();
525}
526
527void LocationBarView::UpdateContentSettingsIcons() {
528  RefreshContentSettingViews();
529
530  Layout();
531  SchedulePaint();
532}
533
534void LocationBarView::UpdatePageActions() {
535  size_t count_before = page_action_views_.size();
536  RefreshPageActionViews();
537  RefreshScriptBubble();
538  if (page_action_views_.size() != count_before) {
539    content::NotificationService::current()->Notify(
540        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
541        content::Source<LocationBar>(this),
542        content::NotificationService::NoDetails());
543  }
544
545  Layout();
546  SchedulePaint();
547}
548
549void LocationBarView::InvalidatePageActions() {
550  size_t count_before = page_action_views_.size();
551  DeletePageActionViews();
552  if (page_action_views_.size() != count_before) {
553    content::NotificationService::current()->Notify(
554        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED,
555        content::Source<LocationBar>(this),
556        content::NotificationService::NoDetails());
557  }
558}
559
560void LocationBarView::UpdateOpenPDFInReaderPrompt() {
561  open_pdf_in_reader_view_->Update(
562      model_->GetInputInProgress() ? NULL : GetWebContents());
563  Layout();
564  SchedulePaint();
565}
566
567void LocationBarView::UpdateGeneratedCreditCardView() {
568  generated_credit_card_view_->Update();
569  Layout();
570  SchedulePaint();
571}
572
573void LocationBarView::OnFocus() {
574  // Focus the view widget first which implements accessibility for
575  // Chrome OS.  It is noop on Win. This should be removed once
576  // Chrome OS migrates to aura, which uses Views' textfield that receives
577  // focus. See crbug.com/106428.
578  NotifyAccessibilityEvent(ui::AccessibilityTypes::EVENT_FOCUS, false);
579
580  // Then focus the native location view which implements accessibility for
581  // Windows.
582  location_entry_->SetFocus();
583}
584
585void LocationBarView::SetPreviewEnabledPageAction(ExtensionAction* page_action,
586                                                  bool preview_enabled) {
587  if (is_popup_mode_)
588    return;
589
590  DCHECK(page_action);
591  WebContents* contents = delegate_->GetWebContents();
592
593  RefreshPageActionViews();
594  PageActionWithBadgeView* page_action_view =
595      static_cast<PageActionWithBadgeView*>(GetPageActionView(page_action));
596  DCHECK(page_action_view);
597  if (!page_action_view)
598    return;
599
600  page_action_view->image_view()->set_preview_enabled(preview_enabled);
601  page_action_view->UpdateVisibility(contents, model_->GetURL());
602  Layout();
603  SchedulePaint();
604}
605
606views::View* LocationBarView::GetPageActionView(ExtensionAction *page_action) {
607  DCHECK(page_action);
608  for (PageActionViews::const_iterator i(page_action_views_.begin());
609       i != page_action_views_.end(); ++i) {
610    if ((*i)->image_view()->page_action() == page_action)
611      return *i;
612  }
613  return NULL;
614}
615
616void LocationBarView::SetStarToggled(bool on) {
617  if (star_view_)
618    star_view_->SetToggled(on);
619}
620
621void LocationBarView::ShowBookmarkPrompt() {
622  if (star_view_ && star_view_->visible())
623    BookmarkPromptView::ShowPrompt(star_view_, profile_->GetPrefs());
624}
625
626void LocationBarView::ZoomChangedForActiveTab(bool can_show_bubble) {
627  DCHECK(zoom_view_);
628  RefreshZoomView();
629
630  Layout();
631  SchedulePaint();
632
633  if (can_show_bubble && zoom_view_->visible() && delegate_->GetWebContents())
634    ZoomBubbleView::ShowBubble(delegate_->GetWebContents(), true);
635}
636
637void LocationBarView::RefreshZoomView() {
638  DCHECK(zoom_view_);
639  WebContents* web_contents = GetWebContents();
640  if (!web_contents)
641    return;
642
643  ZoomController* zoom_controller =
644      ZoomController::FromWebContents(web_contents);
645  zoom_view_->Update(zoom_controller);
646}
647
648gfx::Point LocationBarView::GetLocationEntryOrigin() const {
649  gfx::Point origin(location_entry_view_->bounds().origin());
650  // If the UI layout is RTL, the coordinate system is not transformed and
651  // therefore we need to adjust the X coordinate so that bubble appears on the
652  // right hand side of the location bar.
653  if (base::i18n::IsRTL())
654    origin.set_x(width() - origin.x());
655  views::View::ConvertPointToScreen(this, &origin);
656  return origin;
657}
658
659void LocationBarView::SetImeInlineAutocompletion(const string16& text) {
660  ime_inline_autocomplete_view_->SetText(text);
661  ime_inline_autocomplete_view_->SetVisible(!text.empty());
662}
663
664void LocationBarView::SetGrayTextAutocompletion(const string16& text) {
665  if (suggested_text_view_->text() != text) {
666    suggested_text_view_->SetText(text);
667    suggested_text_view_->SetVisible(!text.empty());
668    Layout();
669    SchedulePaint();
670  }
671}
672
673string16 LocationBarView::GetGrayTextAutocompletion() const {
674  return HasValidSuggestText() ? suggested_text_view_->text() : string16();
675}
676
677void LocationBarView::SetLocationEntryFocusable(bool focusable) {
678  OmniboxViewViews* omnibox_views = GetOmniboxViewViews(location_entry_.get());
679  if (omnibox_views)
680    omnibox_views->set_focusable(focusable);
681  else
682    set_focusable(focusable);
683}
684
685bool LocationBarView::IsLocationEntryFocusableInRootView() const {
686  OmniboxViewViews* omnibox_views = GetOmniboxViewViews(location_entry_.get());
687  return omnibox_views ? omnibox_views->IsFocusable() : View::IsFocusable();
688}
689
690gfx::Size LocationBarView::GetPreferredSize() {
691  return background_border_painter_->GetMinimumSize();
692}
693
694void LocationBarView::Layout() {
695  if (!location_entry_.get())
696    return;
697
698  selected_keyword_view_->SetVisible(false);
699  location_icon_view_->SetVisible(false);
700  ev_bubble_view_->SetVisible(false);
701  keyword_hint_view_->SetVisible(false);
702
703  const int item_padding = GetItemPadding();
704  // The native edit has 1 px of whitespace inside it before the text when the
705  // text is not scrolled off the leading edge.  The views textfield has 1 px of
706  // whitespace before the text in the RTL case only.
707  const int kEditLeadingInternalSpace =
708      (base::i18n::IsRTL() || GetOmniboxViewWin(location_entry_.get())) ? 1 : 0;
709  LocationBarLayout leading_decorations(
710      LocationBarLayout::LEFT_EDGE, item_padding - kEditLeadingInternalSpace);
711  LocationBarLayout trailing_decorations(LocationBarLayout::RIGHT_EDGE,
712                                         item_padding);
713
714  const string16 keyword(location_entry_->model()->keyword());
715  const bool is_keyword_hint(location_entry_->model()->is_keyword_hint());
716  const int bubble_location_y = vertical_edge_thickness() + kBubblePadding;
717  // In some cases (e.g. fullscreen mode) we may have 0 height.  We still want
718  // to position our child views in this case, because other things may be
719  // positioned relative to them (e.g. the "bookmark added" bubble if the user
720  // hits ctrl-d).
721  const int location_height = GetInternalHeight(false);
722  const int bubble_height = std::max(location_height - (kBubblePadding * 2), 0);
723  if (!keyword.empty() && !is_keyword_hint) {
724    leading_decorations.AddDecoration(bubble_location_y, bubble_height, true, 0,
725                                      kBubblePadding, item_padding, 0,
726                                      selected_keyword_view_);
727    if (selected_keyword_view_->keyword() != keyword) {
728      selected_keyword_view_->SetKeyword(keyword);
729      const TemplateURL* template_url =
730          TemplateURLServiceFactory::GetForProfile(profile_)->
731          GetTemplateURLForKeyword(keyword);
732      if (template_url && template_url->IsExtensionKeyword()) {
733        gfx::Image image = extensions::OmniboxAPI::Get(profile_)->
734            GetOmniboxIcon(template_url->GetExtensionId());
735        selected_keyword_view_->SetImage(image.AsImageSkia());
736        selected_keyword_view_->set_is_extension_icon(true);
737      } else {
738        selected_keyword_view_->SetImage(
739            *(GetThemeProvider()->GetImageSkiaNamed(IDR_OMNIBOX_SEARCH)));
740        selected_keyword_view_->set_is_extension_icon(false);
741      }
742    }
743  } else if (model_->GetSecurityLevel(false) == ToolbarModel::EV_SECURE) {
744    ev_bubble_view_->SetLabel(model_->GetEVCertName());
745    // The largest fraction of the omnibox that can be taken by the EV bubble.
746    const double kMaxBubbleFraction = 0.5;
747    leading_decorations.AddDecoration(bubble_location_y, bubble_height, false,
748                                      kMaxBubbleFraction, kBubblePadding,
749                                      item_padding, 0, ev_bubble_view_);
750  } else {
751    leading_decorations.AddDecoration(
752        vertical_edge_thickness(), location_height,
753        GetBuiltInHorizontalPaddingForChildViews(),
754        location_icon_view_);
755  }
756
757  if (star_view_ && star_view_->visible()) {
758    trailing_decorations.AddDecoration(
759        vertical_edge_thickness(), location_height,
760        GetBuiltInHorizontalPaddingForChildViews(), star_view_);
761  }
762  if (script_bubble_icon_view_ && script_bubble_icon_view_->visible()) {
763    trailing_decorations.AddDecoration(
764        vertical_edge_thickness(), location_height,
765        GetBuiltInHorizontalPaddingForChildViews(),
766        script_bubble_icon_view_);
767  }
768  if (open_pdf_in_reader_view_ && open_pdf_in_reader_view_->visible()) {
769    trailing_decorations.AddDecoration(
770        vertical_edge_thickness(), location_height,
771        GetBuiltInHorizontalPaddingForChildViews(),
772        open_pdf_in_reader_view_);
773  }
774  for (PageActionViews::const_iterator i(page_action_views_.begin());
775       i != page_action_views_.end(); ++i) {
776    if ((*i)->visible()) {
777      trailing_decorations.AddDecoration(
778          vertical_edge_thickness(), location_height,
779          GetBuiltInHorizontalPaddingForChildViews(), (*i));
780    }
781  }
782  if (zoom_view_->visible()) {
783    trailing_decorations.AddDecoration(vertical_edge_thickness(),
784                                       location_height, 0, zoom_view_);
785  }
786  for (ContentSettingViews::const_reverse_iterator i(
787           content_setting_views_.rbegin()); i != content_setting_views_.rend();
788       ++i) {
789    if ((*i)->visible()) {
790      trailing_decorations.AddDecoration(
791          bubble_location_y, bubble_height, false, 0, item_padding,
792          item_padding, GetBuiltInHorizontalPaddingForChildViews(), (*i));
793    }
794  }
795  if (generated_credit_card_view_->visible()) {
796    trailing_decorations.AddDecoration(vertical_edge_thickness(),
797                                       location_height, 0,
798                                       generated_credit_card_view_);
799  }
800  if (mic_search_view_->visible()) {
801    trailing_decorations.AddDecoration(vertical_edge_thickness(),
802                                       location_height, 0, mic_search_view_);
803  }
804  // Because IMEs may eat the tab key, we don't show "press tab to search" while
805  // IME composition is in progress.
806  if (!keyword.empty() && is_keyword_hint &&
807      !location_entry_->IsImeComposing()) {
808    trailing_decorations.AddDecoration(vertical_edge_thickness(),
809                                       location_height, true, 0, item_padding,
810                                       item_padding, 0, keyword_hint_view_);
811    if (keyword_hint_view_->keyword() != keyword)
812      keyword_hint_view_->SetKeyword(keyword);
813  }
814
815  // Perform layout.
816  const int horizontal_edge_thickness = GetHorizontalEdgeThickness();
817  int full_width = width() - 2 * horizontal_edge_thickness;
818  int entry_width = full_width;
819  leading_decorations.LayoutPass1(&entry_width);
820  trailing_decorations.LayoutPass1(&entry_width);
821  leading_decorations.LayoutPass2(&entry_width);
822  trailing_decorations.LayoutPass2(&entry_width);
823
824  int location_needed_width = location_entry_->TextWidth();
825  int available_width = entry_width - location_needed_width;
826  // The bounds must be wide enough for all the decorations to fit.
827  gfx::Rect location_bounds(
828      horizontal_edge_thickness, vertical_edge_thickness(),
829      std::max(full_width, full_width - entry_width), location_height);
830  leading_decorations.LayoutPass3(&location_bounds, &available_width);
831  trailing_decorations.LayoutPass3(&location_bounds, &available_width);
832
833  // Layout out the suggested text view right aligned to the location
834  // entry. Only show the suggested text if we can fit the text from one
835  // character before the end of the selection to the end of the text and the
836  // suggested text. If we can't it means either the suggested text is too big,
837  // or the user has scrolled.
838
839  // TODO(sky): We could potentially adjust this to take into account suggested
840  // text to force using minimum size if necessary, but currently the chance of
841  // showing keyword hints and suggested text is minimal and we're not confident
842  // this is the right approach for suggested text.
843
844  OmniboxViewViews* omnibox_views =
845      GetOmniboxViewViews(location_entry_.get());
846  int omnibox_views_margin = 0;
847  if (suggested_text_view_->visible()) {
848    // We do not display the suggested text when it contains a mix of RTL and
849    // LTR characters since this could mean the suggestion should be displayed
850    // in the middle of the string.
851    base::i18n::TextDirection text_direction =
852        base::i18n::GetStringDirection(location_entry_->GetText());
853    if (text_direction !=
854        base::i18n::GetStringDirection(suggested_text_view_->text()))
855      text_direction = base::i18n::UNKNOWN_DIRECTION;
856
857    // TODO(sky): need to layout when the user changes caret position.
858    gfx::Size suggested_text_size(suggested_text_view_->GetPreferredSize());
859    if (suggested_text_size.width() > available_width ||
860        text_direction == base::i18n::UNKNOWN_DIRECTION) {
861      // Hide the suggested text if the user has scrolled or we can't fit all
862      // the suggested text, or we have a mix of RTL and LTR characters.
863      suggested_text_view_->SetBounds(0, 0, 0, 0);
864    } else {
865      location_needed_width =
866          std::min(location_needed_width,
867                   location_bounds.width() - suggested_text_size.width());
868      gfx::Rect suggested_text_bounds(location_bounds.origin(),
869                                      suggested_text_size);
870      // TODO(sky): figure out why this needs the -1.
871      suggested_text_bounds.Offset(location_needed_width - 1, 0);
872      // For non-views the omnibox needs to be shrunk so that the suggest text
873      // is visible.
874      if (!omnibox_views)
875        location_bounds.set_width(location_needed_width);
876
877      // We reverse the order of the location entry and suggested text if:
878      // - Chrome is RTL but the text is fully LTR, or
879      // - Chrome is LTR but the text is fully RTL.
880      // This ensures the suggested text is correctly displayed to the right
881      // (or left) of the user text.
882      if (text_direction == (base::i18n::IsRTL() ?
883          base::i18n::LEFT_TO_RIGHT : base::i18n::RIGHT_TO_LEFT)) {
884        // TODO(sky): Figure out why we need the +1.
885        suggested_text_bounds.set_x(location_bounds.x() + 1);
886        if (omnibox_views) {
887          // Use a margin to prevent the omnibox text from overlapping the
888          // suggest text.
889          omnibox_views_margin = suggested_text_bounds.width();
890        } else {
891          // Non-views doesn't support margins so move the omnibox over.
892          location_bounds.set_x(
893              location_bounds.x() + suggested_text_bounds.width());
894        }
895      }
896      suggested_text_view_->SetBoundsRect(suggested_text_bounds);
897    }
898  }
899
900  if (omnibox_views)
901    omnibox_views->SetHorizontalMargins(0, omnibox_views_margin);
902
903  // Layout |ime_inline_autocomplete_view_| next to the user input.
904  if (ime_inline_autocomplete_view_->visible()) {
905    int width =
906        ime_inline_autocomplete_view_->font().GetStringWidth(
907            ime_inline_autocomplete_view_->text()) +
908        ime_inline_autocomplete_view_->GetInsets().width();
909    // All the target languages (IMEs) are LTR, and we do not need to support
910    // RTL so far.  In other words, no testable RTL environment so far.
911    int x = location_needed_width;
912    if (width > entry_width)
913      x = 0;
914    else if (location_needed_width + width > entry_width)
915      x = entry_width - width;
916    location_bounds.set_width(x);
917    ime_inline_autocomplete_view_->SetBounds(
918        location_bounds.right(), location_bounds.y(),
919        std::min(width, entry_width),
920        ime_inline_autocomplete_view_->GetPreferredSize().height());
921  }
922
923  location_entry_view_->SetBoundsRect(location_bounds);
924}
925
926void LocationBarView::OnPaint(gfx::Canvas* canvas) {
927  View::OnPaint(canvas);
928
929  // Fill the location bar background color behind the border.  Parts of the
930  // border images are meant to rest atop the toolbar background and parts atop
931  // the omnibox background, so we can't just blindly fill our entire bounds.
932  const int horizontal_edge_thickness = GetHorizontalEdgeThickness();
933  if (!background_filling_painter_) {
934    gfx::Rect bounds(GetContentsBounds());
935    bounds.Inset(horizontal_edge_thickness, vertical_edge_thickness());
936    SkColor color(GetColor(ToolbarModel::NONE, BACKGROUND));
937    if (is_popup_mode_) {
938      canvas->FillRect(bounds, color);
939    } else {
940      SkPaint paint;
941      paint.setStyle(SkPaint::kFill_Style);
942      paint.setColor(color);
943      const int kBorderCornerRadius = 2;
944      canvas->DrawRoundRect(bounds, kBorderCornerRadius, paint);
945    }
946  }
947
948  // Maximized popup windows don't draw the horizontal edges.  We implement this
949  // by simply expanding the paint area outside the view by the edge thickness.
950  gfx::Rect background_rect(GetContentsBounds());
951  if (is_popup_mode_ && (horizontal_edge_thickness == 0))
952    background_rect.Inset(-kPopupEdgeThickness, 0);
953  views::Painter::PaintPainterAt(canvas, background_border_painter_.get(),
954                                 background_rect);
955  if (background_filling_painter_)
956    background_filling_painter_->Paint(canvas, size());
957
958  if (!is_popup_mode_)
959    PaintPageActionBackgrounds(canvas);
960
961  // For non-InstantExtendedAPI cases, if necessary, show focus rect.
962  // Note: |Canvas::DrawFocusRect| paints a dashed rect with gray color.
963  if (show_focus_rect_ && HasFocus()) {
964    gfx::Rect r = location_entry_view_->bounds();
965    // TODO(jamescook): Is this still needed?
966    r.Inset(-1, 0);
967#if defined(OS_WIN)
968    r.Inset(0, -1);
969#endif
970    canvas->DrawFocusRect(r);
971  }
972}
973
974void LocationBarView::SetShowFocusRect(bool show) {
975  show_focus_rect_ = show;
976  SchedulePaint();
977}
978
979void LocationBarView::SelectAll() {
980  location_entry_->SelectAll(true);
981}
982
983#if defined(OS_WIN) && !defined(USE_AURA)
984bool LocationBarView::OnMousePressed(const ui::MouseEvent& event) {
985  UINT msg;
986  if (event.IsLeftMouseButton()) {
987    msg = (event.flags() & ui::EF_IS_DOUBLE_CLICK) ?
988        WM_LBUTTONDBLCLK : WM_LBUTTONDOWN;
989  } else if (event.IsMiddleMouseButton()) {
990    msg = (event.flags() & ui::EF_IS_DOUBLE_CLICK) ?
991        WM_MBUTTONDBLCLK : WM_MBUTTONDOWN;
992  } else if (event.IsRightMouseButton()) {
993    msg = (event.flags() & ui::EF_IS_DOUBLE_CLICK) ?
994        WM_RBUTTONDBLCLK : WM_RBUTTONDOWN;
995  } else {
996    NOTREACHED();
997    return false;
998  }
999  OnMouseEvent(event, msg);
1000  return true;
1001}
1002
1003bool LocationBarView::OnMouseDragged(const ui::MouseEvent& event) {
1004  OnMouseEvent(event, WM_MOUSEMOVE);
1005  return true;
1006}
1007
1008void LocationBarView::OnMouseReleased(const ui::MouseEvent& event) {
1009  UINT msg;
1010  if (event.IsLeftMouseButton()) {
1011    msg = WM_LBUTTONUP;
1012  } else if (event.IsMiddleMouseButton()) {
1013    msg = WM_MBUTTONUP;
1014  } else if (event.IsRightMouseButton()) {
1015    msg = WM_RBUTTONUP;
1016  } else {
1017    NOTREACHED();
1018    return;
1019  }
1020  OnMouseEvent(event, msg);
1021}
1022
1023void LocationBarView::OnMouseCaptureLost() {
1024  OmniboxViewWin* omnibox_win = GetOmniboxViewWin(location_entry_.get());
1025  if (omnibox_win)
1026    omnibox_win->HandleExternalMsg(WM_CAPTURECHANGED, 0, CPoint());
1027}
1028#endif
1029
1030views::View* LocationBarView::generated_credit_card_view() {
1031  return generated_credit_card_view_;
1032}
1033
1034void LocationBarView::OnAutocompleteAccept(
1035    const GURL& url,
1036    WindowOpenDisposition disposition,
1037    content::PageTransition transition,
1038    const GURL& alternate_nav_url) {
1039  // WARNING: don't add an early return here. The calls after the if must
1040  // happen.
1041  if (url.is_valid()) {
1042    location_input_ = UTF8ToUTF16(url.spec());
1043    disposition_ = disposition;
1044    transition_ = content::PageTransitionFromInt(
1045        transition | content::PAGE_TRANSITION_FROM_ADDRESS_BAR);
1046
1047    if (command_updater_) {
1048      if (!alternate_nav_url.is_valid()) {
1049        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
1050      } else {
1051        AlternateNavURLFetcher* fetcher =
1052            new AlternateNavURLFetcher(alternate_nav_url);
1053        // The AlternateNavURLFetcher will listen for the pending navigation
1054        // notification that will be issued as a result of the "open URL." It
1055        // will automatically install itself into that navigation controller.
1056        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
1057        if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
1058          // I'm not sure this should be reachable, but I'm not also sure enough
1059          // that it shouldn't to stick in a NOTREACHED().  In any case, this is
1060          // harmless.
1061          delete fetcher;
1062        } else {
1063          // The navigation controller will delete the fetcher.
1064        }
1065      }
1066    }
1067  }
1068}
1069
1070void LocationBarView::OnChanged() {
1071  location_icon_view_->SetImage(
1072      GetThemeProvider()->GetImageSkiaNamed(location_entry_->GetIcon()));
1073  location_icon_view_->ShowTooltip(!GetLocationEntry()->IsEditingOrEmpty());
1074
1075  Layout();
1076  SchedulePaint();
1077}
1078
1079void LocationBarView::OnSelectionBoundsChanged() {
1080}
1081
1082void LocationBarView::OnInputInProgress(bool in_progress) {
1083  delegate_->OnInputInProgress(in_progress);
1084}
1085
1086void LocationBarView::OnKillFocus() {
1087}
1088
1089void LocationBarView::OnSetFocus() {
1090  views::FocusManager* focus_manager = GetFocusManager();
1091  if (!focus_manager) {
1092    NOTREACHED();
1093    return;
1094  }
1095  focus_manager->SetFocusedView(this);
1096}
1097
1098gfx::Image LocationBarView::GetFavicon() const {
1099  return FaviconTabHelper::FromWebContents(
1100      delegate_->GetWebContents())->GetFavicon();
1101}
1102
1103string16 LocationBarView::GetTitle() const {
1104  return delegate_->GetWebContents()->GetTitle();
1105}
1106
1107InstantController* LocationBarView::GetInstant() {
1108  return delegate_->GetInstant();
1109}
1110
1111WebContents* LocationBarView::GetWebContents() const {
1112  return delegate_->GetWebContents();
1113}
1114
1115// static
1116int LocationBarView::GetBuiltInHorizontalPaddingForChildViews() {
1117  return (ui::GetDisplayLayout() == ui::LAYOUT_TOUCH) ?
1118      GetItemPadding() / 2 : 0;
1119}
1120
1121int LocationBarView::GetHorizontalEdgeThickness() const {
1122  // In maximized popup mode, there isn't any edge.
1123  return (is_popup_mode_ && browser_ && browser_->window() &&
1124      browser_->window()->IsMaximized()) ? 0 : vertical_edge_thickness();
1125}
1126
1127void LocationBarView::RefreshContentSettingViews() {
1128  for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
1129       i != content_setting_views_.end(); ++i) {
1130    (*i)->Update(model_->GetInputInProgress() ? NULL : GetWebContents());
1131  }
1132}
1133
1134void LocationBarView::DeletePageActionViews() {
1135  for (PageActionViews::const_iterator i(page_action_views_.begin());
1136       i != page_action_views_.end(); ++i)
1137    RemoveChildView(*i);
1138  STLDeleteElements(&page_action_views_);
1139}
1140
1141void LocationBarView::RefreshPageActionViews() {
1142  if (is_popup_mode_)
1143    return;
1144
1145  // Remember the previous visibility of the page actions so that we can
1146  // notify when this changes.
1147  std::map<ExtensionAction*, bool> old_visibility;
1148  for (PageActionViews::const_iterator i(page_action_views_.begin());
1149       i != page_action_views_.end(); ++i) {
1150    old_visibility[(*i)->image_view()->page_action()] = (*i)->visible();
1151  }
1152
1153  std::vector<ExtensionAction*> new_page_actions;
1154
1155  WebContents* contents = delegate_->GetWebContents();
1156  if (contents) {
1157    extensions::TabHelper* extensions_tab_helper =
1158        extensions::TabHelper::FromWebContents(contents);
1159    extensions::LocationBarController* controller =
1160        extensions_tab_helper->location_bar_controller();
1161    new_page_actions = controller->GetCurrentActions();
1162  }
1163
1164  // On startup we sometimes haven't loaded any extensions. This makes sure
1165  // we catch up when the extensions (and any page actions) load.
1166  if (page_actions_ != new_page_actions) {
1167    page_actions_.swap(new_page_actions);
1168    DeletePageActionViews();  // Delete the old views (if any).
1169
1170    page_action_views_.resize(page_actions_.size());
1171    View* right_anchor = open_pdf_in_reader_view_;
1172    if (!right_anchor)
1173      right_anchor = star_view_;
1174    if (!right_anchor)
1175      right_anchor = script_bubble_icon_view_;
1176    DCHECK(right_anchor);
1177
1178    // Add the page actions in reverse order, so that the child views are
1179    // inserted in left-to-right order for accessibility.
1180    for (int i = page_actions_.size() - 1; i >= 0; --i) {
1181      page_action_views_[i] = new PageActionWithBadgeView(
1182          delegate_->CreatePageActionImageView(this, page_actions_[i]));
1183      page_action_views_[i]->SetVisible(false);
1184      AddChildViewAt(page_action_views_[i], GetIndexOf(right_anchor));
1185    }
1186  }
1187
1188  if (!page_action_views_.empty() && contents) {
1189    Browser* browser = chrome::FindBrowserWithWebContents(contents);
1190    GURL url = browser->tab_strip_model()->GetActiveWebContents()->GetURL();
1191
1192    for (PageActionViews::const_iterator i(page_action_views_.begin());
1193         i != page_action_views_.end(); ++i) {
1194      (*i)->UpdateVisibility(model_->GetInputInProgress() ? NULL : contents,
1195                             url);
1196
1197      // Check if the visibility of the action changed and notify if it did.
1198      ExtensionAction* action = (*i)->image_view()->page_action();
1199      if (old_visibility.find(action) == old_visibility.end() ||
1200          old_visibility[action] != (*i)->visible()) {
1201        content::NotificationService::current()->Notify(
1202            chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
1203            content::Source<ExtensionAction>(action),
1204            content::Details<WebContents>(contents));
1205      }
1206    }
1207  }
1208}
1209
1210size_t LocationBarView::ScriptBubbleScriptsRunning() {
1211  WebContents* contents = delegate_->GetWebContents();
1212  if (!contents)
1213    return false;
1214  extensions::TabHelper* extensions_tab_helper =
1215      extensions::TabHelper::FromWebContents(contents);
1216  if (!extensions_tab_helper)
1217    return false;
1218  extensions::ScriptBubbleController* script_bubble_controller =
1219      extensions_tab_helper->script_bubble_controller();
1220  if (!script_bubble_controller)
1221    return false;
1222  size_t script_count =
1223      script_bubble_controller->extensions_running_scripts().size();
1224  return script_count;
1225}
1226
1227void LocationBarView::RefreshScriptBubble() {
1228  if (!script_bubble_icon_view_)
1229    return;
1230  size_t script_count = ScriptBubbleScriptsRunning();
1231  script_bubble_icon_view_->SetVisible(script_count > 0);
1232  if (script_count > 0)
1233    script_bubble_icon_view_->SetScriptCount(script_count);
1234}
1235
1236#if defined(OS_WIN) && !defined(USE_AURA)
1237void LocationBarView::OnMouseEvent(const ui::MouseEvent& event, UINT msg) {
1238  OmniboxViewWin* omnibox_win = GetOmniboxViewWin(location_entry_.get());
1239  if (omnibox_win) {
1240    UINT flags = event.native_event().wParam;
1241    gfx::Point screen_point(event.location());
1242    ConvertPointToScreen(this, &screen_point);
1243    omnibox_win->HandleExternalMsg(msg, flags, screen_point.ToPOINT());
1244  }
1245}
1246#endif
1247
1248void LocationBarView::ShowFirstRunBubbleInternal() {
1249#if !defined(OS_CHROMEOS)
1250  // First run bubble doesn't make sense for Chrome OS.
1251  Browser* browser = GetBrowserFromDelegate(delegate_);
1252  if (!browser)
1253    return; // Possible when browser is shutting down.
1254
1255  FirstRunBubble::ShowBubble(browser, location_icon_view_);
1256#endif
1257}
1258
1259void LocationBarView::PaintPageActionBackgrounds(gfx::Canvas* canvas) {
1260  WebContents* web_contents = GetWebContents();
1261  // web_contents may be NULL while the browser is shutting down.
1262  if (!web_contents)
1263    return;
1264
1265  const int32 tab_id = SessionID::IdForTab(web_contents);
1266  const ToolbarModel::SecurityLevel security_level =
1267      model_->GetSecurityLevel(false);
1268  const SkColor text_color = GetColor(security_level, TEXT);
1269  const SkColor background_color = GetColor(security_level, BACKGROUND);
1270
1271  for (PageActionViews::const_iterator
1272           page_action_view = page_action_views_.begin();
1273       page_action_view != page_action_views_.end();
1274       ++page_action_view) {
1275    gfx::Rect bounds = (*page_action_view)->bounds();
1276    int horizontal_padding =
1277        GetItemPadding() - GetBuiltInHorizontalPaddingForChildViews();
1278    // Make the bounding rectangle include the whole vertical range of the
1279    // location bar, and the mid-point pixels between adjacent page actions.
1280    //
1281    // For odd horizontal_paddings, "horizontal_padding + 1" includes the
1282    // mid-point between two page actions in the bounding rectangle.  For even
1283    // paddings, the +1 is dropped, which is right since there is no pixel at
1284    // the mid-point.
1285    bounds.Inset(-(horizontal_padding + 1) / 2, 0);
1286    location_bar_util::PaintExtensionActionBackground(
1287        *(*page_action_view)->image_view()->page_action(),
1288        tab_id, canvas, bounds, text_color, background_color);
1289  }
1290}
1291
1292const char* LocationBarView::GetClassName() const {
1293  return kViewClassName;
1294}
1295
1296bool LocationBarView::SkipDefaultKeyEventProcessing(const ui::KeyEvent& event) {
1297#if defined(OS_WIN)
1298  if (views::FocusManager::IsTabTraversalKeyEvent(event)) {
1299    if (location_entry_->model()->popup_model()->IsOpen()) {
1300      // Return true so that the edit sees the tab and moves the selection.
1301      return true;
1302    }
1303    if (keyword_hint_view_->visible() && !event.IsShiftDown()) {
1304      // Return true so the edit gets the tab event and enters keyword mode.
1305      return true;
1306    }
1307  }
1308
1309#if defined(USE_AURA)
1310  NOTIMPLEMENTED();
1311#else
1312  OmniboxViewWin* omnibox_win = GetOmniboxViewWin(location_entry_.get());
1313  if (omnibox_win)
1314    return omnibox_win->SkipDefaultKeyEventProcessing(event);
1315#endif  // USE_AURA
1316#endif  // OS_WIN
1317
1318  // This method is not used for Linux ports. See FocusManager::OnKeyEvent() in
1319  // src/ui/views/focus/focus_manager.cc for details.
1320  return false;
1321}
1322
1323void LocationBarView::GetAccessibleState(ui::AccessibleViewState* state) {
1324  if (!location_entry_)
1325    return;
1326
1327  state->role = ui::AccessibilityTypes::ROLE_LOCATION_BAR;
1328  state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_LOCATION);
1329  state->value = location_entry_->GetText();
1330
1331  string16::size_type entry_start;
1332  string16::size_type entry_end;
1333  location_entry_->GetSelectionBounds(&entry_start, &entry_end);
1334  state->selection_start = entry_start;
1335  state->selection_end = entry_end;
1336
1337  if (is_popup_mode_) {
1338    state->state |= ui::AccessibilityTypes::STATE_READONLY;
1339  } else {
1340    state->set_value_callback =
1341        base::Bind(&LocationBarView::AccessibilitySetValue,
1342                   weak_ptr_factory_.GetWeakPtr());
1343  }
1344}
1345
1346bool LocationBarView::HasFocus() const {
1347  return location_entry_->model()->has_focus();
1348}
1349
1350void LocationBarView::OnBoundsChanged(const gfx::Rect& previous_bounds) {
1351  if (browser_ && browser_->instant_controller() && parent())
1352    browser_->instant_controller()->SetOmniboxBounds(bounds());
1353  OmniboxPopupView* popup = location_entry_->model()->popup_model()->view();
1354  if (popup->IsOpen())
1355    popup->UpdatePopupAppearance();
1356}
1357
1358void LocationBarView::ButtonPressed(views::Button* sender,
1359                                    const ui::Event& event) {
1360  DCHECK_EQ(mic_search_view_, sender);
1361  command_updater_->ExecuteCommand(IDC_TOGGLE_SPEECH_INPUT);
1362}
1363
1364void LocationBarView::WriteDragDataForView(views::View* sender,
1365                                           const gfx::Point& press_pt,
1366                                           OSExchangeData* data) {
1367  DCHECK_NE(GetDragOperationsForView(sender, press_pt),
1368            ui::DragDropTypes::DRAG_NONE);
1369
1370  WebContents* web_contents = GetWebContents();
1371  FaviconTabHelper* favicon_tab_helper =
1372      FaviconTabHelper::FromWebContents(web_contents);
1373  gfx::ImageSkia favicon = favicon_tab_helper->GetFavicon().AsImageSkia();
1374  button_drag_utils::SetURLAndDragImage(web_contents->GetURL(),
1375                                        web_contents->GetTitle(),
1376                                        favicon,
1377                                        data,
1378                                        sender->GetWidget());
1379}
1380
1381int LocationBarView::GetDragOperationsForView(views::View* sender,
1382                                              const gfx::Point& p) {
1383  DCHECK((sender == location_icon_view_) || (sender == ev_bubble_view_));
1384  WebContents* web_contents = delegate_->GetWebContents();
1385  return (web_contents && web_contents->GetURL().is_valid() &&
1386          !GetLocationEntry()->IsEditingOrEmpty()) ?
1387      (ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK) :
1388      ui::DragDropTypes::DRAG_NONE;
1389}
1390
1391bool LocationBarView::CanStartDragForView(View* sender,
1392                                          const gfx::Point& press_pt,
1393                                          const gfx::Point& p) {
1394  return true;
1395}
1396
1397////////////////////////////////////////////////////////////////////////////////
1398// LocationBarView, LocationBar implementation:
1399
1400void LocationBarView::ShowFirstRunBubble() {
1401  // Wait until search engines have loaded to show the first run bubble.
1402  TemplateURLService* url_service =
1403      TemplateURLServiceFactory::GetForProfile(profile_);
1404  if (!url_service->loaded()) {
1405    template_url_service_ = url_service;
1406    template_url_service_->AddObserver(this);
1407    template_url_service_->Load();
1408    return;
1409  }
1410  ShowFirstRunBubbleInternal();
1411}
1412
1413string16 LocationBarView::GetInputString() const {
1414  return location_input_;
1415}
1416
1417WindowOpenDisposition LocationBarView::GetWindowOpenDisposition() const {
1418  return disposition_;
1419}
1420
1421content::PageTransition LocationBarView::GetPageTransition() const {
1422  return transition_;
1423}
1424
1425void LocationBarView::AcceptInput() {
1426  location_entry_->model()->AcceptInput(CURRENT_TAB, false);
1427}
1428
1429void LocationBarView::FocusLocation(bool select_all) {
1430  location_entry_->SetFocus();
1431  if (select_all)
1432    location_entry_->SelectAll(true);
1433}
1434
1435void LocationBarView::FocusSearch() {
1436  location_entry_->SetFocus();
1437  location_entry_->SetForcedQuery();
1438}
1439
1440void LocationBarView::SaveStateToContents(WebContents* contents) {
1441  location_entry_->SaveStateToTab(contents);
1442}
1443
1444void LocationBarView::Revert() {
1445  location_entry_->RevertAll();
1446}
1447
1448const OmniboxView* LocationBarView::GetLocationEntry() const {
1449  return location_entry_.get();
1450}
1451
1452OmniboxView* LocationBarView::GetLocationEntry() {
1453  return location_entry_.get();
1454}
1455
1456LocationBarTesting* LocationBarView::GetLocationBarForTesting() {
1457  return this;
1458}
1459
1460int LocationBarView::PageActionCount() {
1461  return page_action_views_.size();
1462}
1463
1464int LocationBarView::PageActionVisibleCount() {
1465  int result = 0;
1466  for (size_t i = 0; i < page_action_views_.size(); i++) {
1467    if (page_action_views_[i]->visible())
1468      ++result;
1469  }
1470  return result;
1471}
1472
1473ExtensionAction* LocationBarView::GetPageAction(size_t index) {
1474  if (index < page_action_views_.size())
1475    return page_action_views_[index]->image_view()->page_action();
1476
1477  NOTREACHED();
1478  return NULL;
1479}
1480
1481ExtensionAction* LocationBarView::GetVisiblePageAction(size_t index) {
1482  size_t current = 0;
1483  for (size_t i = 0; i < page_action_views_.size(); ++i) {
1484    if (page_action_views_[i]->visible()) {
1485      if (current == index)
1486        return page_action_views_[i]->image_view()->page_action();
1487
1488      ++current;
1489    }
1490  }
1491
1492  NOTREACHED();
1493  return NULL;
1494}
1495
1496void LocationBarView::TestPageActionPressed(size_t index) {
1497  size_t current = 0;
1498  for (size_t i = 0; i < page_action_views_.size(); ++i) {
1499    if (page_action_views_[i]->visible()) {
1500      if (current == index) {
1501        page_action_views_[i]->image_view()->ExecuteAction(
1502            ExtensionPopup::SHOW);
1503        return;
1504      }
1505      ++current;
1506    }
1507  }
1508
1509  NOTREACHED();
1510}
1511
1512bool LocationBarView::GetBookmarkStarVisibility() {
1513  DCHECK(star_view_);
1514  return star_view_->visible();
1515}
1516
1517void LocationBarView::OnTemplateURLServiceChanged() {
1518  template_url_service_->RemoveObserver(this);
1519  template_url_service_ = NULL;
1520  // If the browser is no longer active, let's not show the info bubble, as this
1521  // would make the browser the active window again.
1522  if (location_entry_view_ && location_entry_view_->GetWidget()->IsActive())
1523    ShowFirstRunBubble();
1524}
1525
1526void LocationBarView::Observe(int type,
1527                              const content::NotificationSource& source,
1528                              const content::NotificationDetails& details) {
1529  switch (type) {
1530    case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: {
1531      // Only update if the updated action box was for the active tab contents.
1532      WebContents* target_tab = content::Details<WebContents>(details).ptr();
1533      if (target_tab == GetWebContents())
1534        UpdatePageActions();
1535      break;
1536    }
1537
1538    default:
1539      NOTREACHED() << "Unexpected notification.";
1540  }
1541}
1542
1543void LocationBarView::ModelChanged(const SearchModel::State& old_state,
1544                                   const SearchModel::State& new_state) {
1545  const bool visible =
1546      !model_->GetInputInProgress() && new_state.voice_search_supported;
1547  if (mic_search_view_->visible() != visible) {
1548    mic_search_view_->SetVisible(visible);
1549    Layout();
1550  }
1551}
1552
1553int LocationBarView::GetInternalHeight(bool use_preferred_size) {
1554  int total_height =
1555      use_preferred_size ? GetPreferredSize().height() : height();
1556  return std::max(total_height - (vertical_edge_thickness() * 2), 0);
1557}
1558
1559bool LocationBarView::HasValidSuggestText() const {
1560  return suggested_text_view_->visible() &&
1561      !suggested_text_view_->size().IsEmpty();
1562}
1563
1564void LocationBarView::AccessibilitySetValue(const string16& new_value) {
1565  location_entry_->SetUserText(new_value);
1566}
1567