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