1// Copyright 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/gtk/location_bar_view_gtk.h" 6 7#include <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/basictypes.h" 12#include "base/bind.h" 13#include "base/command_line.h" 14#include "base/debug/trace_event.h" 15#include "base/i18n/rtl.h" 16#include "base/logging.h" 17#include "base/message_loop/message_loop.h" 18#include "base/prefs/pref_service.h" 19#include "base/strings/string_number_conversions.h" 20#include "base/strings/string_util.h" 21#include "base/strings/utf_string_conversions.h" 22#include "chrome/app/chrome_command_ids.h" 23#include "chrome/browser/accessibility/accessibility_events.h" 24#include "chrome/browser/chrome_notification_types.h" 25#include "chrome/browser/command_updater.h" 26#include "chrome/browser/content_settings/tab_specific_content_settings.h" 27#include "chrome/browser/defaults.h" 28#include "chrome/browser/extensions/api/commands/command_service.h" 29#include "chrome/browser/extensions/api/omnibox/omnibox_api.h" 30#include "chrome/browser/extensions/extension_action.h" 31#include "chrome/browser/extensions/extension_service.h" 32#include "chrome/browser/extensions/extension_tab_util.h" 33#include "chrome/browser/extensions/location_bar_controller.h" 34#include "chrome/browser/extensions/script_bubble_controller.h" 35#include "chrome/browser/extensions/tab_helper.h" 36#include "chrome/browser/favicon/favicon_tab_helper.h" 37#include "chrome/browser/profiles/profile.h" 38#include "chrome/browser/search_engines/template_url.h" 39#include "chrome/browser/search_engines/template_url_service.h" 40#include "chrome/browser/search_engines/template_url_service_factory.h" 41#include "chrome/browser/themes/theme_properties.h" 42#include "chrome/browser/ui/browser.h" 43#include "chrome/browser/ui/browser_command_controller.h" 44#include "chrome/browser/ui/browser_commands.h" 45#include "chrome/browser/ui/browser_content_setting_bubble_model_delegate.h" 46#include "chrome/browser/ui/browser_instant_controller.h" 47#include "chrome/browser/ui/browser_list.h" 48#include "chrome/browser/ui/content_settings/content_setting_bubble_model.h" 49#include "chrome/browser/ui/content_settings/content_setting_image_model.h" 50#include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h" 51#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h" 52#include "chrome/browser/ui/gtk/browser_window_gtk.h" 53#include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h" 54#include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h" 55#include "chrome/browser/ui/gtk/first_run_bubble.h" 56#include "chrome/browser/ui/gtk/gtk_theme_service.h" 57#include "chrome/browser/ui/gtk/gtk_util.h" 58#include "chrome/browser/ui/gtk/nine_box.h" 59#include "chrome/browser/ui/gtk/omnibox/omnibox_view_gtk.h" 60#include "chrome/browser/ui/gtk/rounded_window.h" 61#include "chrome/browser/ui/gtk/script_bubble_gtk.h" 62#include "chrome/browser/ui/gtk/view_id_util.h" 63#include "chrome/browser/ui/gtk/zoom_bubble_gtk.h" 64#include "chrome/browser/ui/omnibox/alternate_nav_url_fetcher.h" 65#include "chrome/browser/ui/omnibox/location_bar_util.h" 66#include "chrome/browser/ui/omnibox/omnibox_edit_model.h" 67#include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 68#include "chrome/browser/ui/tabs/tab_strip_model.h" 69#include "chrome/browser/ui/webui/extensions/extension_info_ui.h" 70#include "chrome/browser/ui/zoom/zoom_controller.h" 71#include "chrome/common/badge_util.h" 72#include "chrome/common/chrome_switches.h" 73#include "chrome/common/extensions/extension.h" 74#include "chrome/common/extensions/extension_manifest_constants.h" 75#include "chrome/common/extensions/feature_switch.h" 76#include "chrome/common/extensions/manifest_handlers/icons_handler.h" 77#include "chrome/common/pref_names.h" 78#include "content/public/browser/navigation_entry.h" 79#include "content/public/browser/notification_service.h" 80#include "content/public/browser/web_contents.h" 81#include "grit/generated_resources.h" 82#include "grit/theme_resources.h" 83#include "net/base/net_util.h" 84#include "ui/base/accelerators/platform_accelerator_gtk.h" 85#include "ui/base/dragdrop/gtk_dnd_util.h" 86#include "ui/base/gtk/gtk_hig_constants.h" 87#include "ui/base/gtk/gtk_signal_registrar.h" 88#include "ui/base/l10n/l10n_util.h" 89#include "ui/base/resource/resource_bundle.h" 90#include "ui/base/window_open_disposition.h" 91#include "ui/gfx/canvas_skia_paint.h" 92#include "ui/gfx/font.h" 93#include "ui/gfx/gtk_util.h" 94#include "ui/gfx/image/image.h" 95 96using content::NavigationEntry; 97using content::OpenURLParams; 98using content::WebContents; 99using extensions::LocationBarController; 100using extensions::Extension; 101 102namespace { 103 104// We are positioned with a little bit of extra space that we don't use now. 105const int kTopMargin = 1; 106const int kBottomMargin = 1; 107// We draw a border on the top and bottom (but not on left or right). 108const int kBorderThickness = 1; 109 110const int kPopupEdgeThickness = 1; 111const int kNormalEdgeThickness = 2; 112 113// Spacing needed to align the bubble with the left side of the omnibox. 114const int kFirstRunBubbleLeftSpacing = 4; 115 116// The padding around the top, bottom, and sides of the location bar hbox. 117// We don't want to edit control's text to be right against the edge, 118// as well the tab to search box and other widgets need to have the padding on 119// top and bottom to avoid drawing larger than the location bar space. 120const int kHboxBorder = 2; 121 122// Padding between the elements in the bar. 123const int kInnerPadding = 2; 124const int kScriptBadgeInnerPadding = 9; 125 126// Colors used to draw the EV certificate rounded bubble. 127const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00); 128const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef); 129const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90); 130 131// Colors used to draw the Tab to Search rounded bubble. 132const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa); 133const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7); 134 135// Use weak gray for showing search and keyword hint text. 136const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75); 137 138// Size of the rounding of the "Search site for:" box. 139const int kCornerSize = 3; 140 141// Default page tool animation time (open and close). In ms. 142const int kPageToolAnimationTime = 150; 143 144// The time, in ms, that the content setting label is fully displayed, for the 145// cases where we animate it into and out of view. 146const int kContentSettingImageDisplayTime = 3200; 147// The time, in ms, of the animation (open and close). 148const int kContentSettingImageAnimationTime = 150; 149 150// Color of border of content setting area (icon/label). 151const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66); 152// Colors for the background gradient. 153const GdkColor kContentSettingTopColor = GDK_COLOR_RGB(0xff, 0xf8, 0xd4); 154const GdkColor kContentSettingBottomColor = GDK_COLOR_RGB(0xff, 0xe6, 0xaf); 155 156// Styling for gray button. 157const GdkColor kGrayBorderColor = GDK_COLOR_RGB(0xa0, 0xa0, 0xa0); 158const GdkColor kTopColorGray = GDK_COLOR_RGB(0xe5, 0xe5, 0xe5); 159const GdkColor kBottomColorGray = GDK_COLOR_RGB(0xd0, 0xd0, 0xd0); 160 161inline int InnerPadding() { 162 return extensions::FeatureSwitch::script_badges()->IsEnabled() ? 163 kScriptBadgeInnerPadding : kInnerPadding; 164} 165 166// If widget is visible, increment the int pointed to by count. 167// Suitible for use with gtk_container_foreach. 168void CountVisibleWidgets(GtkWidget* widget, gpointer count) { 169 if (gtk_widget_get_visible(widget)) 170 *static_cast<int*>(count) += 1; 171} 172 173class ContentSettingImageViewGtk : public LocationBarViewGtk::PageToolViewGtk, 174 public BubbleDelegateGtk { 175 public: 176 ContentSettingImageViewGtk(ContentSettingsType content_type, 177 const LocationBarViewGtk* parent); 178 virtual ~ContentSettingImageViewGtk(); 179 180 // PageToolViewGtk 181 virtual void Update(WebContents* web_contents) OVERRIDE; 182 183 // ui::AnimationDelegate 184 virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE; 185 186 private: 187 // PageToolViewGtk 188 virtual GdkColor button_border_color() const OVERRIDE; 189 virtual GdkColor gradient_top_color() const OVERRIDE; 190 virtual GdkColor gradient_bottom_color() const OVERRIDE; 191 virtual void OnClick(GtkWidget* sender) OVERRIDE; 192 193 // BubbleDelegateGtk 194 virtual void BubbleClosing(BubbleGtk* bubble, 195 bool closed_by_escape) OVERRIDE; 196 197 scoped_ptr<ContentSettingImageModel> content_setting_image_model_; 198 199 // The currently shown bubble if any. 200 ContentSettingBubbleGtk* content_setting_bubble_; 201 202 DISALLOW_COPY_AND_ASSIGN(ContentSettingImageViewGtk); 203}; 204 205ContentSettingImageViewGtk::ContentSettingImageViewGtk( 206 ContentSettingsType content_type, 207 const LocationBarViewGtk* parent) 208 : PageToolViewGtk(parent), 209 content_setting_image_model_( 210 ContentSettingImageModel::CreateContentSettingImageModel( 211 content_type)), 212 content_setting_bubble_(NULL) { 213 animation_.SetSlideDuration(kContentSettingImageAnimationTime); 214} 215 216ContentSettingImageViewGtk::~ContentSettingImageViewGtk() { 217 if (content_setting_bubble_) 218 content_setting_bubble_->Close(); 219} 220 221void ContentSettingImageViewGtk::Update(WebContents* web_contents) { 222 if (web_contents) 223 content_setting_image_model_->UpdateFromWebContents(web_contents); 224 225 if (!content_setting_image_model_->is_visible()) { 226 gtk_widget_hide(widget()); 227 return; 228 } 229 230 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), 231 GtkThemeService::GetFrom(parent_->browser()->profile())->GetImageNamed( 232 content_setting_image_model_->get_icon()).ToGdkPixbuf()); 233 234 gtk_widget_set_tooltip_text(widget(), 235 content_setting_image_model_->get_tooltip().c_str()); 236 gtk_widget_show_all(widget()); 237 238 if (!web_contents) 239 return; 240 241 TabSpecificContentSettings* content_settings = 242 TabSpecificContentSettings::FromWebContents(web_contents); 243 if (!content_settings || content_settings->IsBlockageIndicated( 244 content_setting_image_model_->get_content_settings_type())) 245 return; 246 247 // The content blockage was not yet indicated to the user. Start indication 248 // animation and clear "not yet shown" flag. 249 content_settings->SetBlockageHasBeenIndicated( 250 content_setting_image_model_->get_content_settings_type()); 251 252 int label_string_id = 253 content_setting_image_model_->explanatory_string_id(); 254 // If there's no string for the content type, we don't animate. 255 if (!label_string_id) 256 return; 257 258 gtk_label_set_text(GTK_LABEL(label_.get()), 259 l10n_util::GetStringUTF8(label_string_id).c_str()); 260 StartAnimating(); 261} 262 263void ContentSettingImageViewGtk::AnimationEnded( 264 const ui::Animation* animation) { 265 if (animation_.IsShowing()) { 266 base::MessageLoop::current()->PostDelayedTask( 267 FROM_HERE, 268 base::Bind(&ContentSettingImageViewGtk::CloseAnimation, 269 weak_factory_.GetWeakPtr()), 270 base::TimeDelta::FromMilliseconds(kContentSettingImageDisplayTime)); 271 } else { 272 gtk_widget_hide(label_.get()); 273 gtk_util::StopActingAsRoundedWindow(event_box_.get()); 274 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); 275 } 276} 277 278GdkColor ContentSettingImageViewGtk:: 279 button_border_color() const { 280 return kContentSettingBorderColor; 281} 282 283GdkColor ContentSettingImageViewGtk:: 284 gradient_top_color() const { 285 return kContentSettingTopColor; 286} 287 288GdkColor ContentSettingImageViewGtk:: 289 gradient_bottom_color() const { 290 return kContentSettingBottomColor; 291} 292 293void ContentSettingImageViewGtk::OnClick( 294 GtkWidget* sender) { 295 WebContents* web_contents = parent_->GetWebContents(); 296 if (!web_contents) 297 return; 298 Profile* profile = parent_->browser()->profile(); 299 content_setting_bubble_ = new ContentSettingBubbleGtk( 300 sender, this, 301 ContentSettingBubbleModel::CreateContentSettingBubbleModel( 302 parent_->browser()->content_setting_bubble_model_delegate(), 303 web_contents, 304 profile, 305 content_setting_image_model_->get_content_settings_type()), 306 profile, web_contents); 307 return; 308} 309 310void ContentSettingImageViewGtk::BubbleClosing( 311 BubbleGtk* bubble, 312 bool closed_by_escape) { 313 content_setting_bubble_ = NULL; 314} 315 316gfx::Rect AllocationToRect(const GtkAllocation& allocation) { 317 return gfx::Rect(allocation.x, allocation.y, 318 allocation.width, allocation.height); 319} 320 321} // namespace 322 323//////////////////////////////////////////////////////////////////////////////// 324// LocationBarViewGtk 325 326// static 327const GdkColor LocationBarViewGtk::kBackgroundColor = 328 GDK_COLOR_RGB(255, 255, 255); 329 330LocationBarViewGtk::LocationBarViewGtk(Browser* browser) 331 : zoom_image_(NULL), 332 script_bubble_button_image_(NULL), 333 num_running_scripts_(0u), 334 star_image_(NULL), 335 starred_(false), 336 star_sized_(false), 337 site_type_alignment_(NULL), 338 site_type_event_box_(NULL), 339 location_icon_image_(NULL), 340 drag_icon_(NULL), 341 enable_location_drag_(false), 342 security_info_label_(NULL), 343 tab_to_search_alignment_(NULL), 344 tab_to_search_box_(NULL), 345 tab_to_search_full_label_(NULL), 346 tab_to_search_partial_label_(NULL), 347 tab_to_search_hint_(NULL), 348 tab_to_search_hint_leading_label_(NULL), 349 tab_to_search_hint_icon_(NULL), 350 tab_to_search_hint_trailing_label_(NULL), 351 command_updater_(browser->command_controller()->command_updater()), 352 toolbar_model_(browser->toolbar_model()), 353 browser_(browser), 354 disposition_(CURRENT_TAB), 355 transition_(content::PageTransitionFromInt( 356 content::PAGE_TRANSITION_TYPED | 357 content::PAGE_TRANSITION_FROM_ADDRESS_BAR)), 358 weak_ptr_factory_(this), 359 popup_window_mode_(false), 360 theme_service_(NULL), 361 hbox_width_(0), 362 entry_box_width_(0), 363 show_selected_keyword_(false), 364 show_keyword_hint_(false) { 365} 366 367LocationBarViewGtk::~LocationBarViewGtk() { 368 // All of our widgets should be children of / owned by the alignment. 369 zoom_.Destroy(); 370 script_bubble_button_.Destroy(); 371 star_.Destroy(); 372 hbox_.Destroy(); 373 content_setting_hbox_.Destroy(); 374 page_action_hbox_.Destroy(); 375} 376 377void LocationBarViewGtk::Init(bool popup_window_mode) { 378 popup_window_mode_ = popup_window_mode; 379 380 Profile* profile = browser_->profile(); 381 theme_service_ = GtkThemeService::GetFrom(profile); 382 383 // Create the widget first, so we can pass it to the OmniboxViewGtk. 384 hbox_.Own(gtk_hbox_new(FALSE, InnerPadding())); 385 gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder); 386 // We will paint for the alignment, to paint the background and border. 387 gtk_widget_set_app_paintable(hbox_.get(), TRUE); 388 // Redraw the whole location bar when it changes size (e.g., when toggling 389 // the home button on/off. 390 gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE); 391 392 // Now initialize the OmniboxViewGtk. 393 location_entry_.reset(new OmniboxViewGtk(this, toolbar_model_, browser_, 394 browser_->profile(), command_updater_, popup_window_mode_, hbox_.get())); 395 location_entry_->Init(); 396 397 g_signal_connect(hbox_.get(), "expose-event", 398 G_CALLBACK(&HandleExposeThunk), this); 399 400 BuildSiteTypeArea(); 401 402 // Put |tab_to_search_box_|, |location_entry_|, and |tab_to_search_hint_| into 403 // a sub hbox, so that we can make this part horizontally shrinkable without 404 // affecting other elements in the location bar. 405 entry_box_ = gtk_hbox_new(FALSE, InnerPadding()); 406 gtk_widget_show(entry_box_); 407 gtk_widget_set_size_request(entry_box_, 0, -1); 408 gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0); 409 410 // We need to adjust the visibility of the search hint widgets according to 411 // the horizontal space in the |entry_box_|. 412 g_signal_connect(entry_box_, "size-allocate", 413 G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this); 414 415 // Tab to search (the keyword box on the left hand side). 416 tab_to_search_full_label_ = 417 theme_service_->BuildLabel(std::string(), ui::kGdkBlack); 418 tab_to_search_partial_label_ = 419 theme_service_->BuildLabel(std::string(), ui::kGdkBlack); 420 GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0); 421 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox), 422 tab_to_search_full_label_, FALSE, FALSE, 0); 423 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox), 424 tab_to_search_partial_label_, FALSE, FALSE, 0); 425 GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0); 426 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 427 tab_to_search_magnifier_ = gtk_image_new_from_pixbuf( 428 rb.GetNativeImageNamed(IDR_KEYWORD_SEARCH_MAGNIFIER).ToGdkPixbuf()); 429 gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_, 430 FALSE, FALSE, 0); 431 gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox, 432 false, 0); 433 434 // This creates a box around the keyword text with a border, background color, 435 // and padding around the text. 436 tab_to_search_box_ = gtk_util::CreateGtkBorderBin( 437 tab_to_search_hbox, NULL, 1, 1, 1, 3); 438 gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box"); 439 gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor, 440 kCornerSize, 441 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL); 442 443 // Put the event box in an alignment to get the padding correct. 444 tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1); 445 gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_), 446 tab_to_search_box_); 447 gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_, 448 FALSE, FALSE, 0); 449 450 // Show all children widgets of |tab_to_search_box_| initially, except 451 // |tab_to_search_partial_label_|. 452 gtk_widget_show_all(tab_to_search_box_); 453 gtk_widget_hide(tab_to_search_partial_label_); 454 455 location_entry_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); 456 gtk_container_add(GTK_CONTAINER(location_entry_alignment_), 457 location_entry_->GetNativeView()); 458 gtk_box_pack_start(GTK_BOX(entry_box_), location_entry_alignment_, 459 TRUE, TRUE, 0); 460 461 // Tab to search notification (the hint on the right hand side). 462 tab_to_search_hint_ = gtk_hbox_new(FALSE, 0); 463 gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint"); 464 tab_to_search_hint_leading_label_ = 465 theme_service_->BuildLabel(std::string(), kHintTextColor); 466 gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE); 467 tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf( 468 rb.GetNativeImageNamed(IDR_OMNIBOX_KEYWORD_HINT_TAB).ToGdkPixbuf()); 469 tab_to_search_hint_trailing_label_ = 470 theme_service_->BuildLabel(std::string(), kHintTextColor); 471 gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE); 472 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_), 473 tab_to_search_hint_leading_label_, 474 FALSE, FALSE, 0); 475 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_), 476 tab_to_search_hint_icon_, 477 FALSE, FALSE, 0); 478 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_), 479 tab_to_search_hint_trailing_label_, 480 FALSE, FALSE, 0); 481 // Show all children widgets of |tab_to_search_hint_| initially. 482 gtk_widget_show_all(tab_to_search_hint_); 483 gtk_widget_hide(tab_to_search_hint_); 484 // tab_to_search_hint_ gets hidden initially in OnChanged. Hiding it here 485 // doesn't work, someone is probably calling show_all on our parent box. 486 gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0); 487 488 if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) { 489 // Hide the star icon in popups, app windows, etc. 490 CreateStarButton(); 491 gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0); 492 } 493 494 CreateScriptBubbleButton(); 495 gtk_box_pack_end(GTK_BOX(hbox_.get()), script_bubble_button_.get(), FALSE, 496 FALSE, 0); 497 498 CreateZoomButton(); 499 gtk_box_pack_end(GTK_BOX(hbox_.get()), zoom_.get(), FALSE, FALSE, 0); 500 501 content_setting_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding() + 1)); 502 gtk_widget_set_name(content_setting_hbox_.get(), 503 "chrome-content-setting-hbox"); 504 gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(), 505 FALSE, FALSE, 1); 506 507 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { 508 ContentSettingImageViewGtk* content_setting_view = 509 new ContentSettingImageViewGtk( 510 static_cast<ContentSettingsType>(i), this); 511 content_setting_views_.push_back(content_setting_view); 512 gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()), 513 content_setting_view->widget(), FALSE, FALSE, 0); 514 } 515 516 page_action_hbox_.Own(gtk_hbox_new(FALSE, InnerPadding())); 517 gtk_widget_set_name(page_action_hbox_.get(), 518 "chrome-page-action-hbox"); 519 gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(), 520 FALSE, FALSE, 0); 521 522 // Now that we've created the widget hierarchy, connect to the main |hbox_|'s 523 // size-allocate so we can do proper resizing and eliding on 524 // |security_info_label_|. 525 g_signal_connect(hbox_.get(), "size-allocate", 526 G_CALLBACK(&OnHboxSizeAllocateThunk), this); 527 528 registrar_.Add(this, 529 chrome::NOTIFICATION_BROWSER_THEME_CHANGED, 530 content::Source<ThemeService>(theme_service_)); 531 registrar_.Add(this, 532 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, 533 content::Source<Profile>(browser()->profile())); 534 edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled, 535 profile->GetPrefs(), 536 base::Bind(&LocationBarViewGtk::UpdateStarIcon, 537 base::Unretained(this))); 538 539 theme_service_->InitThemesFor(this); 540} 541 542void LocationBarViewGtk::BuildSiteTypeArea() { 543 location_icon_image_ = gtk_image_new(); 544 gtk_widget_set_name(location_icon_image_, "chrome-location-icon"); 545 546 GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1); 547 gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0); 548 gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_); 549 gtk_widget_show_all(icon_alignment); 550 551 security_info_label_ = gtk_label_new(NULL); 552 gtk_label_set_ellipsize(GTK_LABEL(security_info_label_), 553 PANGO_ELLIPSIZE_MIDDLE); 554 gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL, 555 &kEvSecureTextColor); 556 gtk_widget_set_name(security_info_label_, 557 "chrome-location-bar-security-info-label"); 558 559 GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1); 560 gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment, 561 FALSE, FALSE, 0); 562 gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_, 563 FALSE, FALSE, 2); 564 565 site_type_event_box_ = gtk_event_box_new(); 566 gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL, 567 &kEvSecureBackgroundColor); 568 g_signal_connect(site_type_event_box_, "drag-data-get", 569 G_CALLBACK(&OnIconDragDataThunk), this); 570 g_signal_connect(site_type_event_box_, "drag-begin", 571 G_CALLBACK(&OnIconDragBeginThunk), this); 572 g_signal_connect(site_type_event_box_, "drag-end", 573 G_CALLBACK(&OnIconDragEndThunk), this); 574 575 // Make the event box not visible so it does not paint a background. 576 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_), 577 FALSE); 578 gtk_widget_set_name(site_type_event_box_, 579 "chrome-location-icon-eventbox"); 580 gtk_container_add(GTK_CONTAINER(site_type_event_box_), 581 site_type_hbox); 582 583 // Put the event box in an alignment to get the padding correct. 584 site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1); 585 gtk_container_add(GTK_CONTAINER(site_type_alignment_), 586 site_type_event_box_); 587 gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_, 588 FALSE, FALSE, 0); 589 590 gtk_widget_set_tooltip_text(location_icon_image_, 591 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str()); 592 593 g_signal_connect(site_type_event_box_, "button-release-event", 594 G_CALLBACK(&OnIconReleasedThunk), this); 595} 596 597void LocationBarViewGtk::SetSiteTypeDragSource() { 598 bool enable = !GetLocationEntry()->IsEditingOrEmpty(); 599 if (enable_location_drag_ == enable) 600 return; 601 enable_location_drag_ = enable; 602 603 if (!enable) { 604 gtk_drag_source_unset(site_type_event_box_); 605 return; 606 } 607 608 gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK, 609 NULL, 0, GDK_ACTION_COPY); 610 ui::SetSourceTargetListFromCodeMask(site_type_event_box_, 611 ui::TEXT_PLAIN | 612 ui::TEXT_URI_LIST | 613 ui::CHROME_NAMED_URL); 614} 615 616WebContents* LocationBarViewGtk::GetWebContents() const { 617 return browser_->tab_strip_model()->GetActiveWebContents(); 618} 619 620void LocationBarViewGtk::SetPreviewEnabledPageAction( 621 ExtensionAction* page_action, 622 bool preview_enabled) { 623 DCHECK(page_action); 624 for (ScopedVector<PageActionViewGtk>::iterator iter = 625 page_action_views_.begin(); iter != page_action_views_.end(); 626 ++iter) { 627 if ((*iter)->page_action() == page_action) { 628 (*iter)->set_preview_enabled(preview_enabled); 629 UpdatePageActions(); 630 return; 631 } 632 } 633} 634 635GtkWidget* LocationBarViewGtk::GetPageActionWidget( 636 ExtensionAction* page_action) { 637 DCHECK(page_action); 638 for (ScopedVector<PageActionViewGtk>::iterator iter = 639 page_action_views_.begin(); 640 iter != page_action_views_.end(); 641 ++iter) { 642 if ((*iter)->page_action() == page_action) 643 return (*iter)->widget(); 644 } 645 return NULL; 646} 647 648void LocationBarViewGtk::Update(const WebContents* contents) { 649 UpdateZoomIcon(); 650 UpdateScriptBubbleIcon(); 651 UpdateStarIcon(); 652 UpdateSiteTypeArea(); 653 UpdateContentSettingsIcons(); 654 UpdatePageActions(); 655 location_entry_->Update(contents); 656 // The security level (background color) could have changed, etc. 657 if (theme_service_->UsingNativeTheme()) { 658 // In GTK mode, we need our parent to redraw, as it draws the text entry 659 // border. 660 gtk_widget_queue_draw(gtk_widget_get_parent(widget())); 661 } else { 662 gtk_widget_queue_draw(widget()); 663 } 664 ZoomBubbleGtk::CloseBubble(); 665} 666 667void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url, 668 WindowOpenDisposition disposition, 669 content::PageTransition transition, 670 const GURL& alternate_nav_url) { 671 if (url.is_valid()) { 672 location_input_ = UTF8ToUTF16(url.spec()); 673 disposition_ = disposition; 674 transition_ = content::PageTransitionFromInt( 675 transition | content::PAGE_TRANSITION_FROM_ADDRESS_BAR); 676 677 if (command_updater_) { 678 if (!alternate_nav_url.is_valid()) { 679 command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); 680 } else { 681 AlternateNavURLFetcher* fetcher = 682 new AlternateNavURLFetcher(alternate_nav_url); 683 // The AlternateNavURLFetcher will listen for the pending navigation 684 // notification that will be issued as a result of the "open URL." It 685 // will automatically install itself into that navigation controller. 686 command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); 687 if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { 688 // I'm not sure this should be reachable, but I'm not also sure enough 689 // that it shouldn't to stick in a NOTREACHED(). In any case, this is 690 // harmless. 691 delete fetcher; 692 } else { 693 // The navigation controller will delete the fetcher. 694 } 695 } 696 } 697 } 698} 699 700void LocationBarViewGtk::OnChanged() { 701 UpdateSiteTypeArea(); 702 703 const string16 keyword(location_entry_->model()->keyword()); 704 const bool is_keyword_hint = location_entry_->model()->is_keyword_hint(); 705 show_selected_keyword_ = !keyword.empty() && !is_keyword_hint; 706 show_keyword_hint_ = !keyword.empty() && is_keyword_hint; 707 708 if (show_selected_keyword_) 709 SetKeywordLabel(keyword); 710 711 if (show_keyword_hint_) 712 SetKeywordHintLabel(keyword); 713 714 AdjustChildrenVisibility(); 715} 716 717void LocationBarViewGtk::OnSelectionBoundsChanged() { 718 NOTIMPLEMENTED(); 719} 720 721GtkWidget* LocationBarViewGtk::CreateIconButton( 722 GtkWidget** image, 723 int image_id, 724 ViewID debug_id, 725 int tooltip_id, 726 gboolean (click_callback)(GtkWidget*, GdkEventButton*, gpointer)) { 727 *image = image_id ? 728 gtk_image_new_from_pixbuf( 729 theme_service_->GetImageNamed(image_id).ToGdkPixbuf()) : 730 gtk_image_new(); 731 732 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1); 733 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 734 0, InnerPadding()); 735 gtk_container_add(GTK_CONTAINER(alignment), *image); 736 737 GtkWidget* result = gtk_event_box_new(); 738 gtk_event_box_set_visible_window(GTK_EVENT_BOX(result), FALSE); 739 gtk_container_add(GTK_CONTAINER(result), alignment); 740 gtk_widget_show_all(result); 741 742 if (debug_id != VIEW_ID_NONE) 743 ViewIDUtil::SetID(result, debug_id); 744 745 if (tooltip_id) { 746 gtk_widget_set_tooltip_text(result, 747 l10n_util::GetStringUTF8(tooltip_id).c_str()); 748 } 749 750 g_signal_connect(result, "button-press-event", 751 G_CALLBACK(click_callback), this); 752 753 return result; 754} 755 756void LocationBarViewGtk::CreateZoomButton() { 757 zoom_.Own(CreateIconButton(&zoom_image_, 758 0, 759 VIEW_ID_ZOOM_BUTTON, 760 0, 761 OnZoomButtonPressThunk)); 762} 763 764void LocationBarViewGtk::CreateScriptBubbleButton() { 765 script_bubble_button_.Own(CreateIconButton(&script_bubble_button_image_, 766 0, 767 VIEW_ID_SCRIPT_BUBBLE, 768 IDS_TOOLTIP_SCRIPT_BUBBLE, 769 OnScriptBubbleButtonPressThunk)); 770 gtk_image_set_from_pixbuf( 771 GTK_IMAGE(script_bubble_button_image_), 772 theme_service_->GetImageNamed( 773 IDR_EXTENSIONS_SCRIPT_BUBBLE).ToGdkPixbuf()); 774 g_signal_connect_after(script_bubble_button_image_, "expose-event", 775 G_CALLBACK(&OnScriptBubbleButtonExposeThunk), this); 776} 777 778void LocationBarViewGtk::CreateStarButton() { 779 star_.Own(CreateIconButton(&star_image_, 780 0, 781 VIEW_ID_STAR_BUTTON, 782 IDS_TOOLTIP_STAR, 783 OnStarButtonPressThunk)); 784 // We need to track when the star button is resized to show any bubble 785 // attached to it at this time. 786 g_signal_connect(star_image_, "size-allocate", 787 G_CALLBACK(&OnStarButtonSizeAllocateThunk), this); 788} 789 790void LocationBarViewGtk::OnInputInProgress(bool in_progress) { 791 // This is identical to the Windows code, except that we don't proxy the call 792 // back through the Toolbar, and just access the model here. 793 // The edit should make sure we're only notified when something changes. 794 DCHECK(toolbar_model_->GetInputInProgress() != in_progress); 795 796 toolbar_model_->SetInputInProgress(in_progress); 797 Update(NULL); 798} 799 800void LocationBarViewGtk::OnKillFocus() { 801} 802 803void LocationBarViewGtk::OnSetFocus() { 804 Profile* profile = browser_->profile(); 805 AccessibilityTextBoxInfo info( 806 profile, 807 l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION), 808 std::string(), 809 false); 810 content::NotificationService::current()->Notify( 811 chrome::NOTIFICATION_ACCESSIBILITY_CONTROL_FOCUSED, 812 content::Source<Profile>(profile), 813 content::Details<AccessibilityTextBoxInfo>(&info)); 814 815 // Update the keyword and search hint states. 816 OnChanged(); 817} 818 819gfx::Image LocationBarViewGtk::GetFavicon() const { 820 return FaviconTabHelper::FromWebContents(GetWebContents())->GetFavicon(); 821} 822 823string16 LocationBarViewGtk::GetTitle() const { 824 return GetWebContents()->GetTitle(); 825} 826 827InstantController* LocationBarViewGtk::GetInstant() { 828 return browser_->instant_controller() ? 829 browser_->instant_controller()->instant() : NULL; 830} 831 832void LocationBarViewGtk::ShowFirstRunBubble() { 833 // We need the browser window to be shown before we can show the bubble, but 834 // we get called before that's happened. 835 base::MessageLoop::current()->PostTask( 836 FROM_HERE, 837 base::Bind(&LocationBarViewGtk::ShowFirstRunBubbleInternal, 838 weak_ptr_factory_.GetWeakPtr())); 839} 840 841string16 LocationBarViewGtk::GetInputString() const { 842 return location_input_; 843} 844 845WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const { 846 return disposition_; 847} 848 849content::PageTransition LocationBarViewGtk::GetPageTransition() const { 850 return transition_; 851} 852 853void LocationBarViewGtk::AcceptInput() { 854 location_entry_->model()->AcceptInput(CURRENT_TAB, false); 855} 856 857void LocationBarViewGtk::FocusLocation(bool select_all) { 858 location_entry_->SetFocus(); 859 if (select_all) 860 location_entry_->SelectAll(true); 861} 862 863void LocationBarViewGtk::FocusSearch() { 864 location_entry_->SetFocus(); 865 location_entry_->SetForcedQuery(); 866} 867 868void LocationBarViewGtk::UpdateContentSettingsIcons() { 869 bool any_visible = false; 870 for (ScopedVector<PageToolViewGtk>::iterator i( 871 content_setting_views_.begin()); 872 i != content_setting_views_.end(); ++i) { 873 (*i)->Update( 874 toolbar_model_->GetInputInProgress() ? NULL : GetWebContents()); 875 any_visible = (*i)->IsVisible() || any_visible; 876 } 877 878 // If there are no visible content things, hide the top level box so it 879 // doesn't mess with padding. 880 gtk_widget_set_visible(content_setting_hbox_.get(), any_visible); 881} 882 883void LocationBarViewGtk::UpdatePageActions() { 884 UpdateScriptBubbleIcon(); 885 886 std::vector<ExtensionAction*> new_page_actions; 887 888 WebContents* contents = GetWebContents(); 889 if (contents) { 890 LocationBarController* location_bar_controller = 891 extensions::TabHelper::FromWebContents(contents)-> 892 location_bar_controller(); 893 new_page_actions = location_bar_controller->GetCurrentActions(); 894 } 895 896 // Initialize on the first call, or re-initialize if more extensions have been 897 // loaded or added after startup. 898 if (new_page_actions != page_actions_) { 899 page_actions_.swap(new_page_actions); 900 page_action_views_.clear(); 901 902 for (size_t i = 0; i < page_actions_.size(); ++i) { 903 page_action_views_.push_back( 904 new PageActionViewGtk(this, page_actions_[i])); 905 gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()), 906 page_action_views_[i]->widget(), FALSE, FALSE, 0); 907 } 908 content::NotificationService::current()->Notify( 909 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, 910 content::Source<LocationBar>(this), 911 content::NotificationService::NoDetails()); 912 } 913 914 if (!page_action_views_.empty() && contents) { 915 GURL url = browser()->tab_strip_model()->GetActiveWebContents()->GetURL(); 916 917 for (size_t i = 0; i < page_action_views_.size(); i++) { 918 page_action_views_[i]->UpdateVisibility( 919 toolbar_model_->GetInputInProgress() ? NULL : contents, url); 920 } 921 gtk_widget_queue_draw(hbox_.get()); 922 } 923 924 // If there are no visible page actions, hide the hbox too, so that it does 925 // not affect the padding in the location bar. 926 gtk_widget_set_visible(page_action_hbox_.get(), 927 PageActionVisibleCount() && !ShouldOnlyShowLocation()); 928} 929 930void LocationBarViewGtk::InvalidatePageActions() { 931 size_t count_before = page_action_views_.size(); 932 page_action_views_.clear(); 933 if (page_action_views_.size() != count_before) { 934 content::NotificationService::current()->Notify( 935 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_COUNT_CHANGED, 936 content::Source<LocationBar>(this), 937 content::NotificationService::NoDetails()); 938 } 939} 940 941void LocationBarViewGtk::UpdateOpenPDFInReaderPrompt() { 942 // Not implemented on Gtk. 943} 944 945void LocationBarViewGtk::UpdateGeneratedCreditCardView() { 946 NOTIMPLEMENTED(); 947} 948 949void LocationBarViewGtk::SaveStateToContents(WebContents* contents) { 950 location_entry_->SaveStateToTab(contents); 951} 952 953void LocationBarViewGtk::Revert() { 954 location_entry_->RevertAll(); 955} 956 957const OmniboxView* LocationBarViewGtk::GetLocationEntry() const { 958 return location_entry_.get(); 959} 960 961OmniboxView* LocationBarViewGtk::GetLocationEntry() { 962 return location_entry_.get(); 963} 964 965LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() { 966 return this; 967} 968 969int LocationBarViewGtk::PageActionCount() { 970 return page_action_views_.size(); 971} 972 973int LocationBarViewGtk::PageActionVisibleCount() { 974 int count = 0; 975 gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()), 976 CountVisibleWidgets, &count); 977 return count; 978} 979 980ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) { 981 if (index >= page_action_views_.size()) { 982 NOTREACHED(); 983 return NULL; 984 } 985 986 return page_action_views_[index]->page_action(); 987} 988 989ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) { 990 size_t visible_index = 0; 991 for (size_t i = 0; i < page_action_views_.size(); ++i) { 992 if (page_action_views_[i]->IsVisible()) { 993 if (index == visible_index++) 994 return page_action_views_[i]->page_action(); 995 } 996 } 997 998 NOTREACHED(); 999 return NULL; 1000} 1001 1002void LocationBarViewGtk::TestPageActionPressed(size_t index) { 1003 if (index >= page_action_views_.size()) { 1004 NOTREACHED(); 1005 return; 1006 } 1007 1008 page_action_views_[index]->TestActivatePageAction(); 1009} 1010 1011bool LocationBarViewGtk::GetBookmarkStarVisibility() { 1012 return starred_; 1013} 1014 1015void LocationBarViewGtk::Observe(int type, 1016 const content::NotificationSource& source, 1017 const content::NotificationDetails& details) { 1018 switch (type) { 1019 case chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED: { 1020 // Only update if the updated action box was for the active tab contents. 1021 WebContents* target_tab = content::Details<WebContents>(details).ptr(); 1022 if (target_tab == GetWebContents()) 1023 UpdatePageActions(); 1024 break; 1025 } 1026 1027 case chrome::NOTIFICATION_BROWSER_THEME_CHANGED: { 1028 if (theme_service_->UsingNativeTheme()) { 1029 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL); 1030 1031 GdkColor border_color = theme_service_->GetGdkColor( 1032 ThemeProperties::COLOR_FRAME); 1033 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color); 1034 1035 gtk_util::UndoForceFontSize(security_info_label_); 1036 gtk_util::UndoForceFontSize(tab_to_search_full_label_); 1037 gtk_util::UndoForceFontSize(tab_to_search_partial_label_); 1038 gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_); 1039 gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_); 1040 1041 gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_), 1042 0, 0, 0, 0); 1043 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_), 1044 1, 1, 1, 0); 1045 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_), 1046 1, 1, 1, 0); 1047 } else { 1048 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, 1049 &kKeywordBackgroundColor); 1050 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, 1051 kKeywordBorderColor); 1052 1053 // Until we switch to vector graphics, force the font size of labels. 1054 // 12.1px = 9pt @ 96dpi 1055 gtk_util::ForceFontSizePixels(security_info_label_, 12.1); 1056 gtk_util::ForceFontSizePixels(tab_to_search_full_label_, 1057 browser_defaults::kOmniboxFontPixelSize); 1058 gtk_util::ForceFontSizePixels(tab_to_search_partial_label_, 1059 browser_defaults::kOmniboxFontPixelSize); 1060 gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_, 1061 browser_defaults::kOmniboxFontPixelSize); 1062 gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_, 1063 browser_defaults::kOmniboxFontPixelSize); 1064 1065 const int top_bottom = popup_window_mode_ ? kPopupEdgeThickness : 0; 1066 gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_), 1067 kTopMargin + kBorderThickness, 1068 kBottomMargin + kBorderThickness, 1069 top_bottom, top_bottom); 1070 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_), 1071 1, 1, 0, 0); 1072 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_), 1073 1, 1, 0, 0); 1074 } 1075 1076 UpdateZoomIcon(); 1077 UpdateScriptBubbleIcon(); 1078 UpdateStarIcon(); 1079 UpdateSiteTypeArea(); 1080 UpdateContentSettingsIcons(); 1081 break; 1082 } 1083 1084 default: 1085 NOTREACHED(); 1086 } 1087} 1088 1089gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget, 1090 GdkEventExpose* event) { 1091 // If we're not using GTK theming, draw our own border over the edge pixels 1092 // of the background. 1093 GtkThemeService* theme_service = 1094 GtkThemeService::GetFrom(browser_->profile()); 1095 if (!theme_service->UsingNativeTheme()) { 1096 // Perform a scoped paint to fill in the background color. 1097 { 1098 gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false); 1099 1100 GtkAllocation allocation; 1101 gtk_widget_get_allocation(widget, &allocation); 1102 1103 int thickness = popup_window_mode_ ? 1104 kPopupEdgeThickness : kNormalEdgeThickness; 1105 gfx::Rect bounds(allocation); 1106 bounds.Inset(thickness, thickness); 1107 1108 const SkColor color = SK_ColorWHITE; 1109 if (popup_window_mode_) { 1110 canvas.FillRect(bounds, color); 1111 } else { 1112 SkPaint paint; 1113 paint.setStyle(SkPaint::kFill_Style); 1114 paint.setColor(color); 1115 const int kBorderCornerRadius = 2; 1116 canvas.DrawRoundRect(bounds, kBorderCornerRadius, paint); 1117 } 1118 } 1119 1120 if (popup_window_mode_) { 1121 NineBox(IDR_OMNIBOX_POPUP_BORDER_TOP_LEFT, 1122 IDR_OMNIBOX_POPUP_BORDER_TOP, 1123 IDR_OMNIBOX_POPUP_BORDER_TOP_RIGHT, 1124 IDR_OMNIBOX_POPUP_BORDER_LEFT, 1125 IDR_OMNIBOX_POPUP_BORDER_CENTER, 1126 IDR_OMNIBOX_POPUP_BORDER_RIGHT, 1127 IDR_OMNIBOX_POPUP_BORDER_BOTTOM_LEFT, 1128 IDR_OMNIBOX_POPUP_BORDER_BOTTOM, 1129 IDR_OMNIBOX_POPUP_BORDER_BOTTOM_RIGHT).RenderToWidget(widget); 1130 } else { 1131 NineBox(IDR_OMNIBOX_BORDER_TOP_LEFT, 1132 IDR_OMNIBOX_BORDER_TOP, 1133 IDR_OMNIBOX_BORDER_TOP_RIGHT, 1134 IDR_OMNIBOX_BORDER_LEFT, 1135 IDR_OMNIBOX_BORDER_CENTER, 1136 IDR_OMNIBOX_BORDER_RIGHT, 1137 IDR_OMNIBOX_BORDER_BOTTOM_LEFT, 1138 IDR_OMNIBOX_BORDER_BOTTOM, 1139 IDR_OMNIBOX_BORDER_BOTTOM_RIGHT).RenderToWidget(widget); 1140 } 1141 } 1142 1143 // Draw ExtensionAction backgrounds and borders, if necessary. The borders 1144 // appear exactly between the elements, so they can't draw the borders 1145 // themselves. 1146 gfx::CanvasSkiaPaint canvas(event, /*opaque=*/false); 1147 for (ScopedVector<PageActionViewGtk>::const_iterator 1148 page_action_view = page_action_views_.begin(); 1149 page_action_view != page_action_views_.end(); 1150 ++page_action_view) { 1151 if ((*page_action_view)->IsVisible()) { 1152 // Figure out where the page action is drawn so we can draw 1153 // borders to its left and right. 1154 GtkAllocation allocation; 1155 gtk_widget_get_allocation((*page_action_view)->widget(), &allocation); 1156 ExtensionAction* action = (*page_action_view)->page_action(); 1157 gfx::Rect bounds(allocation); 1158 // Make the bounding rectangle include the whole vertical range of the 1159 // location bar, and the mid-point pixels between adjacent page actions. 1160 // 1161 // For odd InnerPadding()s, "InnerPadding() + 1" includes the mid-point 1162 // between two page actions in the bounding rectangle. For even paddings, 1163 // the +1 is dropped, which is right since there is no pixel at the 1164 // mid-point. 1165 bounds.Inset(-(InnerPadding() + 1) / 2, 1166 theme_service_->UsingNativeTheme() ? -1 : 0); 1167 location_bar_util::PaintExtensionActionBackground( 1168 *action, SessionID::IdForTab(GetWebContents()), 1169 &canvas, bounds, 1170 theme_service_->get_location_bar_text_color(), 1171 theme_service_->get_location_bar_bg_color()); 1172 } 1173 } 1174 // Destroying |canvas| draws the background. 1175 1176 return FALSE; // Continue propagating the expose. 1177} 1178 1179void LocationBarViewGtk::UpdateSiteTypeArea() { 1180 // The icon is always visible except when the |tab_to_search_alignment_| is 1181 // visible. 1182 if (!location_entry_->model()->keyword().empty() && 1183 !location_entry_->model()->is_keyword_hint()) { 1184 gtk_widget_hide(site_type_area()); 1185 return; 1186 } 1187 1188 int resource_id = location_entry_->GetIcon(); 1189 gtk_image_set_from_pixbuf( 1190 GTK_IMAGE(location_icon_image_), 1191 theme_service_->GetImageNamed(resource_id).ToGdkPixbuf()); 1192 1193 if (toolbar_model_->GetSecurityLevel(false) == ToolbarModel::EV_SECURE) { 1194 if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) { 1195 // Fun fact: If wee try to make |site_type_event_box_| act as a 1196 // rounded window while it doesn't have a visible window, GTK interprets 1197 // this as a sign that it should paint the skyline texture into the 1198 // omnibox. 1199 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_), 1200 TRUE); 1201 1202 gtk_util::ActAsRoundedWindow(site_type_event_box_, 1203 kEvSecureBorderColor, 1204 kCornerSize, 1205 gtk_util::ROUNDED_ALL, 1206 gtk_util::BORDER_ALL); 1207 } 1208 1209 string16 info_text = toolbar_model_->GetEVCertName(); 1210 gtk_label_set_text(GTK_LABEL(security_info_label_), 1211 UTF16ToUTF8(info_text).c_str()); 1212 1213 UpdateEVCertificateLabelSize(); 1214 1215 gtk_widget_show(GTK_WIDGET(security_info_label_)); 1216 } else { 1217 if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) { 1218 gtk_util::StopActingAsRoundedWindow(site_type_event_box_); 1219 1220 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_), 1221 FALSE); 1222 } 1223 1224 gtk_widget_hide(GTK_WIDGET(security_info_label_)); 1225 } 1226 1227 if (GetLocationEntry()->IsEditingOrEmpty()) { 1228 // Do not show the tooltip if the user has been editing the location 1229 // bar, or the location bar is at the NTP. 1230 gtk_widget_set_tooltip_text(location_icon_image_, ""); 1231 } else { 1232 gtk_widget_set_tooltip_text(location_icon_image_, 1233 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str()); 1234 } 1235 1236 gtk_widget_show(site_type_area()); 1237 1238 SetSiteTypeDragSource(); 1239} 1240 1241void LocationBarViewGtk::UpdateEVCertificateLabelSize() { 1242 // Figure out the width of the average character. 1243 PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_)); 1244 PangoContext* context = pango_layout_get_context(layout); 1245 PangoFontMetrics* metrics = pango_context_get_metrics( 1246 context, 1247 gtk_widget_get_style(security_info_label_)->font_desc, 1248 pango_context_get_language(context)); 1249 int char_width = 1250 pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE; 1251 1252 // The EV label should never take up more than half the hbox. We try to 1253 // correct our inaccurate measurement units ("the average character width") 1254 // by dividing more than an even 2. 1255 GtkAllocation security_label_allocation; 1256 gtk_widget_get_allocation(security_info_label_, &security_label_allocation); 1257 GtkAllocation entry_box_allocation; 1258 gtk_widget_get_allocation(entry_box_, &entry_box_allocation); 1259 int text_area = security_label_allocation.width + 1260 entry_box_allocation.width; 1261 int max_chars = static_cast<int>(static_cast<float>(text_area) / 1262 static_cast<float>(char_width) / 2.75); 1263 // Don't let the label be smaller than 10 characters so that the country 1264 // code is always visible. 1265 gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_), 1266 std::max(10, max_chars)); 1267 1268 pango_font_metrics_unref(metrics); 1269} 1270 1271void LocationBarViewGtk::SetKeywordLabel(const string16& keyword) { 1272 if (keyword.empty()) 1273 return; 1274 1275 Profile* profile = browser_->profile(); 1276 TemplateURLService* template_url_service = 1277 TemplateURLServiceFactory::GetForProfile(profile); 1278 if (!template_url_service) 1279 return; 1280 1281 bool is_extension_keyword; 1282 const string16 short_name = template_url_service->GetKeywordShortName( 1283 keyword, &is_extension_keyword); 1284 const string16 min_string = location_bar_util::CalculateMinString(short_name); 1285 const string16 full_name = is_extension_keyword ? 1286 short_name : 1287 l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, short_name); 1288 const string16 partial_name = is_extension_keyword ? 1289 min_string : 1290 l10n_util::GetStringFUTF16(IDS_OMNIBOX_KEYWORD_TEXT, min_string); 1291 gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_), 1292 UTF16ToUTF8(full_name).c_str()); 1293 gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_), 1294 UTF16ToUTF8(partial_name).c_str()); 1295 1296 if (last_keyword_ != keyword) { 1297 last_keyword_ = keyword; 1298 1299 if (is_extension_keyword) { 1300 const TemplateURL* template_url = 1301 template_url_service->GetTemplateURLForKeyword(keyword); 1302 gfx::Image image = extensions::OmniboxAPI::Get(profile)-> 1303 GetOmniboxIcon(template_url->GetExtensionId()); 1304 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), 1305 image.ToGdkPixbuf()); 1306 } else { 1307 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); 1308 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), 1309 rb.GetNativeImageNamed(IDR_OMNIBOX_SEARCH).ToGdkPixbuf()); 1310 } 1311 } 1312} 1313 1314void LocationBarViewGtk::SetKeywordHintLabel(const string16& keyword) { 1315 if (keyword.empty()) 1316 return; 1317 1318 TemplateURLService* template_url_service = 1319 TemplateURLServiceFactory::GetForProfile(browser_->profile()); 1320 if (!template_url_service) 1321 return; 1322 1323 bool is_extension_keyword; 1324 const string16 short_name = template_url_service-> 1325 GetKeywordShortName(keyword, &is_extension_keyword); 1326 int message_id = is_extension_keyword ? 1327 IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT; 1328 std::vector<size_t> content_param_offsets; 1329 const string16 keyword_hint = l10n_util::GetStringFUTF16( 1330 message_id, 1331 string16(), 1332 short_name, 1333 &content_param_offsets); 1334 if (content_param_offsets.size() != 2) { 1335 // See comments on an identical NOTREACHED() in search_provider.cc. 1336 NOTREACHED(); 1337 return; 1338 } 1339 1340 std::string leading(UTF16ToUTF8( 1341 keyword_hint.substr(0, content_param_offsets.front()))); 1342 std::string trailing(UTF16ToUTF8( 1343 keyword_hint.substr(content_param_offsets.front()))); 1344 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_), 1345 leading.c_str()); 1346 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_), 1347 trailing.c_str()); 1348} 1349 1350void LocationBarViewGtk::ShowFirstRunBubbleInternal() { 1351 if (!location_entry_.get() || !gtk_widget_get_window(widget())) 1352 return; 1353 1354 gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_); 1355 bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing); 1356 FirstRunBubble::Show(browser_, location_icon_image_, bounds); 1357} 1358 1359gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender, 1360 GdkEventButton* event) { 1361 WebContents* tab = GetWebContents(); 1362 1363 if (event->button == 1) { 1364 // Do not show page info if the user has been editing the location 1365 // bar, or the location bar is at the NTP. 1366 if (GetLocationEntry()->IsEditingOrEmpty()) 1367 return FALSE; 1368 1369 // (0,0) event coordinates indicates that the release came at the end of 1370 // a drag. 1371 if (event->x == 0 && event->y == 0) 1372 return FALSE; 1373 1374 // Important to use GetVisibleEntry to match what's showing in the omnibox. 1375 NavigationEntry* nav_entry = tab->GetController().GetVisibleEntry(); 1376 if (!nav_entry) { 1377 NOTREACHED(); 1378 return FALSE; 1379 } 1380 chrome::ShowWebsiteSettings(browser_, tab, nav_entry->GetURL(), 1381 nav_entry->GetSSL()); 1382 return TRUE; 1383 } else if (event->button == 2) { 1384 // When the user middle clicks on the location icon, try to open the 1385 // contents of the PRIMARY selection in the current tab. 1386 // If the click was outside our bounds, do nothing. 1387 if (!gtk_util::WidgetBounds(sender).Contains( 1388 gfx::Point(event->x, event->y))) { 1389 return FALSE; 1390 } 1391 1392 GURL url; 1393 if (!gtk_util::URLFromPrimarySelection(browser_->profile(), &url)) 1394 return FALSE; 1395 1396 tab->OpenURL(OpenURLParams( 1397 url, content::Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED, 1398 false)); 1399 return TRUE; 1400 } 1401 1402 return FALSE; 1403} 1404 1405void LocationBarViewGtk::OnIconDragData(GtkWidget* sender, 1406 GdkDragContext* context, 1407 GtkSelectionData* data, 1408 guint info, guint time) { 1409 ui::WriteURLWithName(data, drag_url_, drag_title_, info); 1410} 1411 1412void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender, 1413 GdkDragContext* context) { 1414 gfx::Image favicon = GetFavicon(); 1415 if (favicon.IsEmpty()) 1416 return; 1417 drag_icon_ = bookmark_utils::GetDragRepresentation(favicon.ToGdkPixbuf(), 1418 GetTitle(), theme_service_); 1419 gtk_drag_set_icon_widget(context, drag_icon_, 0, 0); 1420 1421 WebContents* tab = GetWebContents(); 1422 if (!tab) 1423 return; 1424 drag_url_ = tab->GetURL(); 1425 drag_title_ = tab->GetTitle(); 1426} 1427 1428void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender, 1429 GdkDragContext* context) { 1430 DCHECK(drag_icon_); 1431 gtk_widget_destroy(drag_icon_); 1432 drag_icon_ = NULL; 1433 drag_url_ = GURL::EmptyGURL(); 1434 drag_title_.clear(); 1435} 1436 1437void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender, 1438 GtkAllocation* allocation) { 1439 if (hbox_width_ != allocation->width) { 1440 hbox_width_ = allocation->width; 1441 UpdateEVCertificateLabelSize(); 1442 } 1443 if (browser_ && browser_->instant_controller()) { 1444 browser_->instant_controller()-> 1445 SetOmniboxBounds(AllocationToRect(*allocation)); 1446 } 1447} 1448 1449void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender, 1450 GtkAllocation* allocation) { 1451 if (entry_box_width_ != allocation->width) { 1452 entry_box_width_ = allocation->width; 1453 AdjustChildrenVisibility(); 1454 } 1455} 1456 1457gboolean LocationBarViewGtk::OnZoomButtonPress(GtkWidget* widget, 1458 GdkEventButton* event) { 1459 if (event->button == 1 && GetWebContents()) { 1460 // If the zoom icon is clicked, show the zoom bubble and keep it open until 1461 // it loses focus. 1462 ZoomBubbleGtk::ShowBubble(GetWebContents(), false); 1463 return TRUE; 1464 } 1465 return FALSE; 1466} 1467 1468gboolean LocationBarViewGtk::OnScriptBubbleButtonPress(GtkWidget* widget, 1469 GdkEventButton* event) { 1470 if (event->button == 1 && GetWebContents()) { 1471 ScriptBubbleGtk::Show(script_bubble_button_image_, GetWebContents()); 1472 return TRUE; 1473 } 1474 return FALSE; 1475} 1476 1477gboolean LocationBarViewGtk::OnScriptBubbleButtonExpose(GtkWidget* widget, 1478 GdkEventExpose* event) { 1479 gfx::CanvasSkiaPaint canvas(event, false); 1480 GtkAllocation allocation; 1481 gtk_widget_get_allocation(widget, &allocation); 1482 badge_util::PaintBadge(&canvas, 1483 gfx::Rect(allocation), 1484 base::UintToString(num_running_scripts_), 1485 SK_ColorWHITE, 1486 SkColorSetRGB(0, 170, 0), 1487 allocation.width, 1488 extensions::ActionInfo::TYPE_PAGE); 1489 return FALSE; 1490} 1491 1492void LocationBarViewGtk::OnStarButtonSizeAllocate(GtkWidget* sender, 1493 GtkAllocation* allocation) { 1494 if (!on_star_sized_.is_null()) { 1495 on_star_sized_.Run(); 1496 on_star_sized_.Reset(); 1497 } 1498 star_sized_ = true; 1499} 1500 1501gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget, 1502 GdkEventButton* event) { 1503 if (event->button == 1) { 1504 chrome::ExecuteCommand(browser_, IDC_BOOKMARK_PAGE); 1505 return TRUE; 1506 } 1507 return FALSE; 1508} 1509 1510void LocationBarViewGtk::ShowZoomBubble() { 1511 if (toolbar_model_->GetInputInProgress() || !GetWebContents()) 1512 return; 1513 1514 ZoomBubbleGtk::ShowBubble(GetWebContents(), true); 1515} 1516 1517void LocationBarViewGtk::ShowStarBubble(const GURL& url, 1518 bool newly_bookmarked) { 1519 if (!star_.get()) 1520 return; 1521 1522 if (star_sized_) { 1523 BookmarkBubbleGtk::Show(star_.get(), browser_->profile(), url, 1524 newly_bookmarked); 1525 } else { 1526 on_star_sized_ = base::Bind(&BookmarkBubbleGtk::Show, 1527 star_.get(), browser_->profile(), 1528 url, newly_bookmarked); 1529 } 1530} 1531 1532void LocationBarViewGtk::SetStarred(bool starred) { 1533 if (starred == starred_) 1534 return; 1535 1536 starred_ = starred; 1537 UpdateStarIcon(); 1538} 1539 1540void LocationBarViewGtk::ZoomChangedForActiveTab(bool can_show_bubble) { 1541 UpdateZoomIcon(); 1542 1543 if (can_show_bubble && gtk_widget_get_visible(zoom_.get())) 1544 ShowZoomBubble(); 1545} 1546 1547void LocationBarViewGtk::UpdateZoomIcon() { 1548 WebContents* web_contents = GetWebContents(); 1549 if (!zoom_.get() || !web_contents) 1550 return; 1551 1552 ZoomController* zoom_controller = 1553 ZoomController::FromWebContents(web_contents); 1554 if (!zoom_controller || zoom_controller->IsAtDefaultZoom() || 1555 toolbar_model_->GetInputInProgress()) { 1556 gtk_widget_hide(zoom_.get()); 1557 ZoomBubbleGtk::CloseBubble(); 1558 return; 1559 } 1560 1561 const int zoom_resource = zoom_controller->GetResourceForZoomLevel(); 1562 gtk_image_set_from_pixbuf(GTK_IMAGE(zoom_image_), 1563 theme_service_->GetImageNamed(zoom_resource).ToGdkPixbuf()); 1564 1565 string16 tooltip = l10n_util::GetStringFUTF16Int( 1566 IDS_TOOLTIP_ZOOM, zoom_controller->zoom_percent()); 1567 gtk_widget_set_tooltip_text(zoom_.get(), UTF16ToUTF8(tooltip).c_str()); 1568 1569 gtk_widget_show(zoom_.get()); 1570} 1571 1572void LocationBarViewGtk::UpdateScriptBubbleIcon() { 1573 num_running_scripts_ = 0; 1574 if (GetWebContents()) { 1575 extensions::TabHelper* tab_helper = 1576 extensions::TabHelper::FromWebContents(GetWebContents()); 1577 if (tab_helper && tab_helper->script_bubble_controller()) { 1578 num_running_scripts_ = tab_helper->script_bubble_controller()-> 1579 extensions_running_scripts().size(); 1580 } 1581 } 1582 1583 if (num_running_scripts_ == 0u) 1584 gtk_widget_hide(script_bubble_button_.get()); 1585 else 1586 gtk_widget_show(script_bubble_button_.get()); 1587} 1588 1589void LocationBarViewGtk::UpdateStarIcon() { 1590 if (!star_.get()) 1591 return; 1592 // Indicate the star icon is not correctly sized. It will be marked as sized 1593 // when the next size-allocate signal is received by the star widget. 1594 star_sized_ = false; 1595 bool star_enabled = !toolbar_model_->GetInputInProgress() && 1596 edit_bookmarks_enabled_.GetValue(); 1597 command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled); 1598 command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE_FROM_STAR, 1599 star_enabled); 1600 if (star_enabled) { 1601 gtk_widget_show_all(star_.get()); 1602 int id = starred_ ? IDR_STAR_LIT : IDR_STAR; 1603 gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_), 1604 theme_service_->GetImageNamed(id).ToGdkPixbuf()); 1605 gtk_widget_set_tooltip_text(star_.get(), l10n_util::GetStringUTF8( 1606 starred_ ? IDS_TOOLTIP_STARRED : IDS_TOOLTIP_STAR).c_str()); 1607 } else { 1608 gtk_widget_hide_all(star_.get()); 1609 } 1610} 1611 1612bool LocationBarViewGtk::ShouldOnlyShowLocation() { 1613 return !browser_->is_type_tabbed(); 1614} 1615 1616void LocationBarViewGtk::AdjustChildrenVisibility() { 1617 int text_width = location_entry_->TextWidth(); 1618 int available_width = entry_box_width_ - text_width - InnerPadding(); 1619 1620 // Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be 1621 // visible at the same time. 1622 if (!show_selected_keyword_ && 1623 gtk_widget_get_visible(tab_to_search_alignment_)) { 1624 gtk_widget_hide(tab_to_search_alignment_); 1625 } else if (!show_keyword_hint_ && 1626 gtk_widget_get_visible(tab_to_search_hint_)) { 1627 gtk_widget_hide(tab_to_search_hint_); 1628 } 1629 1630 if (show_selected_keyword_) { 1631 GtkRequisition box, full_label, partial_label; 1632 gtk_widget_size_request(tab_to_search_box_, &box); 1633 gtk_widget_size_request(tab_to_search_full_label_, &full_label); 1634 gtk_widget_size_request(tab_to_search_partial_label_, &partial_label); 1635 int full_partial_width_diff = full_label.width - partial_label.width; 1636 int full_box_width; 1637 int partial_box_width; 1638 if (gtk_widget_get_visible(tab_to_search_full_label_)) { 1639 full_box_width = box.width; 1640 partial_box_width = full_box_width - full_partial_width_diff; 1641 } else { 1642 partial_box_width = box.width; 1643 full_box_width = partial_box_width + full_partial_width_diff; 1644 } 1645 1646 if (partial_box_width >= entry_box_width_ - InnerPadding()) { 1647 gtk_widget_hide(tab_to_search_alignment_); 1648 } else if (full_box_width >= available_width) { 1649 gtk_widget_hide(tab_to_search_full_label_); 1650 gtk_widget_show(tab_to_search_partial_label_); 1651 gtk_widget_show(tab_to_search_alignment_); 1652 } else if (full_box_width < available_width) { 1653 gtk_widget_hide(tab_to_search_partial_label_); 1654 gtk_widget_show(tab_to_search_full_label_); 1655 gtk_widget_show(tab_to_search_alignment_); 1656 } 1657 } else if (show_keyword_hint_) { 1658 GtkRequisition leading, icon, trailing; 1659 gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading); 1660 gtk_widget_size_request(tab_to_search_hint_icon_, &icon); 1661 gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing); 1662 int full_width = leading.width + icon.width + trailing.width; 1663 1664 if (icon.width >= entry_box_width_ - InnerPadding()) { 1665 gtk_widget_hide(tab_to_search_hint_); 1666 } else if (full_width >= available_width) { 1667 gtk_widget_hide(tab_to_search_hint_leading_label_); 1668 gtk_widget_hide(tab_to_search_hint_trailing_label_); 1669 gtk_widget_show(tab_to_search_hint_); 1670 } else if (full_width < available_width) { 1671 gtk_widget_show(tab_to_search_hint_leading_label_); 1672 gtk_widget_show(tab_to_search_hint_trailing_label_); 1673 gtk_widget_show(tab_to_search_hint_); 1674 } 1675 } 1676} 1677 1678//////////////////////////////////////////////////////////////////////////////// 1679// LocationBarViewGtk::PageToolViewGtk 1680 1681LocationBarViewGtk::PageToolViewGtk::PageToolViewGtk( 1682 const LocationBarViewGtk* parent) 1683 : alignment_(gtk_alignment_new(0, 0, 1, 1)), 1684 event_box_(gtk_event_box_new()), 1685 hbox_(gtk_hbox_new(FALSE, InnerPadding())), 1686 image_(gtk_image_new()), 1687 label_(gtk_label_new(NULL)), 1688 parent_(parent), 1689 animation_(this), 1690 weak_factory_(this) { 1691 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), 1, 1, 0, 0); 1692 gtk_container_add(GTK_CONTAINER(alignment_.get()), event_box_.get()); 1693 1694 // Make the event box not visible so it does not paint a background. 1695 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); 1696 g_signal_connect(event_box_.get(), "button-press-event", 1697 G_CALLBACK(&OnButtonPressedThunk), this); 1698 g_signal_connect(event_box_.get(), "expose-event", 1699 G_CALLBACK(&OnExposeThunk), this); 1700 1701 gtk_widget_set_no_show_all(label_.get(), TRUE); 1702 gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE); 1703 1704 gtk_box_pack_start(GTK_BOX(hbox_), image_.get(), FALSE, FALSE, 0); 1705 gtk_box_pack_start(GTK_BOX(hbox_), label_.get(), FALSE, FALSE, 0); 1706 1707 gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder); 1708 1709 gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_); 1710 gtk_widget_hide(widget()); 1711 1712 animation_.SetSlideDuration(kPageToolAnimationTime); 1713} 1714 1715LocationBarViewGtk::PageToolViewGtk::~PageToolViewGtk() { 1716 image_.Destroy(); 1717 label_.Destroy(); 1718 event_box_.Destroy(); 1719 alignment_.Destroy(); 1720} 1721 1722GtkWidget* LocationBarViewGtk::PageToolViewGtk::widget() { 1723 return alignment_.get(); 1724} 1725 1726bool LocationBarViewGtk::PageToolViewGtk::IsVisible() { 1727 return gtk_widget_get_visible(widget()); 1728} 1729 1730void LocationBarViewGtk::PageToolViewGtk::StartAnimating() { 1731 if (animation_.IsShowing() || animation_.IsClosing()) 1732 return; 1733 1734 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE); 1735 GdkColor border_color = button_border_color(); 1736 gtk_util::ActAsRoundedWindow(event_box_.get(), border_color, 1737 kCornerSize, 1738 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL); 1739 1740 gtk_widget_set_size_request(label_.get(), -1, -1); 1741 gtk_widget_size_request(label_.get(), &label_req_); 1742 gtk_widget_set_size_request(label_.get(), 0, -1); 1743 gtk_widget_show(label_.get()); 1744 1745 animation_.Show(); 1746} 1747 1748void LocationBarViewGtk::PageToolViewGtk::CloseAnimation() { 1749 animation_.Hide(); 1750} 1751 1752void LocationBarViewGtk::PageToolViewGtk::AnimationProgressed( 1753 const ui::Animation* animation) { 1754 gtk_widget_set_size_request( 1755 label_.get(), 1756 animation->GetCurrentValue() * label_req_.width, 1757 -1); 1758} 1759 1760void LocationBarViewGtk::PageToolViewGtk::AnimationEnded( 1761 const ui::Animation* animation) { 1762} 1763 1764void LocationBarViewGtk::PageToolViewGtk::AnimationCanceled( 1765 const ui::Animation* animation) { 1766} 1767 1768gboolean LocationBarViewGtk::PageToolViewGtk::OnButtonPressed( 1769 GtkWidget* sender, GdkEvent* event) { 1770 OnClick(sender); 1771 return TRUE; 1772} 1773 1774gboolean LocationBarViewGtk::PageToolViewGtk::OnExpose( 1775 GtkWidget* sender, GdkEventExpose* event) { 1776 TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageToolViewGtk::OnExpose"); 1777 1778 if (!(animation_.IsShowing() || animation_.IsClosing())) 1779 return FALSE; 1780 1781 GtkAllocation allocation; 1782 gtk_widget_get_allocation(sender, &allocation); 1783 const int height = allocation.height; 1784 1785 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(sender)); 1786 gdk_cairo_rectangle(cr, &event->area); 1787 cairo_clip(cr); 1788 1789 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height); 1790 1791 const GdkColor top_color = gradient_top_color(); 1792 const GdkColor bottom_color = gradient_bottom_color(); 1793 cairo_pattern_add_color_stop_rgb( 1794 pattern, 0.0, 1795 top_color.red/255.0, 1796 top_color.blue/255.0, 1797 top_color.green/255.0); 1798 cairo_pattern_add_color_stop_rgb( 1799 pattern, 1.0, 1800 bottom_color.red/255.0, 1801 bottom_color.blue/255.0, 1802 bottom_color.green/255.0); 1803 1804 cairo_set_source(cr, pattern); 1805 cairo_paint(cr); 1806 cairo_pattern_destroy(pattern); 1807 cairo_destroy(cr); 1808 1809 return FALSE; 1810} 1811 1812//////////////////////////////////////////////////////////////////////////////// 1813// LocationBarViewGtk::PageActionViewGtk 1814 1815LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk( 1816 LocationBarViewGtk* owner, 1817 ExtensionAction* page_action) 1818 : owner_(NULL), 1819 page_action_(page_action), 1820 current_tab_id_(-1), 1821 window_(NULL), 1822 accel_group_(NULL), 1823 preview_enabled_(false), 1824 scoped_icon_animation_observer_( 1825 page_action->GetIconAnimation( 1826 SessionID::IdForTab(owner->GetWebContents())), 1827 this) { 1828 event_box_.Own(gtk_event_box_new()); 1829 gtk_widget_set_size_request(event_box_.get(), 1830 extensions::IconsInfo::kPageActionIconMaxSize, 1831 extensions::IconsInfo::kPageActionIconMaxSize); 1832 1833 // Make the event box not visible so it does not paint a background. 1834 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); 1835 g_signal_connect(event_box_.get(), "button-press-event", 1836 G_CALLBACK(&OnButtonPressedThunk), this); 1837 g_signal_connect_after(event_box_.get(), "expose-event", 1838 G_CALLBACK(OnExposeEventThunk), this); 1839 g_signal_connect(event_box_.get(), "realize", 1840 G_CALLBACK(OnRealizeThunk), this); 1841 1842 image_.Own(gtk_image_new()); 1843 gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get()); 1844 1845 const Extension* extension = owner->browser()->profile()-> 1846 GetExtensionService()->GetExtensionById(page_action->extension_id(), 1847 false); 1848 DCHECK(extension); 1849 1850 icon_factory_.reset( 1851 new ExtensionActionIconFactory( 1852 owner->browser()->profile(), extension, page_action, this)); 1853 1854 // We set the owner last of all so that we can determine whether we are in 1855 // the process of initializing this class or not. 1856 owner_ = owner; 1857} 1858 1859LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() { 1860 DisconnectPageActionAccelerator(); 1861 1862 image_.Destroy(); 1863 event_box_.Destroy(); 1864} 1865 1866bool LocationBarViewGtk::PageActionViewGtk::IsVisible() { 1867 return gtk_widget_get_visible(widget()); 1868} 1869 1870void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility( 1871 WebContents* contents, const GURL& url) { 1872 // Save this off so we can pass it back to the extension when the action gets 1873 // executed. See PageActionImageView::OnMousePressed. 1874 current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1; 1875 current_url_ = url; 1876 1877 bool visible = contents && 1878 (preview_enabled_ || page_action_->GetIsVisible(current_tab_id_)); 1879 if (visible) { 1880 // Set the tooltip. 1881 gtk_widget_set_tooltip_text(event_box_.get(), 1882 page_action_->GetTitle(current_tab_id_).c_str()); 1883 1884 // Set the image. 1885 gfx::Image icon = icon_factory_->GetIcon(current_tab_id_); 1886 if (!icon.IsEmpty()) { 1887 GdkPixbuf* pixbuf = icon.ToGdkPixbuf(); 1888 DCHECK(pixbuf); 1889 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf); 1890 } 1891 } 1892 1893 bool old_visible = IsVisible(); 1894 if (visible) 1895 gtk_widget_show_all(event_box_.get()); 1896 else 1897 gtk_widget_hide_all(event_box_.get()); 1898 1899 if (visible != old_visible) { 1900 content::NotificationService::current()->Notify( 1901 chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, 1902 content::Source<ExtensionAction>(page_action_), 1903 content::Details<WebContents>(contents)); 1904 } 1905} 1906 1907void LocationBarViewGtk::PageActionViewGtk::OnIconUpdated() { 1908 // If we have no owner, that means this class is still being constructed. 1909 WebContents* web_contents = owner_ ? owner_->GetWebContents() : NULL; 1910 if (web_contents) 1911 UpdateVisibility(web_contents, current_url_); 1912} 1913 1914void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() { 1915 GdkEventButton event = {}; 1916 event.type = GDK_BUTTON_PRESS; 1917 event.button = 1; 1918 OnButtonPressed(widget(), &event); 1919} 1920 1921void LocationBarViewGtk::PageActionViewGtk::Observe( 1922 int type, 1923 const content::NotificationSource& source, 1924 const content::NotificationDetails& details) { 1925 DCHECK_EQ(type, chrome::NOTIFICATION_WINDOW_CLOSED); 1926 DisconnectPageActionAccelerator(); 1927} 1928 1929void LocationBarViewGtk::PageActionViewGtk::ConnectPageActionAccelerator() { 1930 const ExtensionSet* extensions = owner_->browser()->profile()-> 1931 GetExtensionService()->extensions(); 1932 const Extension* extension = 1933 extensions->GetByID(page_action_->extension_id()); 1934 window_ = owner_->browser()->window()->GetNativeWindow(); 1935 1936 extensions::CommandService* command_service = 1937 extensions::CommandService::Get(owner_->browser()->profile()); 1938 1939 extensions::Command command_page_action; 1940 if (command_service->GetPageActionCommand( 1941 extension->id(), 1942 extensions::CommandService::ACTIVE_ONLY, 1943 &command_page_action, 1944 NULL)) { 1945 // Found the page action shortcut command, register it. 1946 page_action_keybinding_.reset( 1947 new ui::Accelerator(command_page_action.accelerator())); 1948 } 1949 1950 extensions::Command command_script_badge; 1951 if (command_service->GetScriptBadgeCommand( 1952 extension->id(), 1953 extensions::CommandService::ACTIVE_ONLY, 1954 &command_script_badge, 1955 NULL)) { 1956 // Found the script badge shortcut command, register it. 1957 script_badge_keybinding_.reset( 1958 new ui::Accelerator(command_script_badge.accelerator())); 1959 } 1960 1961 if (page_action_keybinding_.get() || script_badge_keybinding_.get()) { 1962 accel_group_ = gtk_accel_group_new(); 1963 gtk_window_add_accel_group(window_, accel_group_); 1964 1965 if (page_action_keybinding_.get()) { 1966 gtk_accel_group_connect( 1967 accel_group_, 1968 ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_), 1969 ui::GetGdkModifierForAccelerator(*page_action_keybinding_), 1970 GtkAccelFlags(0), 1971 g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL)); 1972 } 1973 if (script_badge_keybinding_.get()) { 1974 gtk_accel_group_connect( 1975 accel_group_, 1976 ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_), 1977 ui::GetGdkModifierForAccelerator(*script_badge_keybinding_), 1978 GtkAccelFlags(0), 1979 g_cclosure_new(G_CALLBACK(OnGtkAccelerator), this, NULL)); 1980 } 1981 1982 // Since we've added an accelerator, we'll need to unregister it before 1983 // the window is closed, so we listen for the window being closed. 1984 registrar_.Add(this, 1985 chrome::NOTIFICATION_WINDOW_CLOSED, 1986 content::Source<GtkWindow>(window_)); 1987 } 1988} 1989 1990void LocationBarViewGtk::PageActionViewGtk::OnIconChanged() { 1991 UpdateVisibility(owner_->GetWebContents(), current_url_); 1992} 1993 1994void LocationBarViewGtk::PageActionViewGtk::DisconnectPageActionAccelerator() { 1995 if (accel_group_) { 1996 if (page_action_keybinding_.get()) { 1997 gtk_accel_group_disconnect_key( 1998 accel_group_, 1999 ui::GetGdkKeyCodeForAccelerator(*page_action_keybinding_), 2000 ui::GetGdkModifierForAccelerator(*page_action_keybinding_)); 2001 } 2002 if (script_badge_keybinding_.get()) { 2003 gtk_accel_group_disconnect_key( 2004 accel_group_, 2005 ui::GetGdkKeyCodeForAccelerator(*script_badge_keybinding_), 2006 ui::GetGdkModifierForAccelerator(*script_badge_keybinding_)); 2007 } 2008 gtk_window_remove_accel_group(window_, accel_group_); 2009 g_object_unref(accel_group_); 2010 accel_group_ = NULL; 2011 page_action_keybinding_.reset(NULL); 2012 script_badge_keybinding_.reset(NULL); 2013 } 2014} 2015 2016gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed( 2017 GtkWidget* sender, 2018 GdkEventButton* event) { 2019 // Double and triple-clicks generate both a GDK_BUTTON_PRESS and a 2020 // GDK_[23]BUTTON_PRESS event. We don't want to double-trigger by acting on 2021 // both. 2022 if (event->type != GDK_BUTTON_PRESS) 2023 return TRUE; 2024 2025 WebContents* web_contents = owner_->GetWebContents(); 2026 if (!web_contents) 2027 return TRUE; 2028 2029 ExtensionService* extension_service = 2030 owner_->browser()->profile()->GetExtensionService(); 2031 if (!extension_service) 2032 return TRUE; 2033 2034 const Extension* extension = 2035 extension_service->extensions()->GetByID(page_action()->extension_id()); 2036 if (!extension) 2037 return TRUE; 2038 2039 LocationBarController* controller = 2040 extensions::TabHelper::FromWebContents(web_contents)-> 2041 location_bar_controller(); 2042 2043 switch (controller->OnClicked(extension->id(), event->button)) { 2044 case LocationBarController::ACTION_NONE: 2045 break; 2046 2047 case LocationBarController::ACTION_SHOW_POPUP: 2048 ExtensionPopupGtk::Show( 2049 page_action_->GetPopupUrl(current_tab_id_), 2050 owner_->browser_, 2051 event_box_.get(), 2052 ExtensionPopupGtk::SHOW); 2053 break; 2054 2055 case LocationBarController::ACTION_SHOW_CONTEXT_MENU: 2056 context_menu_model_ = 2057 new ExtensionContextMenuModel(extension, owner_->browser_, this); 2058 context_menu_.reset( 2059 new MenuGtk(NULL, context_menu_model_.get())); 2060 context_menu_->PopupForWidget(sender, event->button, event->time); 2061 break; 2062 2063 case LocationBarController::ACTION_SHOW_SCRIPT_POPUP: 2064 ExtensionPopupGtk::Show( 2065 extensions::ExtensionInfoUI::GetURL(extension->id()), 2066 owner_->browser_, 2067 event_box_.get(), 2068 ExtensionPopupGtk::SHOW); 2069 break; 2070 } 2071 2072 return TRUE; 2073} 2074 2075gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent( 2076 GtkWidget* widget, 2077 GdkEventExpose* event) { 2078 TRACE_EVENT0("ui::gtk", "LocationBarViewGtk::PageActionViewGtk::OnExpose"); 2079 WebContents* contents = owner_->GetWebContents(); 2080 if (!contents) 2081 return FALSE; 2082 2083 int tab_id = ExtensionTabUtil::GetTabId(contents); 2084 if (tab_id < 0) 2085 return FALSE; 2086 2087 std::string badge_text = page_action_->GetBadgeText(tab_id); 2088 if (badge_text.empty()) 2089 return FALSE; 2090 2091 gfx::CanvasSkiaPaint canvas(event, false); 2092 GtkAllocation allocation; 2093 gtk_widget_get_allocation(widget, &allocation); 2094 page_action_->PaintBadge(&canvas, gfx::Rect(allocation), tab_id); 2095 return FALSE; 2096} 2097 2098void LocationBarViewGtk::PageActionViewGtk::OnRealize(GtkWidget* widget) { 2099 ConnectPageActionAccelerator(); 2100} 2101 2102// static 2103gboolean LocationBarViewGtk::PageActionViewGtk::OnGtkAccelerator( 2104 GtkAccelGroup* accel_group, 2105 GObject* acceleratable, 2106 guint keyval, 2107 GdkModifierType modifier, 2108 void* user_data) { 2109 PageActionViewGtk* view = static_cast<PageActionViewGtk*>(user_data); 2110 if (!gtk_widget_get_visible(view->widget())) 2111 return FALSE; 2112 2113 GdkEventButton event = {}; 2114 event.type = GDK_BUTTON_PRESS; 2115 event.button = 1; 2116 return view->OnButtonPressed(view->widget(), &event); 2117} 2118 2119void LocationBarViewGtk::PageActionViewGtk::InspectPopup( 2120 ExtensionAction* action) { 2121 ExtensionPopupGtk::Show( 2122 action->GetPopupUrl(current_tab_id_), 2123 owner_->browser_, 2124 event_box_.get(), 2125 ExtensionPopupGtk::SHOW_AND_INSPECT); 2126} 2127