location_bar_view.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/string_split.h" 15#include "base/strings/utf_string_conversions.h" 16#include "chrome/app/chrome_command_ids.h" 17#include "chrome/browser/chrome_notification_types.h" 18#include "chrome/browser/command_updater.h" 19#include "chrome/browser/defaults.h" 20#include "chrome/browser/extensions/api/omnibox/omnibox_api.h" 21#include "chrome/browser/extensions/extension_service.h" 22#include "chrome/browser/extensions/location_bar_controller.h" 23#include "chrome/browser/extensions/tab_helper.h" 24#include "chrome/browser/favicon/favicon_tab_helper.h" 25#include "chrome/browser/profiles/profile.h" 26#include "chrome/browser/search/instant_service.h" 27#include "chrome/browser/search/instant_service_factory.h" 28#include "chrome/browser/search/search.h" 29#include "chrome/browser/search_engines/template_url.h" 30#include "chrome/browser/search_engines/template_url_service.h" 31#include "chrome/browser/search_engines/template_url_service_factory.h" 32#include "chrome/browser/translate/translate_service.h" 33#include "chrome/browser/translate/translate_tab_helper.h" 34#include "chrome/browser/ui/browser.h" 35#include "chrome/browser/ui/browser_finder.h" 36#include "chrome/browser/ui/browser_instant_controller.h" 37#include "chrome/browser/ui/browser_window.h" 38#include "chrome/browser/ui/omnibox/location_bar_util.h" 39#include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 40#include "chrome/browser/ui/omnibox/omnibox_popup_view.h" 41#include "chrome/browser/ui/passwords/manage_passwords_icon.h" 42#include "chrome/browser/ui/passwords/manage_passwords_ui_controller.h" 43#include "chrome/browser/ui/tabs/tab_strip_model.h" 44#include "chrome/browser/ui/toolbar/origin_chip_info.h" 45#include "chrome/browser/ui/view_ids.h" 46#include "chrome/browser/ui/views/browser_dialogs.h" 47#include "chrome/browser/ui/views/location_bar/content_setting_image_view.h" 48#include "chrome/browser/ui/views/location_bar/ev_bubble_view.h" 49#include "chrome/browser/ui/views/location_bar/generated_credit_card_view.h" 50#include "chrome/browser/ui/views/location_bar/keyword_hint_view.h" 51#include "chrome/browser/ui/views/location_bar/location_bar_layout.h" 52#include "chrome/browser/ui/views/location_bar/location_icon_view.h" 53#include "chrome/browser/ui/views/location_bar/open_pdf_in_reader_view.h" 54#include "chrome/browser/ui/views/location_bar/origin_chip_view.h" 55#include "chrome/browser/ui/views/location_bar/page_action_image_view.h" 56#include "chrome/browser/ui/views/location_bar/page_action_with_badge_view.h" 57#include "chrome/browser/ui/views/location_bar/selected_keyword_view.h" 58#include "chrome/browser/ui/views/location_bar/star_view.h" 59#include "chrome/browser/ui/views/location_bar/translate_icon_view.h" 60#include "chrome/browser/ui/views/location_bar/zoom_bubble_view.h" 61#include "chrome/browser/ui/views/location_bar/zoom_view.h" 62#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h" 63#include "chrome/browser/ui/views/passwords/manage_passwords_icon_view.h" 64#include "chrome/browser/ui/zoom/zoom_controller.h" 65#include "chrome/common/chrome_switches.h" 66#include "chrome/common/pref_names.h" 67#include "content/public/browser/notification_service.h" 68#include "content/public/browser/render_widget_host_view.h" 69#include "content/public/browser/web_contents.h" 70#include "extensions/browser/extension_system.h" 71#include "extensions/common/feature_switch.h" 72#include "extensions/common/permissions/permissions_data.h" 73#include "grit/generated_resources.h" 74#include "grit/theme_resources.h" 75#include "ui/accessibility/ax_view_state.h" 76#include "ui/base/dragdrop/drag_drop_types.h" 77#include "ui/base/l10n/l10n_util.h" 78#include "ui/base/resource/resource_bundle.h" 79#include "ui/base/theme_provider.h" 80#include "ui/events/event.h" 81#include "ui/gfx/animation/slide_animation.h" 82#include "ui/gfx/canvas.h" 83#include "ui/gfx/color_utils.h" 84#include "ui/gfx/image/image.h" 85#include "ui/gfx/image/image_skia_operations.h" 86#include "ui/gfx/skia_util.h" 87#include "ui/gfx/text_utils.h" 88#include "ui/native_theme/native_theme.h" 89#include "ui/views/background.h" 90#include "ui/views/border.h" 91#include "ui/views/button_drag_utils.h" 92#include "ui/views/controls/button/image_button.h" 93#include "ui/views/controls/button/label_button.h" 94#include "ui/views/controls/button/label_button_border.h" 95#include "ui/views/controls/label.h" 96#include "ui/views/widget/widget.h" 97 98#if !defined(OS_CHROMEOS) 99#include "chrome/browser/ui/views/first_run_bubble.h" 100#endif 101 102using content::WebContents; 103using views::View; 104 105namespace { 106 107// The search button images are made to look as if they overlay the normal edge 108// images, but to align things, the search button needs to be inset horizontally 109// by 1 px. 110const int kSearchButtonInset = 1; 111 112// Given a containing |height| and a |base_font_list|, shrinks the font size 113// until the font list will fit within |height| while having its cap height 114// vertically centered. Returns the correctly-sized font list. 115// 116// The expected layout: 117// +--------+-----------------------------------------------+------------+ 118// | | y offset | space | 119// | +--------+-------------------+------------------+ above | 120// | | | | internal leading | cap height | 121// | box | font | ascent (baseline) +------------------+------------+ 122// | height | height | | cap height | 123// | | |-------------------+------------------+------------+ 124// | | | descent (height - baseline) | space | 125// | +--------+--------------------------------------+ below | 126// | | space at bottom | cap height | 127// +--------+-----------------------------------------------+------------+ 128// Goal: 129// center of box height == center of cap height 130// (i.e. space above cap height == space below cap height) 131// Restrictions: 132// y offset >= 0 133// space at bottom >= 0 134// (i.e. Entire font must be visible inside the box.) 135gfx::FontList GetLargestFontListWithHeightBound( 136 const gfx::FontList& base_font_list, 137 int height) { 138 gfx::FontList font_list = base_font_list; 139 for (int font_size = font_list.GetFontSize(); font_size > 1; --font_size) { 140 const int internal_leading = 141 font_list.GetBaseline() - font_list.GetCapHeight(); 142 // Some platforms don't support getting the cap height, and simply return 143 // the entire font ascent from GetCapHeight(). Centering the ascent makes 144 // the font look too low, so if GetCapHeight() returns the ascent, center 145 // the entire font height instead. 146 const int space = 147 height - ((internal_leading != 0) ? 148 font_list.GetCapHeight() : font_list.GetHeight()); 149 const int y_offset = space / 2 - internal_leading; 150 const int space_at_bottom = height - (y_offset + font_list.GetHeight()); 151 if ((y_offset >= 0) && (space_at_bottom >= 0)) 152 break; 153 font_list = font_list.DeriveWithSizeDelta(-1); 154 } 155 return font_list; 156} 157 158int GetEditLeadingInternalSpace() { 159 // The textfield has 1 px of whitespace before the text in the RTL case only. 160 return base::i18n::IsRTL() ? 1 : 0; 161} 162 163// Functor for moving BookmarkManagerPrivate page actions to the right via 164// stable_partition. 165class IsPageActionViewRightAligned { 166 public: 167 explicit IsPageActionViewRightAligned(ExtensionService* extension_service) 168 : extension_service_(extension_service) {} 169 170 bool operator()(PageActionWithBadgeView* page_action_view) { 171 return extensions::PermissionsData::HasAPIPermission( 172 extension_service_->GetExtensionById( 173 page_action_view->image_view()->page_action()->extension_id(), 174 false), 175 extensions::APIPermission::kBookmarkManagerPrivate); 176 } 177 178 private: 179 ExtensionService* extension_service_; 180 181 // NOTE: Can't DISALLOW_COPY_AND_ASSIGN as we pass this object by value to 182 // std::stable_partition(). 183}; 184 185} // namespace 186 187 188// LocationBarView ----------------------------------------------------------- 189 190// static 191const int LocationBarView::kNormalEdgeThickness = 2; 192const int LocationBarView::kPopupEdgeThickness = 1; 193const int LocationBarView::kItemPadding = 3; 194const int LocationBarView::kIconInternalPadding = 2; 195const int LocationBarView::kBubblePadding = 1; 196const char LocationBarView::kViewClassName[] = "LocationBarView"; 197 198LocationBarView::LocationBarView(Browser* browser, 199 Profile* profile, 200 CommandUpdater* command_updater, 201 Delegate* delegate, 202 bool is_popup_mode) 203 : LocationBar(profile), 204 OmniboxEditController(command_updater), 205 browser_(browser), 206 omnibox_view_(NULL), 207 delegate_(delegate), 208 origin_chip_view_(NULL), 209 location_icon_view_(NULL), 210 ev_bubble_view_(NULL), 211 ime_inline_autocomplete_view_(NULL), 212 selected_keyword_view_(NULL), 213 suggested_text_view_(NULL), 214 keyword_hint_view_(NULL), 215 mic_search_view_(NULL), 216 zoom_view_(NULL), 217 generated_credit_card_view_(NULL), 218 open_pdf_in_reader_view_(NULL), 219 manage_passwords_icon_view_(NULL), 220 translate_icon_view_(NULL), 221 star_view_(NULL), 222 search_button_(NULL), 223 is_popup_mode_(is_popup_mode), 224 show_focus_rect_(false), 225 template_url_service_(NULL), 226 dropdown_animation_offset_(0), 227 animated_host_label_(NULL), 228 weak_ptr_factory_(this) { 229 edit_bookmarks_enabled_.Init( 230 prefs::kEditBookmarksEnabled, profile->GetPrefs(), 231 base::Bind(&LocationBarView::Update, base::Unretained(this), 232 static_cast<content::WebContents*>(NULL))); 233 234 if (browser_) 235 browser_->search_model()->AddObserver(this); 236} 237 238LocationBarView::~LocationBarView() { 239 if (template_url_service_) 240 template_url_service_->RemoveObserver(this); 241 if (browser_) 242 browser_->search_model()->RemoveObserver(this); 243} 244 245//////////////////////////////////////////////////////////////////////////////// 246// LocationBarView, public: 247 248void LocationBarView::Init() { 249 // We need to be in a Widget, otherwise GetNativeTheme() may change and we're 250 // not prepared for that. 251 DCHECK(GetWidget()); 252 253 const int kOmniboxPopupBorderImages[] = 254 IMAGE_GRID(IDR_OMNIBOX_POPUP_BORDER_AND_SHADOW); 255 const int kOmniboxBorderImages[] = IMAGE_GRID(IDR_TEXTFIELD); 256 border_painter_.reset(views::Painter::CreateImageGridPainter( 257 is_popup_mode_ ? kOmniboxPopupBorderImages : kOmniboxBorderImages)); 258 259 location_icon_view_ = new LocationIconView(this); 260 location_icon_view_->set_drag_controller(this); 261 AddChildView(location_icon_view_); 262 263 // Determine the main font. 264 gfx::FontList font_list = ResourceBundle::GetSharedInstance().GetFontList( 265 ResourceBundle::BaseFont); 266 const int current_font_size = font_list.GetFontSize(); 267 const int desired_font_size = browser_defaults::kOmniboxFontPixelSize; 268 if (current_font_size != desired_font_size) { 269 font_list = 270 font_list.DeriveWithSizeDelta(desired_font_size - current_font_size); 271 } 272 // Shrink large fonts to make them fit. 273 // TODO(pkasting): Stretch the location bar instead in this case. 274 const int location_height = GetInternalHeight(true); 275 font_list = GetLargestFontListWithHeightBound(font_list, location_height); 276 277 // Determine the font for use inside the bubbles. The bubble background 278 // images have 1 px thick edges, which we don't want to overlap. 279 const int kBubbleInteriorVerticalPadding = 1; 280 const int bubble_vertical_padding = 281 (kBubblePadding + kBubbleInteriorVerticalPadding) * 2; 282 const gfx::FontList bubble_font_list( 283 GetLargestFontListWithHeightBound( 284 font_list, location_height - bubble_vertical_padding)); 285 286 const SkColor background_color = 287 GetColor(ToolbarModel::NONE, LocationBarView::BACKGROUND); 288 ev_bubble_view_ = new EVBubbleView( 289 bubble_font_list, GetColor(ToolbarModel::EV_SECURE, SECURITY_TEXT), 290 background_color, this); 291 ev_bubble_view_->set_drag_controller(this); 292 AddChildView(ev_bubble_view_); 293 294 // Initialize the Omnibox view. 295 omnibox_view_ = new OmniboxViewViews( 296 this, profile(), command_updater(), 297 is_popup_mode_ || 298 (browser_->is_app() && CommandLine::ForCurrentProcess()-> 299 HasSwitch(switches::kEnableStreamlinedHostedApps)), 300 this, font_list); 301 omnibox_view_->Init(); 302 omnibox_view_->SetFocusable(true); 303 AddChildView(omnibox_view_); 304 305 // Initialize the inline autocomplete view which is visible only when IME is 306 // turned on. Use the same font with the omnibox and highlighted background. 307 ime_inline_autocomplete_view_ = new views::Label(base::string16(), font_list); 308 ime_inline_autocomplete_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 309 ime_inline_autocomplete_view_->SetAutoColorReadabilityEnabled(false); 310 ime_inline_autocomplete_view_->set_background( 311 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( 312 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); 313 ime_inline_autocomplete_view_->SetEnabledColor( 314 GetNativeTheme()->GetSystemColor( 315 ui::NativeTheme::kColorId_TextfieldSelectionColor)); 316 ime_inline_autocomplete_view_->SetVisible(false); 317 AddChildView(ime_inline_autocomplete_view_); 318 319 animated_host_label_ = new views::Label(base::string16(), font_list); 320 animated_host_label_->SetVisible(false); 321 AddChildView(animated_host_label_); 322 323 origin_chip_view_ = new OriginChipView(this, profile(), font_list); 324 origin_chip_view_->SetFocusable(false); 325 origin_chip_view_->set_drag_controller(this); 326 AddChildView(origin_chip_view_); 327 328 const SkColor text_color = GetColor(ToolbarModel::NONE, TEXT); 329 selected_keyword_view_ = new SelectedKeywordView( 330 bubble_font_list, text_color, background_color, profile()); 331 AddChildView(selected_keyword_view_); 332 333 suggested_text_view_ = new views::Label(base::string16(), font_list); 334 suggested_text_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); 335 suggested_text_view_->SetAutoColorReadabilityEnabled(false); 336 suggested_text_view_->SetEnabledColor(GetColor( 337 ToolbarModel::NONE, LocationBarView::DEEMPHASIZED_TEXT)); 338 suggested_text_view_->SetVisible(false); 339 AddChildView(suggested_text_view_); 340 341 keyword_hint_view_ = new KeywordHintView( 342 profile(), font_list, 343 GetColor(ToolbarModel::NONE, LocationBarView::DEEMPHASIZED_TEXT), 344 background_color); 345 AddChildView(keyword_hint_view_); 346 347 mic_search_view_ = new views::ImageButton(this); 348 mic_search_view_->set_id(VIEW_ID_MIC_SEARCH_BUTTON); 349 mic_search_view_->SetAccessibilityFocusable(true); 350 mic_search_view_->SetTooltipText( 351 l10n_util::GetStringUTF16(IDS_TOOLTIP_MIC_SEARCH)); 352 mic_search_view_->SetImage( 353 views::Button::STATE_NORMAL, 354 ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed( 355 IDR_OMNIBOX_MIC_SEARCH)); 356 mic_search_view_->SetImageAlignment(views::ImageButton::ALIGN_CENTER, 357 views::ImageButton::ALIGN_MIDDLE); 358 mic_search_view_->SetVisible(false); 359 AddChildView(mic_search_view_); 360 361 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { 362 ContentSettingImageView* content_blocked_view = 363 new ContentSettingImageView(static_cast<ContentSettingsType>(i), this, 364 bubble_font_list, text_color, 365 background_color); 366 content_setting_views_.push_back(content_blocked_view); 367 content_blocked_view->SetVisible(false); 368 AddChildView(content_blocked_view); 369 } 370 371 generated_credit_card_view_ = new GeneratedCreditCardView(delegate_); 372 AddChildView(generated_credit_card_view_); 373 374 zoom_view_ = new ZoomView(delegate_); 375 zoom_view_->set_id(VIEW_ID_ZOOM_BUTTON); 376 AddChildView(zoom_view_); 377 378 open_pdf_in_reader_view_ = new OpenPDFInReaderView(); 379 AddChildView(open_pdf_in_reader_view_); 380 381 manage_passwords_icon_view_ = new ManagePasswordsIconView(command_updater()); 382 AddChildView(manage_passwords_icon_view_); 383 384 translate_icon_view_ = new TranslateIconView(command_updater()); 385 translate_icon_view_->SetVisible(false); 386 AddChildView(translate_icon_view_); 387 388 star_view_ = new StarView(command_updater()); 389 star_view_->SetVisible(false); 390 AddChildView(star_view_); 391 392 search_button_ = new views::LabelButton(this, base::string16()); 393 search_button_->set_triggerable_event_flags( 394 ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON); 395 search_button_->SetStyle(views::Button::STYLE_BUTTON); 396 search_button_->SetFocusable(false); 397 search_button_->set_min_size(gfx::Size()); 398 scoped_ptr<views::LabelButtonBorder> search_button_border( 399 new views::LabelButtonBorder(search_button_->style())); 400 search_button_border->set_insets(gfx::Insets()); 401 const int kSearchButtonNormalImages[] = IMAGE_GRID(IDR_OMNIBOX_SEARCH_BUTTON); 402 search_button_border->SetPainter( 403 false, views::Button::STATE_NORMAL, 404 views::Painter::CreateImageGridPainter(kSearchButtonNormalImages)); 405 const int kSearchButtonHoveredImages[] = 406 IMAGE_GRID(IDR_OMNIBOX_SEARCH_BUTTON_HOVER); 407 search_button_border->SetPainter( 408 false, views::Button::STATE_HOVERED, 409 views::Painter::CreateImageGridPainter(kSearchButtonHoveredImages)); 410 const int kSearchButtonPressedImages[] = 411 IMAGE_GRID(IDR_OMNIBOX_SEARCH_BUTTON_PRESSED); 412 search_button_border->SetPainter( 413 false, views::Button::STATE_PRESSED, 414 views::Painter::CreateImageGridPainter(kSearchButtonPressedImages)); 415 search_button_border->SetPainter(false, views::Button::STATE_DISABLED, NULL); 416 search_button_border->SetPainter(true, views::Button::STATE_NORMAL, NULL); 417 search_button_border->SetPainter(true, views::Button::STATE_HOVERED, NULL); 418 search_button_border->SetPainter(true, views::Button::STATE_PRESSED, NULL); 419 search_button_border->SetPainter(true, views::Button::STATE_DISABLED, NULL); 420 search_button_->SetBorder(search_button_border.PassAs<views::Border>()); 421 const int kSearchButtonWidth = 56; 422 search_button_->set_min_size(gfx::Size(kSearchButtonWidth, 0)); 423 search_button_->SetVisible(false); 424 AddChildView(search_button_); 425 426 show_url_animation_.reset(new gfx::SlideAnimation(this)); 427 show_url_animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN); 428 show_url_animation_->SetSlideDuration(200); 429 430 hide_url_animation_.reset(new gfx::SlideAnimation(this)); 431 hide_url_animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN); 432 hide_url_animation_->SetSlideDuration(200); 433 434 content::Source<Profile> profile_source = content::Source<Profile>(profile()); 435 registrar_.Add(this, 436 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, 437 profile_source); 438 registrar_.Add( 439 this, chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED, profile_source); 440 registrar_.Add(this, 441 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, 442 profile_source); 443 444 // Initialize the location entry. We do this to avoid a black flash which is 445 // visible when the location entry has just been initialized. 446 Update(NULL); 447} 448 449bool LocationBarView::IsInitialized() const { 450 return omnibox_view_ != NULL; 451} 452 453SkColor LocationBarView::GetColor(ToolbarModel::SecurityLevel security_level, 454 ColorKind kind) const { 455 const ui::NativeTheme* native_theme = GetNativeTheme(); 456 switch (kind) { 457 case BACKGROUND: 458 return native_theme->GetSystemColor( 459 ui::NativeTheme::kColorId_TextfieldDefaultBackground); 460 461 case TEXT: 462 return native_theme->GetSystemColor( 463 ui::NativeTheme::kColorId_TextfieldDefaultColor); 464 465 case SELECTED_TEXT: 466 return native_theme->GetSystemColor( 467 ui::NativeTheme::kColorId_TextfieldSelectionColor); 468 469 case DEEMPHASIZED_TEXT: 470 return color_utils::AlphaBlend( 471 GetColor(security_level, TEXT), 472 GetColor(security_level, BACKGROUND), 473 128); 474 475 case SECURITY_TEXT: { 476 SkColor color; 477 switch (security_level) { 478 case ToolbarModel::EV_SECURE: 479 case ToolbarModel::SECURE: 480 color = SkColorSetRGB(7, 149, 0); 481 break; 482 483 case ToolbarModel::SECURITY_WARNING: 484 case ToolbarModel::SECURITY_POLICY_WARNING: 485 return GetColor(security_level, DEEMPHASIZED_TEXT); 486 break; 487 488 case ToolbarModel::SECURITY_ERROR: 489 color = SkColorSetRGB(162, 0, 0); 490 break; 491 492 default: 493 NOTREACHED(); 494 return GetColor(security_level, TEXT); 495 } 496 return color_utils::GetReadableColor( 497 color, GetColor(security_level, BACKGROUND)); 498 } 499 500 default: 501 NOTREACHED(); 502 return GetColor(security_level, TEXT); 503 } 504} 505 506void LocationBarView::ZoomChangedForActiveTab(bool can_show_bubble) { 507 DCHECK(zoom_view_); 508 if (RefreshZoomView()) { 509 Layout(); 510 SchedulePaint(); 511 } 512 513 WebContents* web_contents = GetWebContents(); 514 if (can_show_bubble && zoom_view_->visible() && web_contents) 515 ZoomBubbleView::ShowBubble(web_contents, true); 516} 517 518void LocationBarView::SetPreviewEnabledPageAction(ExtensionAction* page_action, 519 bool preview_enabled) { 520 if (is_popup_mode_) 521 return; 522 523 DCHECK(page_action); 524 WebContents* web_contents = GetWebContents(); 525 526 RefreshPageActionViews(); 527 PageActionWithBadgeView* page_action_view = 528 static_cast<PageActionWithBadgeView*>(GetPageActionView(page_action)); 529 DCHECK(page_action_view); 530 if (!page_action_view) 531 return; 532 533 page_action_view->image_view()->set_preview_enabled(preview_enabled); 534 page_action_view->UpdateVisibility(web_contents, GetToolbarModel()->GetURL()); 535 Layout(); 536 SchedulePaint(); 537} 538 539PageActionWithBadgeView* LocationBarView::GetPageActionView( 540 ExtensionAction* page_action) { 541 DCHECK(page_action); 542 for (PageActionViews::const_iterator i(page_action_views_.begin()); 543 i != page_action_views_.end(); ++i) { 544 if ((*i)->image_view()->page_action() == page_action) 545 return *i; 546 } 547 return NULL; 548} 549 550void LocationBarView::SetStarToggled(bool on) { 551 if (star_view_) 552 star_view_->SetToggled(on); 553} 554 555void LocationBarView::SetTranslateIconToggled(bool on) { 556 translate_icon_view_->SetToggled(on); 557} 558 559gfx::Point LocationBarView::GetOmniboxViewOrigin() const { 560 gfx::Point origin(omnibox_view_->bounds().origin()); 561 // If the UI layout is RTL, the coordinate system is not transformed and 562 // therefore we need to adjust the X coordinate so that bubble appears on the 563 // right hand side of the location bar. 564 if (base::i18n::IsRTL()) 565 origin.set_x(width() - origin.x()); 566 views::View::ConvertPointToScreen(this, &origin); 567 return origin; 568} 569 570void LocationBarView::SetImeInlineAutocompletion(const base::string16& text) { 571 ime_inline_autocomplete_view_->SetText(text); 572 ime_inline_autocomplete_view_->SetVisible(!text.empty()); 573} 574 575void LocationBarView::SetGrayTextAutocompletion(const base::string16& text) { 576 if (suggested_text_view_->text() != text) { 577 suggested_text_view_->SetText(text); 578 suggested_text_view_->SetVisible(!text.empty()); 579 Layout(); 580 SchedulePaint(); 581 } 582} 583 584base::string16 LocationBarView::GetGrayTextAutocompletion() const { 585 return HasValidSuggestText() ? suggested_text_view_->text() 586 : base::string16(); 587} 588 589void LocationBarView::SetShowFocusRect(bool show) { 590 show_focus_rect_ = show; 591 SchedulePaint(); 592} 593 594void LocationBarView::SelectAll() { 595 omnibox_view_->SelectAll(true); 596} 597 598gfx::Point LocationBarView::GetLocationBarAnchorPoint() const { 599 // The +1 in the next line creates a 1-px gap between icon and arrow tip. 600 gfx::Point icon_bottom(0, location_icon_view_->GetImageBounds().bottom() - 601 LocationBarView::kIconInternalPadding + 1); 602 gfx::Point icon_center(location_icon_view_->GetImageBounds().CenterPoint()); 603 gfx::Point point(icon_center.x(), icon_bottom.y()); 604 ConvertPointToTarget(location_icon_view_, this, &point); 605 return point; 606} 607 608views::View* LocationBarView::generated_credit_card_view() { 609 return generated_credit_card_view_; 610} 611 612int LocationBarView::GetInternalHeight(bool use_preferred_size) { 613 int total_height = 614 use_preferred_size ? GetPreferredSize().height() : height(); 615 return std::max(total_height - (vertical_edge_thickness() * 2), 0); 616} 617 618void LocationBarView::GetOmniboxPopupPositioningInfo( 619 gfx::Point* top_left_screen_coord, 620 int* popup_width, 621 int* left_margin, 622 int* right_margin) { 623 // Because the popup might appear atop the attached bookmark bar, there won't 624 // necessarily be a client edge separating it from the rest of the toolbar. 625 // Therefore we position the popup high enough so it can draw its own client 626 // edge at the top, in the same place the toolbar would normally draw the 627 // client edge. 628 *top_left_screen_coord = gfx::Point( 629 0, 630 parent()->height() - views::NonClientFrameView::kClientEdgeThickness); 631 views::View::ConvertPointToScreen(parent(), top_left_screen_coord); 632 *popup_width = parent()->width(); 633 634 gfx::Rect location_bar_bounds(bounds()); 635 location_bar_bounds.Inset(kNormalEdgeThickness, 0); 636 *left_margin = location_bar_bounds.x(); 637 *right_margin = *popup_width - location_bar_bounds.right(); 638} 639 640//////////////////////////////////////////////////////////////////////////////// 641// LocationBarView, public LocationBar implementation: 642 643void LocationBarView::FocusLocation(bool select_all) { 644 omnibox_view_->SetFocus(); 645 if (select_all) 646 omnibox_view_->SelectAll(true); 647} 648 649void LocationBarView::Revert() { 650 omnibox_view_->RevertAll(); 651} 652 653OmniboxView* LocationBarView::GetOmniboxView() { 654 return omnibox_view_; 655} 656 657//////////////////////////////////////////////////////////////////////////////// 658// LocationBarView, public views::View implementation: 659 660bool LocationBarView::HasFocus() const { 661 return omnibox_view_->model()->has_focus(); 662} 663 664void LocationBarView::GetAccessibleState(ui::AXViewState* state) { 665 if (!IsInitialized()) 666 return; 667 668 state->role = ui::AX_ROLE_LOCATION_BAR; 669 state->name = l10n_util::GetStringUTF16(IDS_ACCNAME_LOCATION); 670 state->value = omnibox_view_->GetText(); 671 672 base::string16::size_type entry_start; 673 base::string16::size_type entry_end; 674 omnibox_view_->GetSelectionBounds(&entry_start, &entry_end); 675 state->selection_start = entry_start; 676 state->selection_end = entry_end; 677 678 if (is_popup_mode_) { 679 state->AddStateFlag(ui::AX_STATE_READ_ONLY); 680 } else { 681 state->set_value_callback = 682 base::Bind(&LocationBarView::AccessibilitySetValue, 683 weak_ptr_factory_.GetWeakPtr()); 684 } 685} 686 687gfx::Size LocationBarView::GetPreferredSize() const { 688 // Compute minimum height. 689 gfx::Size min_size(border_painter_->GetMinimumSize()); 690 if (!IsInitialized()) 691 return min_size; 692 gfx::Size search_button_min_size(search_button_->GetMinimumSize()); 693 min_size.SetToMax(search_button_min_size); 694 695 // Compute width of omnibox-leading content. 696 const int horizontal_edge_thickness = GetHorizontalEdgeThickness(); 697 int leading_width = horizontal_edge_thickness; 698 // TODO(pkasting): Make the origin chip min width sane, and make the chip 699 // handle being shrunken down more gracefully; then uncomment this. 700 /*if (GetToolbarModel()->ShouldShowOriginChip()) 701 leading_width += origin_chip_view_->GetMinimumSize().width();*/ 702 if (ShouldShowKeywordBubble()) { 703 // The selected keyword view can collapse completely. 704 } else if (ShouldShowEVBubble()) { 705 leading_width += kBubblePadding + 706 ev_bubble_view_->GetMinimumSizeForLabelText( 707 GetToolbarModel()->GetEVCertName()).width(); 708 } else if (!origin_chip_view_->visible()) { 709 leading_width += 710 kItemPadding + location_icon_view_->GetMinimumSize().width(); 711 } 712 leading_width += kItemPadding - GetEditLeadingInternalSpace(); 713 714 // Compute width of omnibox-trailing content. 715 int trailing_width = search_button_->visible() ? 716 (search_button_->GetMinimumSize().width() + kSearchButtonInset) : 717 horizontal_edge_thickness; 718 trailing_width += IncrementalMinimumWidth(star_view_) + 719 IncrementalMinimumWidth(translate_icon_view_) + 720 IncrementalMinimumWidth(open_pdf_in_reader_view_) + 721 IncrementalMinimumWidth(manage_passwords_icon_view_) + 722 IncrementalMinimumWidth(zoom_view_) + 723 IncrementalMinimumWidth(generated_credit_card_view_) + 724 IncrementalMinimumWidth(mic_search_view_) + kItemPadding; 725 for (PageActionViews::const_iterator i(page_action_views_.begin()); 726 i != page_action_views_.end(); ++i) 727 trailing_width += IncrementalMinimumWidth((*i)); 728 for (ContentSettingViews::const_iterator i(content_setting_views_.begin()); 729 i != content_setting_views_.end(); ++i) 730 trailing_width += IncrementalMinimumWidth((*i)); 731 732 min_size.set_width( 733 leading_width + omnibox_view_->GetMinimumSize().width() + trailing_width); 734 return min_size; 735} 736 737void LocationBarView::Layout() { 738 if (!IsInitialized()) 739 return; 740 741 animated_host_label_->SetVisible(false); 742 origin_chip_view_->SetVisible(GetToolbarModel()->ShouldShowOriginChip()); 743 selected_keyword_view_->SetVisible(false); 744 location_icon_view_->SetVisible(false); 745 ev_bubble_view_->SetVisible(false); 746 keyword_hint_view_->SetVisible(false); 747 748 LocationBarLayout leading_decorations( 749 LocationBarLayout::LEFT_EDGE, 750 kItemPadding - GetEditLeadingInternalSpace()); 751 LocationBarLayout trailing_decorations(LocationBarLayout::RIGHT_EDGE, 752 kItemPadding); 753 754 // Show and position the animated host label used in the show and hide URL 755 // animations. 756 if (show_url_animation_->is_animating() || 757 hide_url_animation_->is_animating()) { 758 WebContents* web_contents = GetWebContents(); 759 const GURL url = web_contents ? web_contents->GetURL() : GURL(); 760 const base::string16 host = 761 OriginChip::LabelFromURLForProfile(url, profile()); 762 animated_host_label_->SetText(host); 763 764 const base::string16 formatted_url = GetToolbarModel()->GetFormattedURL(); 765 766 // Split the formatted URL on the host name in order to determine the size 767 // of the text leading up to it. 768 std::vector<base::string16> substrings; 769 base::SplitStringUsingSubstr(formatted_url, host, &substrings); 770 const base::string16 text_leading_host = substrings[0]; 771 const int leading_text_width = 772 gfx::Canvas::GetStringWidth(text_leading_host, 773 origin_chip_view_->GetFontList()); 774 775 const int position_of_host_name_in_chip = origin_chip_view_->host_label_x(); 776 const int position_of_host_name_in_url = 777 position_of_host_name_in_chip + leading_text_width; 778 779 int host_label_x = position_of_host_name_in_chip; 780 if (show_url_animation_->is_animating()) { 781 host_label_x = show_url_animation_-> 782 CurrentValueBetween(position_of_host_name_in_chip, 783 position_of_host_name_in_url); 784 } else if (hide_url_animation_->is_animating()) { 785 host_label_x = hide_url_animation_-> 786 CurrentValueBetween(position_of_host_name_in_url, 787 position_of_host_name_in_chip); 788 } 789 animated_host_label_->SetBounds( 790 host_label_x, 0, 791 animated_host_label_->GetPreferredSize().width(), height()); 792 animated_host_label_->SetVisible(true); 793 } 794 795 const int origin_chip_width = origin_chip_view_->visible() ? 796 origin_chip_view_->GetPreferredSize().width() : 0; 797 origin_chip_view_->SetBounds(0, 0, origin_chip_width, height()); 798 799 const int bubble_location_y = vertical_edge_thickness() + kBubblePadding; 800 const base::string16 keyword(omnibox_view_->model()->keyword()); 801 // In some cases (e.g. fullscreen mode) we may have 0 height. We still want 802 // to position our child views in this case, because other things may be 803 // positioned relative to them (e.g. the "bookmark added" bubble if the user 804 // hits ctrl-d). 805 const int location_height = GetInternalHeight(false); 806 const int bubble_height = std::max(location_height - (kBubblePadding * 2), 0); 807 if (ShouldShowKeywordBubble()) { 808 leading_decorations.AddDecoration(bubble_location_y, bubble_height, true, 0, 809 kBubblePadding, kItemPadding, 810 selected_keyword_view_); 811 if (selected_keyword_view_->keyword() != keyword) { 812 selected_keyword_view_->SetKeyword(keyword); 813 const TemplateURL* template_url = 814 TemplateURLServiceFactory::GetForProfile(profile())-> 815 GetTemplateURLForKeyword(keyword); 816 if (template_url && 817 (template_url->GetType() == TemplateURL::OMNIBOX_API_EXTENSION)) { 818 gfx::Image image = extensions::OmniboxAPI::Get(profile())-> 819 GetOmniboxIcon(template_url->GetExtensionId()); 820 selected_keyword_view_->SetImage(image.AsImageSkia()); 821 selected_keyword_view_->set_is_extension_icon(true); 822 } else { 823 selected_keyword_view_->SetImage( 824 *(GetThemeProvider()->GetImageSkiaNamed(IDR_OMNIBOX_SEARCH))); 825 selected_keyword_view_->set_is_extension_icon(false); 826 } 827 } 828 } else if (ShouldShowEVBubble()) { 829 ev_bubble_view_->SetLabel(GetToolbarModel()->GetEVCertName()); 830 // The largest fraction of the omnibox that can be taken by the EV bubble. 831 const double kMaxBubbleFraction = 0.5; 832 leading_decorations.AddDecoration(bubble_location_y, bubble_height, false, 833 kMaxBubbleFraction, kBubblePadding, 834 kItemPadding, ev_bubble_view_); 835 } else if (!origin_chip_view_->visible()) { 836 leading_decorations.AddDecoration( 837 vertical_edge_thickness(), location_height, 838 location_icon_view_); 839 } 840 841 if (star_view_->visible()) { 842 trailing_decorations.AddDecoration( 843 vertical_edge_thickness(), location_height, star_view_); 844 } 845 if (translate_icon_view_->visible()) { 846 trailing_decorations.AddDecoration( 847 vertical_edge_thickness(), location_height, translate_icon_view_); 848 } 849 if (open_pdf_in_reader_view_->visible()) { 850 trailing_decorations.AddDecoration( 851 vertical_edge_thickness(), location_height, open_pdf_in_reader_view_); 852 } 853 if (manage_passwords_icon_view_->visible()) { 854 trailing_decorations.AddDecoration(vertical_edge_thickness(), 855 location_height, 856 manage_passwords_icon_view_); 857 } 858 for (PageActionViews::const_iterator i(page_action_views_.begin()); 859 i != page_action_views_.end(); ++i) { 860 if ((*i)->visible()) { 861 trailing_decorations.AddDecoration( 862 vertical_edge_thickness(), location_height, (*i)); 863 } 864 } 865 if (zoom_view_->visible()) { 866 trailing_decorations.AddDecoration(vertical_edge_thickness(), 867 location_height, zoom_view_); 868 } 869 for (ContentSettingViews::const_reverse_iterator i( 870 content_setting_views_.rbegin()); i != content_setting_views_.rend(); 871 ++i) { 872 if ((*i)->visible()) { 873 trailing_decorations.AddDecoration( 874 bubble_location_y, bubble_height, false, 0, kItemPadding, 875 kItemPadding, (*i)); 876 } 877 } 878 if (generated_credit_card_view_->visible()) { 879 trailing_decorations.AddDecoration(vertical_edge_thickness(), 880 location_height, 881 generated_credit_card_view_); 882 } 883 if (mic_search_view_->visible()) { 884 trailing_decorations.AddDecoration(vertical_edge_thickness(), 885 location_height, mic_search_view_); 886 } 887 // Because IMEs may eat the tab key, we don't show "press tab to search" while 888 // IME composition is in progress. 889 if (!keyword.empty() && omnibox_view_->model()->is_keyword_hint() && 890 !omnibox_view_->IsImeComposing()) { 891 trailing_decorations.AddDecoration(vertical_edge_thickness(), 892 location_height, true, 0, kItemPadding, 893 kItemPadding, keyword_hint_view_); 894 if (keyword_hint_view_->keyword() != keyword) 895 keyword_hint_view_->SetKeyword(keyword); 896 } 897 898 // Perform layout. 899 const int horizontal_edge_thickness = GetHorizontalEdgeThickness(); 900 int full_width = width() - horizontal_edge_thickness - origin_chip_width; 901 902 const gfx::Size search_button_size(search_button_->GetPreferredSize()); 903 const int search_button_reserved_width = 904 search_button_size.width() + kSearchButtonInset; 905 full_width -= search_button_->visible() ? 906 search_button_reserved_width : horizontal_edge_thickness; 907 int entry_width = full_width; 908 leading_decorations.LayoutPass1(&entry_width); 909 trailing_decorations.LayoutPass1(&entry_width); 910 leading_decorations.LayoutPass2(&entry_width); 911 trailing_decorations.LayoutPass2(&entry_width); 912 913 int location_needed_width = omnibox_view_->GetTextWidth(); 914 int available_width = entry_width - location_needed_width; 915 // The bounds must be wide enough for all the decorations to fit. 916 gfx::Rect location_bounds( 917 origin_chip_width + horizontal_edge_thickness, vertical_edge_thickness(), 918 std::max(full_width, full_width - entry_width), location_height); 919 leading_decorations.LayoutPass3(&location_bounds, &available_width); 920 trailing_decorations.LayoutPass3(&location_bounds, &available_width); 921 922 // Layout out the suggested text view right aligned to the location 923 // entry. Only show the suggested text if we can fit the text from one 924 // character before the end of the selection to the end of the text and the 925 // suggested text. If we can't it means either the suggested text is too big, 926 // or the user has scrolled. 927 928 // TODO(sky): We could potentially adjust this to take into account suggested 929 // text to force using minimum size if necessary, but currently the chance of 930 // showing keyword hints and suggested text is minimal and we're not confident 931 // this is the right approach for suggested text. 932 933 int omnibox_view_margin = 0; 934 if (suggested_text_view_->visible()) { 935 // We do not display the suggested text when it contains a mix of RTL and 936 // LTR characters since this could mean the suggestion should be displayed 937 // in the middle of the string. 938 base::i18n::TextDirection text_direction = 939 base::i18n::GetStringDirection(omnibox_view_->GetText()); 940 if (text_direction != 941 base::i18n::GetStringDirection(suggested_text_view_->text())) 942 text_direction = base::i18n::UNKNOWN_DIRECTION; 943 944 // TODO(sky): need to layout when the user changes caret position. 945 gfx::Size suggested_text_size(suggested_text_view_->GetPreferredSize()); 946 if (suggested_text_size.width() > available_width || 947 text_direction == base::i18n::UNKNOWN_DIRECTION) { 948 // Hide the suggested text if the user has scrolled or we can't fit all 949 // the suggested text, or we have a mix of RTL and LTR characters. 950 suggested_text_view_->SetBounds(0, 0, 0, 0); 951 } else { 952 location_needed_width = 953 std::min(location_needed_width, 954 location_bounds.width() - suggested_text_size.width()); 955 gfx::Rect suggested_text_bounds(location_bounds.x(), location_bounds.y(), 956 suggested_text_size.width(), 957 location_bounds.height()); 958 // TODO(sky): figure out why this needs the -1. 959 suggested_text_bounds.Offset(location_needed_width - 1, 0); 960 961 // We reverse the order of the location entry and suggested text if: 962 // - Chrome is RTL but the text is fully LTR, or 963 // - Chrome is LTR but the text is fully RTL. 964 // This ensures the suggested text is correctly displayed to the right 965 // (or left) of the user text. 966 if (text_direction == (base::i18n::IsRTL() ? 967 base::i18n::LEFT_TO_RIGHT : base::i18n::RIGHT_TO_LEFT)) { 968 // TODO(sky): Figure out why we need the +1. 969 suggested_text_bounds.set_x(location_bounds.x() + 1); 970 // Use a margin to prevent omnibox text from overlapping suggest text. 971 omnibox_view_margin = suggested_text_bounds.width(); 972 } 973 suggested_text_view_->SetBoundsRect(suggested_text_bounds); 974 } 975 } 976 977 const gfx::Insets insets = omnibox_view_->GetInsets(); 978 omnibox_view_->SetBorder(views::Border::CreateEmptyBorder( 979 insets.top(), insets.left(), insets.bottom(), omnibox_view_margin)); 980 981 // Layout |ime_inline_autocomplete_view_| next to the user input. 982 if (ime_inline_autocomplete_view_->visible()) { 983 int width = 984 gfx::GetStringWidth(ime_inline_autocomplete_view_->text(), 985 ime_inline_autocomplete_view_->font_list()) + 986 ime_inline_autocomplete_view_->GetInsets().width(); 987 // All the target languages (IMEs) are LTR, and we do not need to support 988 // RTL so far. In other words, no testable RTL environment so far. 989 int x = location_needed_width; 990 if (width > entry_width) 991 x = 0; 992 else if (location_needed_width + width > entry_width) 993 x = entry_width - width; 994 location_bounds.set_width(x); 995 ime_inline_autocomplete_view_->SetBounds( 996 location_bounds.right(), location_bounds.y(), 997 std::min(width, entry_width), location_bounds.height()); 998 } 999 1000 omnibox_view_->SetBoundsRect(location_bounds); 1001 1002 search_button_->SetBoundsRect(gfx::Rect( 1003 gfx::Point(width() - search_button_reserved_width, 0), 1004 search_button_size)); 1005} 1006 1007//////////////////////////////////////////////////////////////////////////////// 1008// LocationBarView, public OmniboxEditController implementation: 1009 1010void LocationBarView::Update(const WebContents* contents) { 1011 mic_search_view_->SetVisible( 1012 !GetToolbarModel()->input_in_progress() && browser_ && 1013 browser_->search_model()->voice_search_supported()); 1014 RefreshContentSettingViews(); 1015 generated_credit_card_view_->Update(); 1016 ZoomBubbleView::CloseBubble(); 1017 RefreshZoomView(); 1018 RefreshPageActionViews(); 1019 RefreshTranslateIcon(); 1020 RefreshManagePasswordsIconView(); 1021 open_pdf_in_reader_view_->Update( 1022 GetToolbarModel()->input_in_progress() ? NULL : GetWebContents()); 1023 1024 if (star_view_) { 1025 star_view_->SetVisible( 1026 browser_defaults::bookmarks_enabled && !is_popup_mode_ && 1027 !GetToolbarModel()->input_in_progress() && 1028 edit_bookmarks_enabled_.GetValue() && 1029 !IsBookmarkStarHiddenByExtension()); 1030 } 1031 1032 if (contents) 1033 omnibox_view_->OnTabChanged(contents); 1034 else 1035 omnibox_view_->Update(); 1036 1037 OnChanged(); // NOTE: Calls Layout(). 1038} 1039 1040void LocationBarView::ShowURL() { 1041 if (chrome::ShouldDisplayOriginChip()) { 1042 omnibox_view_->SetVisible(false); 1043 omnibox_view_->ShowURL(); 1044 show_url_animation_->Show(); 1045 } else { 1046 omnibox_view_->ShowURL(); 1047 } 1048} 1049 1050ToolbarModel* LocationBarView::GetToolbarModel() { 1051 return delegate_->GetToolbarModel(); 1052} 1053 1054WebContents* LocationBarView::GetWebContents() { 1055 return delegate_->GetWebContents(); 1056} 1057 1058//////////////////////////////////////////////////////////////////////////////// 1059// LocationBarView, private: 1060 1061// static 1062int LocationBarView::IncrementalMinimumWidth(views::View* view) { 1063 return view->visible() ? (kItemPadding + view->GetMinimumSize().width()) : 0; 1064} 1065 1066int LocationBarView::GetHorizontalEdgeThickness() const { 1067 // In maximized popup mode, there isn't any edge. 1068 return (is_popup_mode_ && browser_ && browser_->window() && 1069 browser_->window()->IsMaximized()) ? 0 : vertical_edge_thickness(); 1070} 1071 1072bool LocationBarView::RefreshContentSettingViews() { 1073 bool visibility_changed = false; 1074 for (ContentSettingViews::const_iterator i(content_setting_views_.begin()); 1075 i != content_setting_views_.end(); ++i) { 1076 const bool was_visible = (*i)->visible(); 1077 (*i)->Update(GetToolbarModel()->input_in_progress() ? 1078 NULL : GetWebContents()); 1079 if (was_visible != (*i)->visible()) 1080 visibility_changed = true; 1081 } 1082 return visibility_changed; 1083} 1084 1085void LocationBarView::DeletePageActionViews() { 1086 for (PageActionViews::const_iterator i(page_action_views_.begin()); 1087 i != page_action_views_.end(); ++i) 1088 RemoveChildView(*i); 1089 STLDeleteElements(&page_action_views_); 1090} 1091 1092bool LocationBarView::RefreshPageActionViews() { 1093 if (is_popup_mode_) 1094 return false; 1095 1096 bool changed = false; 1097 1098 // Remember the previous visibility of the page actions so that we can 1099 // notify when this changes. 1100 std::map<ExtensionAction*, bool> old_visibility; 1101 for (PageActionViews::const_iterator i(page_action_views_.begin()); 1102 i != page_action_views_.end(); ++i) { 1103 old_visibility[(*i)->image_view()->page_action()] = (*i)->visible(); 1104 } 1105 1106 PageActions new_page_actions; 1107 1108 WebContents* web_contents = GetWebContents(); 1109 if (web_contents) { 1110 extensions::TabHelper* extensions_tab_helper = 1111 extensions::TabHelper::FromWebContents(web_contents); 1112 extensions::LocationBarController* controller = 1113 extensions_tab_helper->location_bar_controller(); 1114 new_page_actions = controller->GetCurrentActions(); 1115 } 1116 1117 // On startup we sometimes haven't loaded any extensions. This makes sure 1118 // we catch up when the extensions (and any page actions) load. 1119 if (page_actions_ != new_page_actions) { 1120 changed = true; 1121 1122 page_actions_.swap(new_page_actions); 1123 DeletePageActionViews(); // Delete the old views (if any). 1124 1125 // Create the page action views. 1126 for (PageActions::const_iterator i = page_actions_.begin(); 1127 i != page_actions_.end(); ++i) { 1128 PageActionWithBadgeView* page_action_view = new PageActionWithBadgeView( 1129 delegate_->CreatePageActionImageView(this, *i)); 1130 page_action_view->SetVisible(false); 1131 page_action_views_.push_back(page_action_view); 1132 } 1133 1134 // Move rightmost extensions to the start. 1135 std::stable_partition( 1136 page_action_views_.begin(), 1137 page_action_views_.end(), 1138 IsPageActionViewRightAligned( 1139 extensions::ExtensionSystem::Get(profile())->extension_service())); 1140 1141 View* right_anchor = open_pdf_in_reader_view_; 1142 if (!right_anchor) 1143 right_anchor = star_view_; 1144 DCHECK(right_anchor); 1145 1146 // |page_action_views_| are ordered right-to-left. Add them as children in 1147 // reverse order so the logical order and visual order match for 1148 // accessibility purposes. 1149 for (PageActionViews::reverse_iterator i = page_action_views_.rbegin(); 1150 i != page_action_views_.rend(); ++i) 1151 AddChildViewAt(*i, GetIndexOf(right_anchor)); 1152 } 1153 1154 if (!page_action_views_.empty() && web_contents) { 1155 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); 1156 GURL url = browser->tab_strip_model()->GetActiveWebContents()->GetURL(); 1157 1158 for (PageActionViews::const_iterator i(page_action_views_.begin()); 1159 i != page_action_views_.end(); ++i) { 1160 (*i)->UpdateVisibility( 1161 GetToolbarModel()->input_in_progress() ? NULL : web_contents, url); 1162 1163 // Check if the visibility of the action changed and notify if it did. 1164 ExtensionAction* action = (*i)->image_view()->page_action(); 1165 if (old_visibility.find(action) == old_visibility.end() || 1166 old_visibility[action] != (*i)->visible()) { 1167 changed = true; 1168 content::NotificationService::current()->Notify( 1169 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, 1170 content::Source<ExtensionAction>(action), 1171 content::Details<WebContents>(web_contents)); 1172 } 1173 } 1174 } 1175 return changed; 1176} 1177 1178bool LocationBarView::RefreshZoomView() { 1179 DCHECK(zoom_view_); 1180 WebContents* web_contents = GetWebContents(); 1181 if (!web_contents) 1182 return false; 1183 const bool was_visible = zoom_view_->visible(); 1184 zoom_view_->Update(ZoomController::FromWebContents(web_contents)); 1185 return was_visible != zoom_view_->visible(); 1186} 1187 1188void LocationBarView::RefreshTranslateIcon() { 1189 if (!TranslateService::IsTranslateBubbleEnabled()) 1190 return; 1191 1192 WebContents* web_contents = GetWebContents(); 1193 if (!web_contents) 1194 return; 1195 LanguageState& language_state = TranslateTabHelper::FromWebContents( 1196 web_contents)->GetLanguageState(); 1197 bool enabled = language_state.translate_enabled(); 1198 command_updater()->UpdateCommandEnabled(IDC_TRANSLATE_PAGE, enabled); 1199 translate_icon_view_->SetVisible(enabled); 1200 translate_icon_view_->SetToggled(language_state.IsPageTranslated()); 1201} 1202 1203bool LocationBarView::RefreshManagePasswordsIconView() { 1204 DCHECK(manage_passwords_icon_view_); 1205 WebContents* web_contents = GetWebContents(); 1206 if (!web_contents) 1207 return false; 1208 const bool was_visible = manage_passwords_icon_view_->visible(); 1209 ManagePasswordsUIController::FromWebContents( 1210 web_contents)->UpdateIconAndBubbleState(manage_passwords_icon_view_); 1211 return was_visible != manage_passwords_icon_view_->visible(); 1212} 1213 1214void LocationBarView::ShowFirstRunBubbleInternal() { 1215 // First run bubble doesn't make sense for Chrome OS. 1216#if !defined(OS_CHROMEOS) 1217 WebContents* web_contents = delegate_->GetWebContents(); 1218 if (!web_contents) 1219 return; 1220 Browser* browser = chrome::FindBrowserWithWebContents(web_contents); 1221 if (browser) 1222 FirstRunBubble::ShowBubble(browser, location_icon_view_); 1223#endif 1224} 1225 1226void LocationBarView::AccessibilitySetValue(const base::string16& new_value) { 1227 omnibox_view_->SetUserText(new_value, new_value, true); 1228} 1229 1230bool LocationBarView::HasValidSuggestText() const { 1231 return suggested_text_view_->visible() && 1232 !suggested_text_view_->size().IsEmpty(); 1233} 1234 1235bool LocationBarView::ShouldShowKeywordBubble() const { 1236 return !omnibox_view_->model()->keyword().empty() && 1237 !omnibox_view_->model()->is_keyword_hint(); 1238} 1239 1240bool LocationBarView::ShouldShowEVBubble() const { 1241 return !chrome::ShouldDisplayOriginChip() && 1242 (GetToolbarModel()->GetSecurityLevel(false) == ToolbarModel::EV_SECURE); 1243} 1244 1245void LocationBarView::OnShowURLAnimationEnded() { 1246 animated_host_label_->SetVisible(false); 1247 omnibox_view_->SetVisible(true); 1248 omnibox_view_->FadeIn(); 1249 omnibox_view_->SetFocus(); 1250 1251 // Sometimes the selection established by OmniboxView::ShowURL() is lost at 1252 // the call to SetFocus() above. Select all again to be sure. 1253 // TODO(jdonnelly): Figure out why the selection is sometimes lost and 1254 // implement a more principled fix. 1255 omnibox_view_->SelectAll(true); 1256} 1257 1258void LocationBarView::OnHideURLAnimationEnded() { 1259 animated_host_label_->SetVisible(false); 1260 omnibox_view_->HideURL(); 1261 omnibox_view_->SetVisible(true); 1262 origin_chip_view_->FadeIn(); 1263} 1264 1265//////////////////////////////////////////////////////////////////////////////// 1266// LocationBarView, private LocationBar implementation: 1267 1268void LocationBarView::ShowFirstRunBubble() { 1269 // Wait until search engines have loaded to show the first run bubble. 1270 TemplateURLService* url_service = 1271 TemplateURLServiceFactory::GetForProfile(profile()); 1272 if (!url_service->loaded()) { 1273 template_url_service_ = url_service; 1274 template_url_service_->AddObserver(this); 1275 template_url_service_->Load(); 1276 return; 1277 } 1278 ShowFirstRunBubbleInternal(); 1279} 1280 1281GURL LocationBarView::GetDestinationURL() const { 1282 return destination_url(); 1283} 1284 1285WindowOpenDisposition LocationBarView::GetWindowOpenDisposition() const { 1286 return disposition(); 1287} 1288 1289content::PageTransition LocationBarView::GetPageTransition() const { 1290 return transition(); 1291} 1292 1293void LocationBarView::AcceptInput() { 1294 omnibox_view_->model()->AcceptInput(CURRENT_TAB, false); 1295} 1296 1297void LocationBarView::FocusSearch() { 1298 omnibox_view_->SetFocus(); 1299 omnibox_view_->SetForcedQuery(); 1300} 1301 1302void LocationBarView::UpdateContentSettingsIcons() { 1303 if (RefreshContentSettingViews()) { 1304 Layout(); 1305 SchedulePaint(); 1306 } 1307} 1308 1309void LocationBarView::UpdateManagePasswordsIconAndBubble() { 1310 if (RefreshManagePasswordsIconView()) { 1311 Layout(); 1312 SchedulePaint(); 1313 } 1314} 1315 1316void LocationBarView::UpdatePageActions() { 1317 size_t count_before = page_action_views_.size(); 1318 bool changed = RefreshPageActionViews(); 1319 if (page_action_views_.size() != count_before) { 1320 content::NotificationService::current()->Notify( 1321 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, 1322 content::Source<LocationBar>(this), 1323 content::NotificationService::NoDetails()); 1324 } 1325 1326 if (changed) { 1327 Layout(); 1328 SchedulePaint(); 1329 } 1330} 1331 1332void LocationBarView::InvalidatePageActions() { 1333 size_t count_before = page_action_views_.size(); 1334 DeletePageActionViews(); 1335 if (page_action_views_.size() != count_before) { 1336 content::NotificationService::current()->Notify( 1337 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, 1338 content::Source<LocationBar>(this), 1339 content::NotificationService::NoDetails()); 1340 } 1341} 1342 1343void LocationBarView::UpdateOpenPDFInReaderPrompt() { 1344 open_pdf_in_reader_view_->Update( 1345 GetToolbarModel()->input_in_progress() ? NULL : GetWebContents()); 1346 Layout(); 1347 SchedulePaint(); 1348} 1349 1350void LocationBarView::UpdateGeneratedCreditCardView() { 1351 generated_credit_card_view_->Update(); 1352 Layout(); 1353 SchedulePaint(); 1354} 1355 1356void LocationBarView::SaveStateToContents(WebContents* contents) { 1357 omnibox_view_->SaveStateToTab(contents); 1358} 1359 1360const OmniboxView* LocationBarView::GetOmniboxView() const { 1361 return omnibox_view_; 1362} 1363 1364LocationBarTesting* LocationBarView::GetLocationBarForTesting() { 1365 return this; 1366} 1367 1368//////////////////////////////////////////////////////////////////////////////// 1369// LocationBarView, private LocationBarTesting implementation: 1370 1371int LocationBarView::PageActionCount() { 1372 return page_action_views_.size(); 1373} 1374 1375int LocationBarView::PageActionVisibleCount() { 1376 int result = 0; 1377 for (size_t i = 0; i < page_action_views_.size(); i++) { 1378 if (page_action_views_[i]->visible()) 1379 ++result; 1380 } 1381 return result; 1382} 1383 1384ExtensionAction* LocationBarView::GetPageAction(size_t index) { 1385 if (index < page_action_views_.size()) 1386 return page_action_views_[index]->image_view()->page_action(); 1387 1388 NOTREACHED(); 1389 return NULL; 1390} 1391 1392ExtensionAction* LocationBarView::GetVisiblePageAction(size_t index) { 1393 size_t current = 0; 1394 for (size_t i = 0; i < page_action_views_.size(); ++i) { 1395 if (page_action_views_[i]->visible()) { 1396 if (current == index) 1397 return page_action_views_[i]->image_view()->page_action(); 1398 1399 ++current; 1400 } 1401 } 1402 1403 NOTREACHED(); 1404 return NULL; 1405} 1406 1407void LocationBarView::TestPageActionPressed(size_t index) { 1408 size_t current = 0; 1409 for (size_t i = 0; i < page_action_views_.size(); ++i) { 1410 if (page_action_views_[i]->visible()) { 1411 if (current == index) { 1412 page_action_views_[i]->image_view()->ExecuteAction( 1413 ExtensionPopup::SHOW); 1414 return; 1415 } 1416 ++current; 1417 } 1418 } 1419 1420 NOTREACHED(); 1421} 1422 1423bool LocationBarView::GetBookmarkStarVisibility() { 1424 DCHECK(star_view_); 1425 return star_view_->visible(); 1426} 1427 1428//////////////////////////////////////////////////////////////////////////////// 1429// LocationBarView, private views::View implementation: 1430 1431const char* LocationBarView::GetClassName() const { 1432 return kViewClassName; 1433} 1434 1435void LocationBarView::OnBoundsChanged(const gfx::Rect& previous_bounds) { 1436 InstantServiceFactory::GetForProfile(profile())->OnOmniboxStartMarginChanged( 1437 bounds().x()); 1438 1439 OmniboxPopupView* popup = omnibox_view_->model()->popup_model()->view(); 1440 if (popup->IsOpen()) 1441 popup->UpdatePopupAppearance(); 1442} 1443 1444void LocationBarView::OnPaint(gfx::Canvas* canvas) { 1445 View::OnPaint(canvas); 1446 1447 // Fill the location bar background color behind the border. Parts of the 1448 // border images are meant to rest atop the toolbar background and parts atop 1449 // the omnibox background, so we can't just blindly fill our entire bounds. 1450 gfx::Rect bounds(GetContentsBounds()); 1451 bounds.Inset(GetHorizontalEdgeThickness(), vertical_edge_thickness()); 1452 SkColor color(GetColor(ToolbarModel::NONE, BACKGROUND)); 1453 if (is_popup_mode_) { 1454 canvas->FillRect(bounds, color); 1455 } else { 1456 SkPaint paint; 1457 paint.setStyle(SkPaint::kFill_Style); 1458 paint.setColor(color); 1459 const int kBorderCornerRadius = 2; 1460 canvas->DrawRoundRect(bounds, kBorderCornerRadius, paint); 1461 } 1462 1463 // The border itself will be drawn in PaintChildren() since it includes an 1464 // inner shadow which should be drawn over the contents. 1465} 1466 1467void LocationBarView::PaintChildren(gfx::Canvas* canvas, 1468 const views::CullSet& cull_set) { 1469 // Paint all the children except for the origin chip and the search button, 1470 // which will be painted after the border. 1471 for (int i = 0, count = child_count(); i < count; ++i) { 1472 if (!child_at(i)->layer() && 1473 (child_at(i) != origin_chip_view_) && 1474 (child_at(i) != search_button_)) 1475 child_at(i)->Paint(canvas, cull_set); 1476 } 1477 1478 // For non-InstantExtendedAPI cases, if necessary, show focus rect. As we need 1479 // the focus rect to appear on top of children we paint here rather than 1480 // OnPaint(). 1481 // Note: |Canvas::DrawFocusRect| paints a dashed rect with gray color. 1482 if (show_focus_rect_ && HasFocus()) 1483 canvas->DrawFocusRect(omnibox_view_->bounds()); 1484 1485 // Maximized popup windows don't draw the horizontal edges. We implement this 1486 // by simply expanding the paint area outside the view by the edge thickness. 1487 gfx::Rect border_rect(GetContentsBounds()); 1488 if (is_popup_mode_ && (GetHorizontalEdgeThickness() == 0)) 1489 border_rect.Inset(-kPopupEdgeThickness, 0); 1490 views::Painter::PaintPainterAt(canvas, border_painter_.get(), border_rect); 1491 1492 // The origin chip and the search button must be painted after the border so 1493 // that the border shadow is not drawn over them. 1494 origin_chip_view_->Paint(canvas, cull_set); 1495 search_button_->Paint(canvas, cull_set); 1496} 1497 1498//////////////////////////////////////////////////////////////////////////////// 1499// LocationBarView, private views::ButtonListener implementation: 1500 1501void LocationBarView::ButtonPressed(views::Button* sender, 1502 const ui::Event& event) { 1503 if (sender == mic_search_view_) { 1504 command_updater()->ExecuteCommand(IDC_TOGGLE_SPEECH_INPUT); 1505 return; 1506 } 1507 1508 DCHECK_EQ(search_button_, sender); 1509 // TODO(pkasting): When macourteau adds UMA stats for this, wire them up here. 1510 omnibox_view_->model()->AcceptInput( 1511 ui::DispositionFromEventFlags(event.flags()), false); 1512} 1513 1514//////////////////////////////////////////////////////////////////////////////// 1515// LocationBarView, private views::DragController implementation: 1516 1517void LocationBarView::WriteDragDataForView(views::View* sender, 1518 const gfx::Point& press_pt, 1519 OSExchangeData* data) { 1520 DCHECK_NE(GetDragOperationsForView(sender, press_pt), 1521 ui::DragDropTypes::DRAG_NONE); 1522 1523 WebContents* web_contents = GetWebContents(); 1524 FaviconTabHelper* favicon_tab_helper = 1525 FaviconTabHelper::FromWebContents(web_contents); 1526 gfx::ImageSkia favicon = favicon_tab_helper->GetFavicon().AsImageSkia(); 1527 button_drag_utils::SetURLAndDragImage(web_contents->GetURL(), 1528 web_contents->GetTitle(), 1529 favicon, 1530 data, 1531 sender->GetWidget()); 1532} 1533 1534int LocationBarView::GetDragOperationsForView(views::View* sender, 1535 const gfx::Point& p) { 1536 DCHECK((sender == location_icon_view_) || (sender == ev_bubble_view_) || 1537 (sender == origin_chip_view_)); 1538 WebContents* web_contents = delegate_->GetWebContents(); 1539 return (web_contents && web_contents->GetURL().is_valid() && 1540 (!GetOmniboxView()->IsEditingOrEmpty() || 1541 sender == origin_chip_view_)) ? 1542 (ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK) : 1543 ui::DragDropTypes::DRAG_NONE; 1544} 1545 1546bool LocationBarView::CanStartDragForView(View* sender, 1547 const gfx::Point& press_pt, 1548 const gfx::Point& p) { 1549 return true; 1550} 1551 1552//////////////////////////////////////////////////////////////////////////////// 1553// LocationBarView, private OmniboxEditController implementation: 1554 1555void LocationBarView::OnChanged() { 1556 int icon_id = omnibox_view_->GetIcon(); 1557 location_icon_view_->SetImage(GetThemeProvider()->GetImageSkiaNamed(icon_id)); 1558 location_icon_view_->ShowTooltip(!GetOmniboxView()->IsEditingOrEmpty()); 1559 1560 ToolbarModel* toolbar_model = GetToolbarModel(); 1561 chrome::DisplaySearchButtonConditions conditions = 1562 chrome::GetDisplaySearchButtonConditions(); 1563 bool meets_conditions = 1564 (conditions == chrome::DISPLAY_SEARCH_BUTTON_ALWAYS) || 1565 ((conditions != chrome::DISPLAY_SEARCH_BUTTON_NEVER) && 1566 (toolbar_model->WouldPerformSearchTermReplacement(true) || 1567 ((conditions == chrome::DISPLAY_SEARCH_BUTTON_FOR_STR_OR_IIP) && 1568 toolbar_model->input_in_progress()))); 1569 search_button_->SetVisible(!is_popup_mode_ && meets_conditions); 1570 search_button_->SetImage( 1571 views::Button::STATE_NORMAL, 1572 *GetThemeProvider()->GetImageSkiaNamed((icon_id == IDR_OMNIBOX_SEARCH) ? 1573 IDR_OMNIBOX_SEARCH_BUTTON_LOUPE : IDR_OMNIBOX_SEARCH_BUTTON_ARROW)); 1574 1575 if (origin_chip_view_->visible()) 1576 origin_chip_view_->OnChanged(); 1577 1578 Layout(); 1579 SchedulePaint(); 1580} 1581 1582void LocationBarView::OnSetFocus() { 1583 GetFocusManager()->SetFocusedView(this); 1584} 1585 1586InstantController* LocationBarView::GetInstant() { 1587 return delegate_->GetInstant(); 1588} 1589 1590const ToolbarModel* LocationBarView::GetToolbarModel() const { 1591 return delegate_->GetToolbarModel(); 1592} 1593 1594void LocationBarView::HideURL() { 1595 omnibox_view_->SetVisible(false); 1596 hide_url_animation_->Show(); 1597} 1598 1599//////////////////////////////////////////////////////////////////////////////// 1600// LocationBarView, private DropdownBarHostDelegate implementation: 1601 1602void LocationBarView::SetFocusAndSelection(bool select_all) { 1603 FocusLocation(select_all); 1604} 1605 1606void LocationBarView::SetAnimationOffset(int offset) { 1607 dropdown_animation_offset_ = offset; 1608} 1609 1610//////////////////////////////////////////////////////////////////////////////// 1611// LocationBarView, private gfx::AnimationDelegate implementation: 1612 1613void LocationBarView::AnimationProgressed(const gfx::Animation* animation) { 1614 if (animation == show_url_animation_.get() || 1615 animation == hide_url_animation_.get()) { 1616 Layout(); 1617 SchedulePaint(); 1618 } 1619} 1620 1621void LocationBarView::AnimationEnded(const gfx::Animation* animation) { 1622 if (animation == show_url_animation_.get()) { 1623 show_url_animation_->Reset(); 1624 OnShowURLAnimationEnded(); 1625 } else if (animation == hide_url_animation_.get()) { 1626 hide_url_animation_->Reset(); 1627 OnHideURLAnimationEnded(); 1628 } 1629} 1630 1631//////////////////////////////////////////////////////////////////////////////// 1632// LocationBarView, private TemplateURLServiceObserver implementation: 1633 1634void LocationBarView::OnTemplateURLServiceChanged() { 1635 template_url_service_->RemoveObserver(this); 1636 template_url_service_ = NULL; 1637 // If the browser is no longer active, let's not show the info bubble, as this 1638 // would make the browser the active window again. 1639 if (omnibox_view_ && omnibox_view_->GetWidget()->IsActive()) 1640 ShowFirstRunBubble(); 1641} 1642 1643//////////////////////////////////////////////////////////////////////////////// 1644// LocationBarView, private content::NotificationObserver implementation: 1645 1646void LocationBarView::Observe(int type, 1647 const content::NotificationSource& source, 1648 const content::NotificationDetails& details) { 1649 switch (type) { 1650 case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: { 1651 // Only update if the updated action box was for the active tab contents. 1652 if (content::Details<WebContents>(details).ptr() == GetWebContents()) 1653 UpdatePageActions(); 1654 break; 1655 } 1656 1657 case chrome::NOTIFICATION_EXTENSION_LOADED_DEPRECATED: 1658 case chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED: 1659 Update(NULL); 1660 break; 1661 1662 default: 1663 NOTREACHED() << "Unexpected notification."; 1664 } 1665} 1666 1667//////////////////////////////////////////////////////////////////////////////// 1668// LocationBarView, private SearchModelObserver implementation: 1669 1670void LocationBarView::ModelChanged(const SearchModel::State& old_state, 1671 const SearchModel::State& new_state) { 1672 const bool visible = !GetToolbarModel()->input_in_progress() && 1673 new_state.voice_search_supported; 1674 if (mic_search_view_->visible() != visible) { 1675 mic_search_view_->SetVisible(visible); 1676 Layout(); 1677 } 1678} 1679 1680