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/location_bar/location_bar_view.h"
6
7#if defined(OS_LINUX)
8#include <gtk/gtk.h>
9#endif
10
11#include "base/command_line.h"
12#include "base/stl_util-inl.h"
13#include "base/utf_string_conversions.h"
14#include "chrome/app/chrome_command_ids.h"
15#include "chrome/browser/alternate_nav_url_fetcher.h"
16#include "chrome/browser/autocomplete/autocomplete_popup_model.h"
17#include "chrome/browser/defaults.h"
18#include "chrome/browser/extensions/extension_browser_event_router.h"
19#include "chrome/browser/extensions/extension_service.h"
20#include "chrome/browser/instant/instant_controller.h"
21#include "chrome/browser/profiles/profile.h"
22#include "chrome/browser/search_engines/template_url.h"
23#include "chrome/browser/search_engines/template_url_model.h"
24#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
25#include "chrome/browser/ui/view_ids.h"
26#include "chrome/browser/ui/views/browser_dialogs.h"
27#include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
28#include "chrome/browser/ui/views/location_bar/ev_bubble_view.h"
29#include "chrome/browser/ui/views/location_bar/keyword_hint_view.h"
30#include "chrome/browser/ui/views/location_bar/location_icon_view.h"
31#include "chrome/browser/ui/views/location_bar/page_action_image_view.h"
32#include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h"
33#include "chrome/browser/ui/views/location_bar/selected_keyword_view.h"
34#include "chrome/browser/ui/views/location_bar/star_view.h"
35#include "chrome/common/chrome_switches.h"
36#include "chrome/common/pref_names.h"
37#include "content/browser/renderer_host/render_widget_host_view.h"
38#include "content/common/notification_service.h"
39#include "grit/generated_resources.h"
40#include "grit/theme_resources.h"
41#include "ui/base/accessibility/accessible_view_state.h"
42#include "ui/base/dragdrop/drag_drop_types.h"
43#include "ui/base/l10n/l10n_util.h"
44#include "ui/base/resource/resource_bundle.h"
45#include "ui/base/theme_provider.h"
46#include "ui/gfx/canvas_skia.h"
47#include "ui/gfx/color_utils.h"
48#include "ui/gfx/skia_util.h"
49#include "views/controls/label.h"
50#include "views/drag_utils.h"
51
52#if defined(OS_WIN)
53#include "chrome/browser/ui/views/first_run_bubble.h"
54#include "chrome/browser/ui/views/location_bar/suggested_text_view.h"
55#endif
56
57using views::View;
58
59namespace {
60
61TabContents* GetTabContentsFromDelegate(LocationBarView::Delegate* delegate) {
62  const TabContentsWrapper* wrapper = delegate->GetTabContentsWrapper();
63  return wrapper ? wrapper->tab_contents() : NULL;
64}
65
66}  // namespace
67
68// static
69const int LocationBarView::kNormalHorizontalEdgeThickness = 1;
70const int LocationBarView::kVerticalEdgeThickness = 2;
71const int LocationBarView::kItemPadding = 3;
72const int LocationBarView::kIconInternalPadding = 2;
73const int LocationBarView::kEdgeItemPadding = kItemPadding;
74const int LocationBarView::kBubbleHorizontalPadding = 1;
75const char LocationBarView::kViewClassName[] =
76    "browser/ui/views/location_bar/LocationBarView";
77
78static const int kEVBubbleBackgroundImages[] = {
79  IDR_OMNIBOX_EV_BUBBLE_BACKGROUND_L,
80  IDR_OMNIBOX_EV_BUBBLE_BACKGROUND_C,
81  IDR_OMNIBOX_EV_BUBBLE_BACKGROUND_R,
82};
83
84static const int kSelectedKeywordBackgroundImages[] = {
85  IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_L,
86  IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_C,
87  IDR_LOCATION_BAR_SELECTED_KEYWORD_BACKGROUND_R,
88};
89
90static const int kNormalModeBackgroundImages[] = {
91  IDR_LOCATIONBG_L,
92  IDR_LOCATIONBG_C,
93  IDR_LOCATIONBG_R,
94};
95
96// LocationBarView -----------------------------------------------------------
97
98LocationBarView::LocationBarView(Profile* profile,
99                                 CommandUpdater* command_updater,
100                                 ToolbarModel* model,
101                                 Delegate* delegate,
102                                 Mode mode)
103    : profile_(profile),
104      command_updater_(command_updater),
105      model_(model),
106      delegate_(delegate),
107      disposition_(CURRENT_TAB),
108      transition_(PageTransition::LINK),
109      location_icon_view_(NULL),
110      ev_bubble_view_(NULL),
111      location_entry_view_(NULL),
112      selected_keyword_view_(NULL),
113#if defined(OS_WIN)
114      suggested_text_view_(NULL),
115#endif
116      keyword_hint_view_(NULL),
117      star_view_(NULL),
118      mode_(mode),
119      show_focus_rect_(false),
120      bubble_type_(FirstRun::MINIMAL_BUBBLE),
121      template_url_model_(NULL) {
122  DCHECK(profile_);
123  SetID(VIEW_ID_LOCATION_BAR);
124  SetFocusable(true);
125
126  if (mode_ == NORMAL)
127    painter_.reset(new views::HorizontalPainter(kNormalModeBackgroundImages));
128
129  edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled,
130                               profile_->GetPrefs(), this);
131}
132
133LocationBarView::~LocationBarView() {
134  if (template_url_model_)
135    template_url_model_->RemoveObserver(this);
136}
137
138void LocationBarView::Init() {
139  if (mode_ == POPUP) {
140    font_ = ResourceBundle::GetSharedInstance().GetFont(
141        ResourceBundle::BaseFont);
142  } else {
143    // Use a larger version of the system font.
144    font_ = ResourceBundle::GetSharedInstance().GetFont(
145        ResourceBundle::MediumFont);
146  }
147
148  // If this makes the font too big, try to make it smaller so it will fit.
149  const int height =
150      std::max(GetPreferredSize().height() - (kVerticalEdgeThickness * 2), 0);
151  while ((font_.GetHeight() > height) && (font_.GetFontSize() > 1))
152    font_ = font_.DeriveFont(-1);
153
154  location_icon_view_ = new LocationIconView(this);
155  AddChildView(location_icon_view_);
156  location_icon_view_->SetVisible(true);
157  location_icon_view_->SetDragController(this);
158
159  ev_bubble_view_ =
160      new EVBubbleView(kEVBubbleBackgroundImages, IDR_OMNIBOX_HTTPS_VALID,
161                       GetColor(ToolbarModel::EV_SECURE, SECURITY_TEXT), this);
162  AddChildView(ev_bubble_view_);
163  ev_bubble_view_->SetVisible(false);
164  ev_bubble_view_->SetDragController(this);
165
166  // URL edit field.
167  // View container for URL edit field.
168#if defined(OS_WIN)
169  location_entry_.reset(new AutocompleteEditViewWin(font_, this, model_, this,
170      GetWidget()->GetNativeView(), profile_, command_updater_,
171      mode_ == POPUP, this));
172#else
173  location_entry_.reset(
174      AutocompleteEditViewGtk::Create(
175          this, model_, profile_,
176          command_updater_, mode_ == POPUP, this));
177#endif
178
179  location_entry_view_ = location_entry_->AddToView(this);
180  location_entry_view_->SetID(VIEW_ID_AUTOCOMPLETE);
181
182  selected_keyword_view_ = new SelectedKeywordView(
183      kSelectedKeywordBackgroundImages, IDR_KEYWORD_SEARCH_MAGNIFIER,
184      GetColor(ToolbarModel::NONE, TEXT), profile_),
185  AddChildView(selected_keyword_view_);
186  selected_keyword_view_->SetFont(font_);
187  selected_keyword_view_->SetVisible(false);
188
189  SkColor dimmed_text = GetColor(ToolbarModel::NONE, DEEMPHASIZED_TEXT);
190
191  keyword_hint_view_ = new KeywordHintView(profile_);
192  AddChildView(keyword_hint_view_);
193  keyword_hint_view_->SetVisible(false);
194  keyword_hint_view_->SetFont(font_);
195  keyword_hint_view_->SetColor(dimmed_text);
196
197  for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) {
198    ContentSettingImageView* content_blocked_view = new ContentSettingImageView(
199        static_cast<ContentSettingsType>(i), this, profile_);
200    content_setting_views_.push_back(content_blocked_view);
201    AddChildView(content_blocked_view);
202    content_blocked_view->SetVisible(false);
203  }
204
205  // The star is not visible in popups and in the app launcher.
206  if (browser_defaults::bookmarks_enabled && (mode_ == NORMAL)) {
207    star_view_ = new StarView(command_updater_);
208    AddChildView(star_view_);
209    star_view_->SetVisible(true);
210  }
211
212  // Initialize the location entry. We do this to avoid a black flash which is
213  // visible when the location entry has just been initialized.
214  Update(NULL);
215
216  OnChanged();
217}
218
219bool LocationBarView::IsInitialized() const {
220  return location_entry_view_ != NULL;
221}
222
223// static
224SkColor LocationBarView::GetColor(ToolbarModel::SecurityLevel security_level,
225                                  ColorKind kind) {
226  switch (kind) {
227#if defined(OS_WIN)
228    case BACKGROUND:    return color_utils::GetSysSkColor(COLOR_WINDOW);
229    case TEXT:          return color_utils::GetSysSkColor(COLOR_WINDOWTEXT);
230    case SELECTED_TEXT: return color_utils::GetSysSkColor(COLOR_HIGHLIGHTTEXT);
231#else
232    // TODO(beng): source from theme provider.
233    case BACKGROUND:    return SK_ColorWHITE;
234    case TEXT:          return SK_ColorBLACK;
235    case SELECTED_TEXT: return SK_ColorWHITE;
236#endif
237
238    case DEEMPHASIZED_TEXT:
239      return color_utils::AlphaBlend(GetColor(security_level, TEXT),
240                                     GetColor(security_level, BACKGROUND), 128);
241
242    case SECURITY_TEXT: {
243      SkColor color;
244      switch (security_level) {
245        case ToolbarModel::EV_SECURE:
246        case ToolbarModel::SECURE:
247          color = SkColorSetRGB(7, 149, 0);
248          break;
249
250        case ToolbarModel::SECURITY_WARNING:
251          return GetColor(security_level, DEEMPHASIZED_TEXT);
252          break;
253
254        case ToolbarModel::SECURITY_ERROR:
255          color = SkColorSetRGB(162, 0, 0);
256          break;
257
258        default:
259          NOTREACHED();
260          return GetColor(security_level, TEXT);
261      }
262      return color_utils::GetReadableColor(color, GetColor(security_level,
263                                                           BACKGROUND));
264    }
265
266    default:
267      NOTREACHED();
268      return GetColor(security_level, TEXT);
269  }
270}
271
272void LocationBarView::Update(const TabContents* tab_for_state_restoring) {
273  bool star_enabled = star_view_ && !model_->input_in_progress() &&
274                      edit_bookmarks_enabled_.GetValue();
275  command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled);
276  if (star_view_)
277    star_view_->SetVisible(star_enabled);
278  RefreshContentSettingViews();
279  RefreshPageActionViews();
280  // Don't Update in app launcher mode so that the location entry does not show
281  // a URL or security background.
282  if (mode_ != APP_LAUNCHER)
283    location_entry_->Update(tab_for_state_restoring);
284  OnChanged();
285}
286
287void LocationBarView::UpdateContentSettingsIcons() {
288  RefreshContentSettingViews();
289
290  Layout();
291  SchedulePaint();
292}
293
294void LocationBarView::UpdatePageActions() {
295  size_t count_before = page_action_views_.size();
296  RefreshPageActionViews();
297  if (page_action_views_.size() != count_before) {
298    NotificationService::current()->Notify(
299        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
300        Source<LocationBar>(this),
301        NotificationService::NoDetails());
302  }
303
304  Layout();
305  SchedulePaint();
306}
307
308void LocationBarView::InvalidatePageActions() {
309  size_t count_before = page_action_views_.size();
310  DeletePageActionViews();
311  if (page_action_views_.size() != count_before) {
312    NotificationService::current()->Notify(
313        NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED,
314        Source<LocationBar>(this),
315        NotificationService::NoDetails());
316  }
317}
318
319void LocationBarView::OnFocus() {
320  // Focus the location entry native view.
321  location_entry_->SetFocus();
322  GetWidget()->NotifyAccessibilityEvent(
323      this, ui::AccessibilityTypes::EVENT_FOCUS, true);
324}
325
326void LocationBarView::SetProfile(Profile* profile) {
327  DCHECK(profile);
328  if (profile_ != profile) {
329    profile_ = profile;
330    location_entry_->model()->SetProfile(profile);
331    selected_keyword_view_->set_profile(profile);
332    keyword_hint_view_->set_profile(profile);
333    for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
334         i != content_setting_views_.end(); ++i)
335      (*i)->set_profile(profile);
336  }
337}
338
339void LocationBarView::SetPreviewEnabledPageAction(ExtensionAction* page_action,
340                                                  bool preview_enabled) {
341  if (mode_ != NORMAL)
342    return;
343
344  DCHECK(page_action);
345  TabContents* contents = GetTabContentsFromDelegate(delegate_);
346
347  RefreshPageActionViews();
348  PageActionWithBadgeView* page_action_view =
349      static_cast<PageActionWithBadgeView*>(GetPageActionView(page_action));
350  DCHECK(page_action_view);
351  if (!page_action_view)
352    return;
353
354  page_action_view->image_view()->set_preview_enabled(preview_enabled);
355  page_action_view->UpdateVisibility(contents,
356      GURL(WideToUTF8(model_->GetText())));
357  Layout();
358  SchedulePaint();
359}
360
361views::View* LocationBarView::GetPageActionView(
362    ExtensionAction *page_action) {
363  DCHECK(page_action);
364  for (PageActionViews::const_iterator i(page_action_views_.begin());
365       i != page_action_views_.end(); ++i) {
366    if ((*i)->image_view()->page_action() == page_action)
367      return *i;
368  }
369  return NULL;
370}
371
372void LocationBarView::SetStarToggled(bool on) {
373  if (star_view_)
374    star_view_->SetToggled(on);
375}
376
377void LocationBarView::ShowStarBubble(const GURL& url, bool newly_bookmarked) {
378  gfx::Rect screen_bounds(star_view_->GetImageBounds());
379  // Compensate for some built-in padding in the Star image.
380  screen_bounds.Inset(1, 1, 1, 2);
381  gfx::Point origin(screen_bounds.origin());
382  views::View::ConvertPointToScreen(star_view_, &origin);
383  screen_bounds.set_origin(origin);
384  browser::ShowBookmarkBubbleView(GetWindow(), screen_bounds, star_view_,
385                                  profile_, url, newly_bookmarked);
386}
387
388gfx::Point LocationBarView::GetLocationEntryOrigin() const {
389  gfx::Point origin(location_entry_view_->bounds().origin());
390  // If the UI layout is RTL, the coordinate system is not transformed and
391  // therefore we need to adjust the X coordinate so that bubble appears on the
392  // right hand side of the location bar.
393  if (base::i18n::IsRTL())
394    origin.set_x(width() - origin.x());
395  views::View::ConvertPointToScreen(this, &origin);
396  return origin;
397}
398
399#if defined(OS_WIN)
400void LocationBarView::SetInstantSuggestion(const string16& text,
401                                           bool animate_to_complete) {
402  // Don't show the suggested text if inline autocomplete is prevented.
403  if (!text.empty()) {
404    if (!suggested_text_view_) {
405      suggested_text_view_ = new SuggestedTextView(location_entry_->model());
406      suggested_text_view_->SetHorizontalAlignment(views::Label::ALIGN_LEFT);
407      suggested_text_view_->SetColor(
408          GetColor(ToolbarModel::NONE,
409                   LocationBarView::DEEMPHASIZED_TEXT));
410      suggested_text_view_->SetText(UTF16ToWide(text));
411      suggested_text_view_->SetFont(location_entry_->GetFont());
412      AddChildView(suggested_text_view_);
413    } else if (suggested_text_view_->GetText() != UTF16ToWide(text)) {
414      suggested_text_view_->SetText(UTF16ToWide(text));
415    }
416    if (animate_to_complete && !location_entry_->IsImeComposing())
417      suggested_text_view_->StartAnimation();
418  } else if (suggested_text_view_) {
419    delete suggested_text_view_;
420    suggested_text_view_ = NULL;
421  } else {
422    return;
423  }
424
425  Layout();
426  SchedulePaint();
427}
428
429string16 LocationBarView::GetInstantSuggestion() const {
430  return HasValidSuggestText() ? suggested_text_view_->GetText() : string16();
431}
432#endif
433
434gfx::Size LocationBarView::GetPreferredSize() {
435  return gfx::Size(0, GetThemeProvider()->GetBitmapNamed(mode_ == POPUP ?
436      IDR_LOCATIONBG_POPUPMODE_CENTER : IDR_LOCATIONBG_C)->height());
437}
438
439void LocationBarView::Layout() {
440  if (!location_entry_.get())
441    return;
442
443  // TODO(sky): baseline layout.
444  int location_y = kVerticalEdgeThickness;
445  // In some cases (e.g. fullscreen mode) we may have 0 height.  We still want
446  // to position our child views in this case, because other things may be
447  // positioned relative to them (e.g. the "bookmark added" bubble if the user
448  // hits ctrl-d).
449  int location_height = std::max(height() - (kVerticalEdgeThickness * 2), 0);
450
451  // The edge stroke is 1 px thick.  In popup mode, the edges are drawn by the
452  // omnibox' parent, so there isn't any edge to account for at all.
453  const int kEdgeThickness = (mode_ == NORMAL) ?
454      kNormalHorizontalEdgeThickness : 0;
455  // The edit has 1 px of horizontal whitespace inside it before the text.
456  const int kEditInternalSpace = 1;
457  // The space between an item and the edit is the normal item space, minus the
458  // edit's built-in space (so the apparent space will be the same).
459  const int kItemEditPadding =
460      LocationBarView::kItemPadding - kEditInternalSpace;
461  const int kEdgeEditPadding =
462      LocationBarView::kEdgeItemPadding - kEditInternalSpace;
463  const int kBubbleVerticalPadding = (mode_ == POPUP) ?
464      -1 : kBubbleHorizontalPadding;
465
466  // Start by reserving the padding at the right edge.
467  int entry_width = width() - kEdgeThickness - kEdgeItemPadding;
468
469  // |location_icon_view_| is visible except when |ev_bubble_view_| or
470  // |selected_keyword_view_| are visible.
471  int location_icon_width = 0;
472  int ev_bubble_width = 0;
473  location_icon_view_->SetVisible(false);
474  ev_bubble_view_->SetVisible(false);
475  const string16 keyword(location_entry_->model()->keyword());
476  const bool is_keyword_hint(location_entry_->model()->is_keyword_hint());
477  const bool show_selected_keyword = !keyword.empty() && !is_keyword_hint;
478  if (show_selected_keyword) {
479    // Assume the keyword might be hidden.
480    entry_width -= (kEdgeThickness + kEdgeEditPadding);
481  } else if (model_->GetSecurityLevel() == ToolbarModel::EV_SECURE) {
482    ev_bubble_view_->SetVisible(true);
483    ev_bubble_view_->SetLabel(model_->GetEVCertName());
484    ev_bubble_width = ev_bubble_view_->GetPreferredSize().width();
485    // We'll adjust this width and take it out of |entry_width| below.
486  } else {
487    location_icon_view_->SetVisible(true);
488    location_icon_width = location_icon_view_->GetPreferredSize().width();
489    entry_width -= (kEdgeThickness + kEdgeItemPadding + location_icon_width +
490        kItemEditPadding);
491  }
492
493  if (star_view_ && star_view_->IsVisible())
494    entry_width -= star_view_->GetPreferredSize().width() + kItemPadding;
495  for (PageActionViews::const_iterator i(page_action_views_.begin());
496       i != page_action_views_.end(); ++i) {
497    if ((*i)->IsVisible())
498      entry_width -= ((*i)->GetPreferredSize().width() + kItemPadding);
499  }
500  for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
501       i != content_setting_views_.end(); ++i) {
502    if ((*i)->IsVisible())
503      entry_width -= ((*i)->GetPreferredSize().width() + kItemPadding);
504  }
505  // The gap between the edit and whatever is to its right is shortened.
506  entry_width += kEditInternalSpace;
507
508  // Size the EV bubble.  We do this after taking the star/page actions/content
509  // settings out of |entry_width| so we won't take too much space.
510  if (ev_bubble_width) {
511    // Try to elide the bubble to be no larger than half the total available
512    // space, but never elide it any smaller than 150 px.
513    static const int kMinElidedBubbleWidth = 150;
514    static const double kMaxBubbleFraction = 0.5;
515    const int total_padding =
516        kEdgeThickness + kBubbleHorizontalPadding + kItemEditPadding;
517    ev_bubble_width = std::min(ev_bubble_width, std::max(kMinElidedBubbleWidth,
518        static_cast<int>((entry_width - total_padding) * kMaxBubbleFraction)));
519    entry_width -= (total_padding + ev_bubble_width);
520  }
521
522#if defined(OS_WIN)
523  RECT formatting_rect;
524  location_entry_->GetRect(&formatting_rect);
525  RECT edit_bounds;
526  location_entry_->GetClientRect(&edit_bounds);
527  int max_edit_width = entry_width - formatting_rect.left -
528                       (edit_bounds.right - formatting_rect.right);
529#else
530  int max_edit_width = entry_width;
531#endif
532
533  if (max_edit_width < 0)
534    return;
535  const int available_width = AvailableWidth(max_edit_width);
536
537  const bool show_keyword_hint = !keyword.empty() && is_keyword_hint;
538  selected_keyword_view_->SetVisible(show_selected_keyword);
539  keyword_hint_view_->SetVisible(show_keyword_hint);
540  if (show_selected_keyword) {
541    if (selected_keyword_view_->keyword() != keyword) {
542      selected_keyword_view_->SetKeyword(keyword);
543      const TemplateURL* template_url =
544          profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword);
545      if (template_url && template_url->IsExtensionKeyword()) {
546        const SkBitmap& bitmap = profile_->GetExtensionService()->
547            GetOmniboxIcon(template_url->GetExtensionId());
548        selected_keyword_view_->SetImage(bitmap);
549        selected_keyword_view_->set_is_extension_icon(true);
550      } else {
551        selected_keyword_view_->SetImage(*ResourceBundle::GetSharedInstance().
552            GetBitmapNamed(IDR_OMNIBOX_SEARCH));
553        selected_keyword_view_->set_is_extension_icon(false);
554      }
555    }
556  } else if (show_keyword_hint) {
557    if (keyword_hint_view_->keyword() != keyword)
558      keyword_hint_view_->SetKeyword(keyword);
559  }
560
561  // Lay out items to the right of the edit field.
562  int offset = width() - kEdgeThickness - kEdgeItemPadding;
563  if (star_view_ && star_view_->IsVisible()) {
564    int star_width = star_view_->GetPreferredSize().width();
565    offset -= star_width;
566    star_view_->SetBounds(offset, location_y, star_width, location_height);
567    offset -= kItemPadding;
568  }
569
570  for (PageActionViews::const_iterator i(page_action_views_.begin());
571       i != page_action_views_.end(); ++i) {
572    if ((*i)->IsVisible()) {
573      int page_action_width = (*i)->GetPreferredSize().width();
574      offset -= page_action_width;
575      (*i)->SetBounds(offset, location_y, page_action_width, location_height);
576      offset -= kItemPadding;
577    }
578  }
579  // We use a reverse_iterator here because we're laying out the views from
580  // right to left but in the vector they're ordered left to right.
581  for (ContentSettingViews::const_reverse_iterator
582       i(content_setting_views_.rbegin()); i != content_setting_views_.rend();
583       ++i) {
584    if ((*i)->IsVisible()) {
585      int content_blocked_width = (*i)->GetPreferredSize().width();
586      offset -= content_blocked_width;
587      (*i)->SetBounds(offset, location_y, content_blocked_width,
588                      location_height);
589      offset -= kItemPadding;
590    }
591  }
592
593  // Now lay out items to the left of the edit field.
594  if (location_icon_view_->IsVisible()) {
595    location_icon_view_->SetBounds(kEdgeThickness + kEdgeItemPadding,
596        location_y, location_icon_width, location_height);
597    offset = location_icon_view_->bounds().right() + kItemEditPadding;
598  } else if (ev_bubble_view_->IsVisible()) {
599    ev_bubble_view_->SetBounds(kEdgeThickness + kBubbleHorizontalPadding,
600        location_y + kBubbleVerticalPadding, ev_bubble_width,
601        ev_bubble_view_->GetPreferredSize().height());
602    offset = ev_bubble_view_->bounds().right() + kItemEditPadding;
603  } else {
604    offset = kEdgeThickness +
605        (show_selected_keyword ? kBubbleHorizontalPadding : kEdgeEditPadding);
606  }
607
608  // Now lay out the edit field and views that autocollapse to give it more
609  // room.
610  gfx::Rect location_bounds(offset, location_y, entry_width, location_height);
611  if (show_selected_keyword) {
612    selected_keyword_view_->SetBounds(0, location_y + kBubbleVerticalPadding,
613        0, selected_keyword_view_->GetPreferredSize().height());
614    LayoutView(selected_keyword_view_, kItemEditPadding, available_width,
615               true, &location_bounds);
616    location_bounds.set_x(selected_keyword_view_->IsVisible() ?
617        (offset + selected_keyword_view_->width() + kItemEditPadding) :
618        (kEdgeThickness + kEdgeEditPadding));
619  } else if (show_keyword_hint) {
620    keyword_hint_view_->SetBounds(0, location_y, 0, location_height);
621    // Tricky: |entry_width| has already been enlarged by |kEditInternalSpace|.
622    // But if we add a trailing view, it needs to have that enlargement be to
623    // its left.  So we undo the enlargement, then include it in the padding for
624    // the added view.
625    location_bounds.Inset(0, 0, kEditInternalSpace, 0);
626    LayoutView(keyword_hint_view_, kItemEditPadding, available_width, false,
627               &location_bounds);
628    if (!keyword_hint_view_->IsVisible()) {
629      // Put back the enlargement that we undid above.
630      location_bounds.Inset(0, 0, -kEditInternalSpace, 0);
631    }
632  }
633
634#if defined(OS_WIN)
635  // Layout out the suggested text view right aligned to the location
636  // entry. Only show the suggested text if we can fit the text from one
637  // character before the end of the selection to the end of the text and the
638  // suggested text. If we can't it means either the suggested text is too big,
639  // or the user has scrolled.
640
641  // TODO(sky): We could potentially combine this with the previous step to
642  // force using minimum size if necessary, but currently the chance of showing
643  // keyword hints and suggested text is minimal and we're not confident this
644  // is the right approach for suggested text.
645  if (suggested_text_view_) {
646    // TODO(sky): need to layout when the user changes caret position.
647    int suggested_text_width = suggested_text_view_->GetPreferredSize().width();
648    int vis_text_width = location_entry_->WidthOfTextAfterCursor();
649    if (vis_text_width + suggested_text_width > entry_width) {
650      // Hide the suggested text if the user has scrolled or we can't fit all
651      // the suggested text.
652      suggested_text_view_->SetBounds(0, 0, 0, 0);
653    } else {
654      int location_needed_width = location_entry_->TextWidth();
655      location_bounds.set_width(std::min(location_needed_width,
656                                         entry_width - suggested_text_width));
657      // TODO(sky): figure out why this needs the -1.
658      suggested_text_view_->SetBounds(location_bounds.right() - 1,
659                                      location_bounds.y(),
660                                      suggested_text_width,
661                                      location_bounds.height());
662    }
663  }
664#endif
665
666  location_entry_view_->SetBoundsRect(location_bounds);
667}
668
669void LocationBarView::OnPaint(gfx::Canvas* canvas) {
670  View::OnPaint(canvas);
671
672  if (painter_.get()) {
673    painter_->Paint(width(), height(), canvas);
674  } else if (mode_ == POPUP) {
675    canvas->TileImageInt(*GetThemeProvider()->GetBitmapNamed(
676        IDR_LOCATIONBG_POPUPMODE_CENTER), 0, 0, 0, 0, width(), height());
677  }
678  // When used in the app launcher, don't draw a border, the LocationBarView has
679  // its own views::Border.
680
681  // Draw the background color so that the graphical elements at the edges
682  // appear over the correct color.  (The edit draws its own background, so this
683  // isn't important for that.)
684  // TODO(pkasting): We need images that are transparent in the middle, so we
685  // can draw the border images over the background color instead of the
686  // reverse; this antialiases better (see comments in
687  // AutocompletePopupContentsView::OnPaint()).
688  gfx::Rect bounds(GetContentsBounds());
689  bounds.Inset(0, kVerticalEdgeThickness);
690  SkColor color(GetColor(ToolbarModel::NONE, BACKGROUND));
691  if (mode_ == NORMAL) {
692    SkPaint paint;
693    paint.setColor(color);
694    paint.setStyle(SkPaint::kFill_Style);
695    paint.setAntiAlias(true);
696    // The round corners of the omnibox match the round corners of the dropdown
697    // below, and all our other bubbles.
698    const SkScalar radius(SkIntToScalar(BubbleBorder::GetCornerRadius()));
699    bounds.Inset(kNormalHorizontalEdgeThickness, 0);
700    canvas->AsCanvasSkia()->drawRoundRect(gfx::RectToSkRect(bounds), radius,
701                                          radius, paint);
702  } else {
703    canvas->FillRectInt(color, bounds.x(), bounds.y(), bounds.width(),
704                        bounds.height());
705  }
706
707  if (show_focus_rect_ && HasFocus()) {
708    gfx::Rect r = location_entry_view_->bounds();
709#if defined(OS_WIN)
710    canvas->DrawFocusRect(r.x() - 1, r.y() - 1, r.width() + 2, r.height() + 2);
711#else
712    canvas->DrawFocusRect(r.x() - 1, r.y(), r.width() + 2, r.height());
713#endif
714  }
715}
716
717void LocationBarView::SetShowFocusRect(bool show) {
718  show_focus_rect_ = show;
719  SchedulePaint();
720}
721
722void LocationBarView::SelectAll() {
723  location_entry_->SelectAll(true);
724}
725
726#if defined(OS_WIN)
727bool LocationBarView::OnMousePressed(const views::MouseEvent& event) {
728  UINT msg;
729  if (event.IsLeftMouseButton()) {
730    msg = (event.flags() & ui::EF_IS_DOUBLE_CLICK) ?
731        WM_LBUTTONDBLCLK : WM_LBUTTONDOWN;
732  } else if (event.IsMiddleMouseButton()) {
733    msg = (event.flags() & ui::EF_IS_DOUBLE_CLICK) ?
734        WM_MBUTTONDBLCLK : WM_MBUTTONDOWN;
735  } else if (event.IsRightMouseButton()) {
736    msg = (event.flags() & ui::EF_IS_DOUBLE_CLICK) ?
737        WM_RBUTTONDBLCLK : WM_RBUTTONDOWN;
738  } else {
739    NOTREACHED();
740    return false;
741  }
742  OnMouseEvent(event, msg);
743  return true;
744}
745
746bool LocationBarView::OnMouseDragged(const views::MouseEvent& event) {
747  OnMouseEvent(event, WM_MOUSEMOVE);
748  return true;
749}
750
751void LocationBarView::OnMouseReleased(const views::MouseEvent& event) {
752  UINT msg;
753  if (event.IsLeftMouseButton()) {
754    msg = WM_LBUTTONUP;
755  } else if (event.IsMiddleMouseButton()) {
756    msg = WM_MBUTTONUP;
757  } else if (event.IsRightMouseButton()) {
758    msg = WM_RBUTTONUP;
759  } else {
760    NOTREACHED();
761    return;
762  }
763  OnMouseEvent(event, msg);
764}
765
766void LocationBarView::OnMouseCaptureLost() {
767  location_entry_->HandleExternalMsg(WM_CAPTURECHANGED, 0, CPoint());
768}
769#endif
770
771void LocationBarView::OnAutocompleteAccept(
772    const GURL& url,
773    WindowOpenDisposition disposition,
774    PageTransition::Type transition,
775    const GURL& alternate_nav_url) {
776  // WARNING: don't add an early return here. The calls after the if must
777  // happen.
778  if (url.is_valid()) {
779    location_input_ = UTF8ToWide(url.spec());
780    disposition_ = disposition;
781    transition_ = transition;
782
783    if (command_updater_) {
784      if (!alternate_nav_url.is_valid()) {
785        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
786      } else {
787        AlternateNavURLFetcher* fetcher =
788            new AlternateNavURLFetcher(alternate_nav_url);
789        // The AlternateNavURLFetcher will listen for the pending navigation
790        // notification that will be issued as a result of the "open URL." It
791        // will automatically install itself into that navigation controller.
792        command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL);
793        if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) {
794          // I'm not sure this should be reachable, but I'm not also sure enough
795          // that it shouldn't to stick in a NOTREACHED().  In any case, this is
796          // harmless.
797          delete fetcher;
798        } else {
799          // The navigation controller will delete the fetcher.
800        }
801      }
802    }
803  }
804}
805
806void LocationBarView::OnChanged() {
807  location_icon_view_->SetImage(
808      ResourceBundle::GetSharedInstance().GetBitmapNamed(
809          location_entry_->GetIcon()));
810  location_icon_view_->ShowTooltip(!location_entry()->IsEditingOrEmpty());
811
812  Layout();
813  SchedulePaint();
814}
815
816void LocationBarView::OnSelectionBoundsChanged() {
817#if defined(OS_WIN)
818  if (suggested_text_view_)
819    suggested_text_view_->StopAnimation();
820#else
821  NOTREACHED();
822#endif
823}
824
825void LocationBarView::OnInputInProgress(bool in_progress) {
826  delegate_->OnInputInProgress(in_progress);
827}
828
829void LocationBarView::OnKillFocus() {
830}
831
832void LocationBarView::OnSetFocus() {
833  views::FocusManager* focus_manager = GetFocusManager();
834  if (!focus_manager) {
835    NOTREACHED();
836    return;
837  }
838  focus_manager->SetFocusedView(this);
839}
840
841SkBitmap LocationBarView::GetFavicon() const {
842  return GetTabContentsFromDelegate(delegate_)->GetFavicon();
843}
844
845string16 LocationBarView::GetTitle() const {
846  return GetTabContentsFromDelegate(delegate_)->GetTitle();
847}
848
849InstantController* LocationBarView::GetInstant() {
850  return delegate_->GetInstant();
851}
852
853TabContentsWrapper* LocationBarView::GetTabContentsWrapper() const {
854  return delegate_->GetTabContentsWrapper();
855}
856
857int LocationBarView::AvailableWidth(int location_bar_width) {
858  return location_bar_width - location_entry_->TextWidth();
859}
860
861void LocationBarView::LayoutView(views::View* view,
862                                 int padding,
863                                 int available_width,
864                                 bool leading,
865                                 gfx::Rect* bounds) {
866  DCHECK(view && bounds);
867  gfx::Size view_size = view->GetPreferredSize();
868  if ((view_size.width() + padding) > available_width)
869    view_size = view->GetMinimumSize();
870  int desired_width = view_size.width() + padding;
871  view->SetVisible(desired_width < bounds->width());
872  if (view->IsVisible()) {
873    view->SetBounds(
874        leading ? bounds->x() : (bounds->right() - view_size.width()),
875        view->y(), view_size.width(), view->height());
876    bounds->set_width(bounds->width() - desired_width);
877  }
878}
879
880void LocationBarView::RefreshContentSettingViews() {
881  for (ContentSettingViews::const_iterator i(content_setting_views_.begin());
882       i != content_setting_views_.end(); ++i) {
883    (*i)->UpdateFromTabContents(model_->input_in_progress() ? NULL :
884                                GetTabContentsFromDelegate(delegate_));
885  }
886}
887
888void LocationBarView::DeletePageActionViews() {
889  for (PageActionViews::const_iterator i(page_action_views_.begin());
890       i != page_action_views_.end(); ++i)
891    RemoveChildView(*i);
892  STLDeleteElements(&page_action_views_);
893}
894
895void LocationBarView::RefreshPageActionViews() {
896  if (mode_ != NORMAL)
897    return;
898
899  ExtensionService* service = profile_->GetExtensionService();
900  if (!service)
901    return;
902
903  std::map<ExtensionAction*, bool> old_visibility;
904  for (PageActionViews::const_iterator i(page_action_views_.begin());
905       i != page_action_views_.end(); ++i)
906    old_visibility[(*i)->image_view()->page_action()] = (*i)->IsVisible();
907
908  // Remember the previous visibility of the page actions so that we can
909  // notify when this changes.
910  std::vector<ExtensionAction*> page_actions;
911  for (size_t i = 0; i < service->extensions()->size(); ++i) {
912    if (service->extensions()->at(i)->page_action())
913      page_actions.push_back(service->extensions()->at(i)->page_action());
914  }
915
916  // On startup we sometimes haven't loaded any extensions. This makes sure
917  // we catch up when the extensions (and any page actions) load.
918  if (page_actions.size() != page_action_views_.size()) {
919    DeletePageActionViews();  // Delete the old views (if any).
920
921    page_action_views_.resize(page_actions.size());
922
923    // Add the page actions in reverse order, so that the child views are
924    // inserted in left-to-right order for accessibility.
925    for (int i = page_actions.size() - 1; i >= 0; --i) {
926      page_action_views_[i] = new PageActionWithBadgeView(
927          new PageActionImageView(this, profile_, page_actions[i]));
928      page_action_views_[i]->SetVisible(false);
929      AddChildViewAt(page_action_views_[i], GetIndexOf(star_view_));
930    }
931  }
932
933  TabContents* contents = GetTabContentsFromDelegate(delegate_);
934  if (!page_action_views_.empty() && contents) {
935    GURL url = GURL(WideToUTF8(model_->GetText()));
936
937    for (PageActionViews::const_iterator i(page_action_views_.begin());
938         i != page_action_views_.end(); ++i) {
939      (*i)->UpdateVisibility(model_->input_in_progress() ? NULL : contents,
940                             url);
941
942      // Check if the visibility of the action changed and notify if it did.
943      ExtensionAction* action = (*i)->image_view()->page_action();
944      if (old_visibility.find(action) == old_visibility.end() ||
945          old_visibility[action] != (*i)->IsVisible()) {
946        NotificationService::current()->Notify(
947            NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
948            Source<ExtensionAction>(action),
949            Details<TabContents>(contents));
950      }
951    }
952  }
953}
954
955#if defined(OS_WIN)
956void LocationBarView::OnMouseEvent(const views::MouseEvent& event, UINT msg) {
957  UINT flags = event.GetWindowsFlags();
958  gfx::Point screen_point(event.location());
959  ConvertPointToScreen(this, &screen_point);
960  location_entry_->HandleExternalMsg(msg, flags, screen_point.ToPOINT());
961}
962#endif
963
964void LocationBarView::ShowFirstRunBubbleInternal(
965    FirstRun::BubbleType bubble_type) {
966#if defined(OS_WIN)  // First run bubble doesn't make sense for Chrome OS.
967  // Point at the start of the edit control; adjust to look as good as possible.
968  const int kXOffset = kNormalHorizontalEdgeThickness + kEdgeItemPadding +
969      ResourceBundle::GetSharedInstance().GetBitmapNamed(
970      IDR_OMNIBOX_HTTP)->width() + kItemPadding;
971  const int kYOffset = -(kVerticalEdgeThickness + 2);
972  gfx::Point origin(location_entry_view_->bounds().x() + kXOffset,
973                    y() + height() + kYOffset);
974  // If the UI layout is RTL, the coordinate system is not transformed and
975  // therefore we need to adjust the X coordinate so that bubble appears on the
976  // right hand side of the location bar.
977  if (base::i18n::IsRTL())
978    origin.set_x(width() - origin.x());
979  views::View::ConvertPointToScreen(this, &origin);
980  FirstRunBubble::Show(profile_, GetWidget(), gfx::Rect(origin, gfx::Size()),
981                       BubbleBorder::TOP_LEFT, bubble_type);
982#endif
983}
984
985std::string LocationBarView::GetClassName() const {
986  return kViewClassName;
987}
988
989bool LocationBarView::SkipDefaultKeyEventProcessing(
990    const views::KeyEvent& event) {
991#if defined(OS_WIN)
992  if (views::FocusManager::IsTabTraversalKeyEvent(event)) {
993    if (HasValidSuggestText()) {
994      // Return true so that the edit sees the tab and commits the suggestion.
995      return true;
996    }
997    if (keyword_hint_view_->IsVisible() && !event.IsShiftDown()) {
998      // Return true so the edit gets the tab event and enters keyword mode.
999      return true;
1000    }
1001
1002    // If the caret is not at the end, then tab moves the caret to the end.
1003    if (!location_entry_->IsCaretAtEnd())
1004      return true;
1005
1006    // Tab while showing instant commits instant immediately.
1007    // Return true so that focus traversal isn't attempted. The edit ends
1008    // up doing nothing in this case.
1009    if (location_entry_->model()->AcceptCurrentInstantPreview())
1010      return true;
1011  }
1012
1013  return location_entry_->SkipDefaultKeyEventProcessing(event);
1014#else
1015  // This method is not used for Linux ports. See FocusManager::OnKeyEvent() in
1016  // src/views/focus/focus_manager.cc for details.
1017  return false;
1018#endif
1019}
1020
1021void LocationBarView::GetAccessibleState(ui::AccessibleViewState* state) {
1022  state->role = ui::AccessibilityTypes::ROLE_GROUPING;
1023  state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_LOCATION);
1024  state->value = location_entry_->GetText();
1025
1026  string16::size_type entry_start;
1027  string16::size_type entry_end;
1028  location_entry_->GetSelectionBounds(&entry_start, &entry_end);
1029  state->selection_start = entry_start;
1030  state->selection_end = entry_end;
1031}
1032
1033void LocationBarView::WriteDragDataForView(views::View* sender,
1034                                           const gfx::Point& press_pt,
1035                                           OSExchangeData* data) {
1036  DCHECK_NE(GetDragOperationsForView(sender, press_pt),
1037            ui::DragDropTypes::DRAG_NONE);
1038
1039  TabContents* tab_contents = GetTabContentsFromDelegate(delegate_);
1040  DCHECK(tab_contents);
1041  drag_utils::SetURLAndDragImage(tab_contents->GetURL(),
1042                                 UTF16ToWideHack(tab_contents->GetTitle()),
1043                                 tab_contents->GetFavicon(), data);
1044}
1045
1046int LocationBarView::GetDragOperationsForView(views::View* sender,
1047                                              const gfx::Point& p) {
1048  DCHECK((sender == location_icon_view_) || (sender == ev_bubble_view_));
1049  TabContents* tab_contents = GetTabContentsFromDelegate(delegate_);
1050  return (tab_contents && tab_contents->GetURL().is_valid() &&
1051          !location_entry()->IsEditingOrEmpty()) ?
1052      (ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK) :
1053      ui::DragDropTypes::DRAG_NONE;
1054}
1055
1056bool LocationBarView::CanStartDragForView(View* sender,
1057                                          const gfx::Point& press_pt,
1058                                          const gfx::Point& p) {
1059  return true;
1060}
1061
1062////////////////////////////////////////////////////////////////////////////////
1063// LocationBarView, LocationBar implementation:
1064
1065void LocationBarView::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) {
1066  // Wait until search engines have loaded to show the first run bubble.
1067  if (!profile_->GetTemplateURLModel()->loaded()) {
1068    bubble_type_ = bubble_type;
1069    template_url_model_ = profile_->GetTemplateURLModel();
1070    template_url_model_->AddObserver(this);
1071    template_url_model_->Load();
1072    return;
1073  }
1074  ShowFirstRunBubbleInternal(bubble_type);
1075}
1076
1077void LocationBarView::SetSuggestedText(const string16& text,
1078                                       InstantCompleteBehavior behavior) {
1079  location_entry_->model()->SetSuggestedText(text, behavior);
1080}
1081
1082std::wstring LocationBarView::GetInputString() const {
1083  return location_input_;
1084}
1085
1086WindowOpenDisposition LocationBarView::GetWindowOpenDisposition() const {
1087  return disposition_;
1088}
1089
1090PageTransition::Type LocationBarView::GetPageTransition() const {
1091  return transition_;
1092}
1093
1094void LocationBarView::AcceptInput() {
1095  location_entry_->model()->AcceptInput(CURRENT_TAB, false);
1096}
1097
1098void LocationBarView::FocusLocation(bool select_all) {
1099  location_entry_->SetFocus();
1100  if (select_all)
1101    location_entry_->SelectAll(true);
1102}
1103
1104void LocationBarView::FocusSearch() {
1105  location_entry_->SetFocus();
1106  location_entry_->SetForcedQuery();
1107}
1108
1109void LocationBarView::SaveStateToContents(TabContents* contents) {
1110  location_entry_->SaveStateToTab(contents);
1111}
1112
1113void LocationBarView::Revert() {
1114  location_entry_->RevertAll();
1115}
1116
1117const AutocompleteEditView* LocationBarView::location_entry() const {
1118  return location_entry_.get();
1119}
1120
1121AutocompleteEditView* LocationBarView::location_entry() {
1122  return location_entry_.get();
1123}
1124
1125LocationBarTesting* LocationBarView::GetLocationBarForTesting() {
1126  return this;
1127}
1128
1129int LocationBarView::PageActionCount() {
1130  return page_action_views_.size();
1131}
1132
1133int LocationBarView::PageActionVisibleCount() {
1134  int result = 0;
1135  for (size_t i = 0; i < page_action_views_.size(); i++) {
1136    if (page_action_views_[i]->IsVisible())
1137      ++result;
1138  }
1139  return result;
1140}
1141
1142ExtensionAction* LocationBarView::GetPageAction(size_t index) {
1143  if (index < page_action_views_.size())
1144    return page_action_views_[index]->image_view()->page_action();
1145
1146  NOTREACHED();
1147  return NULL;
1148}
1149
1150ExtensionAction* LocationBarView::GetVisiblePageAction(size_t index) {
1151  size_t current = 0;
1152  for (size_t i = 0; i < page_action_views_.size(); ++i) {
1153    if (page_action_views_[i]->IsVisible()) {
1154      if (current == index)
1155        return page_action_views_[i]->image_view()->page_action();
1156
1157      ++current;
1158    }
1159  }
1160
1161  NOTREACHED();
1162  return NULL;
1163}
1164
1165void LocationBarView::TestPageActionPressed(size_t index) {
1166  size_t current = 0;
1167  for (size_t i = 0; i < page_action_views_.size(); ++i) {
1168    if (page_action_views_[i]->IsVisible()) {
1169      if (current == index) {
1170        const int kLeftMouseButton = 1;
1171        page_action_views_[i]->image_view()->ExecuteAction(kLeftMouseButton,
1172            false);  // inspect_with_devtools
1173        return;
1174      }
1175      ++current;
1176    }
1177  }
1178
1179  NOTREACHED();
1180}
1181
1182void LocationBarView::OnTemplateURLModelChanged() {
1183  template_url_model_->RemoveObserver(this);
1184  template_url_model_ = NULL;
1185  // If the browser is no longer active, let's not show the info bubble, as this
1186  // would make the browser the active window again.
1187  if (location_entry_view_ && location_entry_view_->GetWidget()->IsActive())
1188    ShowFirstRunBubble(bubble_type_);
1189}
1190
1191void LocationBarView::Observe(NotificationType type,
1192                              const NotificationSource& source,
1193                              const NotificationDetails& details) {
1194  if (type.value == NotificationType::PREF_CHANGED) {
1195    std::string* name = Details<std::string>(details).ptr();
1196    if (*name == prefs::kEditBookmarksEnabled)
1197      Update(NULL);
1198  }
1199}
1200
1201#if defined(OS_WIN)
1202bool LocationBarView::HasValidSuggestText() const {
1203  return suggested_text_view_ && !suggested_text_view_->size().IsEmpty() &&
1204      !suggested_text_view_->GetText().empty();
1205}
1206#endif
1207