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