1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/ui/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/command_line.h" 13#include "base/i18n/rtl.h" 14#include "base/logging.h" 15#include "base/string_util.h" 16#include "base/utf_string_conversions.h" 17#include "chrome/app/chrome_command_ids.h" 18#include "chrome/browser/accessibility_events.h" 19#include "chrome/browser/alternate_nav_url_fetcher.h" 20#include "chrome/browser/autocomplete/autocomplete_edit_view_gtk.h" 21#include "chrome/browser/autocomplete/autocomplete_popup_model.h" 22#include "chrome/browser/command_updater.h" 23#include "chrome/browser/content_setting_bubble_model.h" 24#include "chrome/browser/content_setting_image_model.h" 25#include "chrome/browser/defaults.h" 26#include "chrome/browser/extensions/extension_browser_event_router.h" 27#include "chrome/browser/extensions/extension_service.h" 28#include "chrome/browser/extensions/extension_tabs_module.h" 29#include "chrome/browser/instant/instant_controller.h" 30#include "chrome/browser/profiles/profile.h" 31#include "chrome/browser/search_engines/template_url.h" 32#include "chrome/browser/search_engines/template_url_model.h" 33#include "chrome/browser/ui/browser.h" 34#include "chrome/browser/ui/browser_list.h" 35#include "chrome/browser/ui/gtk/bookmarks/bookmark_bubble_gtk.h" 36#include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h" 37#include "chrome/browser/ui/gtk/cairo_cached_surface.h" 38#include "chrome/browser/ui/gtk/content_setting_bubble_gtk.h" 39#include "chrome/browser/ui/gtk/extensions/extension_popup_gtk.h" 40#include "chrome/browser/ui/gtk/first_run_bubble.h" 41#include "chrome/browser/ui/gtk/gtk_theme_service.h" 42#include "chrome/browser/ui/gtk/gtk_util.h" 43#include "chrome/browser/ui/gtk/nine_box.h" 44#include "chrome/browser/ui/gtk/rounded_window.h" 45#include "chrome/browser/ui/gtk/view_id_util.h" 46#include "chrome/browser/ui/omnibox/location_bar_util.h" 47#include "chrome/common/chrome_switches.h" 48#include "chrome/common/extensions/extension.h" 49#include "chrome/common/extensions/extension_action.h" 50#include "chrome/common/extensions/extension_resource.h" 51#include "chrome/common/pref_names.h" 52#include "content/browser/tab_contents/tab_contents.h" 53#include "content/common/notification_service.h" 54#include "content/common/page_transition_types.h" 55#include "grit/generated_resources.h" 56#include "grit/theme_resources.h" 57#include "net/base/net_util.h" 58#include "ui/base/dragdrop/gtk_dnd_util.h" 59#include "ui/base/l10n/l10n_util.h" 60#include "ui/base/resource/resource_bundle.h" 61#include "ui/gfx/canvas_skia_paint.h" 62#include "ui/gfx/font.h" 63#include "ui/gfx/gtk_util.h" 64#include "webkit/glue/window_open_disposition.h" 65 66namespace { 67 68// We are positioned with a little bit of extra space that we don't use now. 69const int kTopMargin = 1; 70const int kBottomMargin = 1; 71const int kLeftMargin = 1; 72const int kRightMargin = 1; 73// We draw a border on the top and bottom (but not on left or right). 74const int kBorderThickness = 1; 75 76// Left margin of first run bubble. 77const int kFirstRunBubbleLeftMargin = 8; 78// Extra vertical spacing for first run bubble. 79const int kFirstRunBubbleTopMargin = 5; 80// Spacing needed to align the bubble with the left side of the omnibox. 81const int kFirstRunBubbleLeftSpacing = 4; 82 83// The padding around the top, bottom, and sides of the location bar hbox. 84// We don't want to edit control's text to be right against the edge, 85// as well the tab to search box and other widgets need to have the padding on 86// top and bottom to avoid drawing larger than the location bar space. 87const int kHboxBorder = 2; 88 89// Padding between the elements in the bar. 90const int kInnerPadding = 2; 91 92// Padding between the right of the star and the edge of the URL entry. 93const int kStarRightPadding = 2; 94 95// Colors used to draw the EV certificate rounded bubble. 96const GdkColor kEvSecureTextColor = GDK_COLOR_RGB(0x07, 0x95, 0x00); 97const GdkColor kEvSecureBackgroundColor = GDK_COLOR_RGB(0xef, 0xfc, 0xef); 98const GdkColor kEvSecureBorderColor = GDK_COLOR_RGB(0x90, 0xc3, 0x90); 99 100// Colors used to draw the Tab to Search rounded bubble. 101const GdkColor kKeywordBackgroundColor = GDK_COLOR_RGB(0xf0, 0xf4, 0xfa); 102const GdkColor kKeywordBorderColor = GDK_COLOR_RGB(0xcb, 0xde, 0xf7); 103 104// Use weak gray for showing search and keyword hint text. 105const GdkColor kHintTextColor = GDK_COLOR_RGB(0x75, 0x75, 0x75); 106 107// Size of the rounding of the "Search site for:" box. 108const int kCornerSize = 3; 109 110// The time, in ms, that the content setting label is fully displayed, for the 111// cases where we animate it into and out of view. 112const int kContentSettingImageDisplayTime = 3200; 113// The time, in ms, of the animation (open and close). 114const int kContentSettingImageAnimationTime = 150; 115 116// Color of border of content setting area (icon/label). 117const GdkColor kContentSettingBorderColor = GDK_COLOR_RGB(0xe9, 0xb9, 0x66); 118// Colors for the background gradient. 119const double kContentSettingTopColor[] = { 0xff / 255.0, 120 0xf8 / 255.0, 121 0xd4 / 255.0 }; 122const double kContentSettingBottomColor[] = { 0xff / 255.0, 123 0xe6 / 255.0, 124 0xaf / 255.0 }; 125 126// If widget is visible, increment the int pointed to by count. 127// Suitible for use with gtk_container_foreach. 128void CountVisibleWidgets(GtkWidget* widget, gpointer count) { 129 if (GTK_WIDGET_VISIBLE(widget)) 130 *static_cast<int*>(count) += 1; 131} 132 133} // namespace 134 135//////////////////////////////////////////////////////////////////////////////// 136// LocationBarViewGtk 137 138// static 139const GdkColor LocationBarViewGtk::kBackgroundColor = 140 GDK_COLOR_RGB(255, 255, 255); 141 142LocationBarViewGtk::LocationBarViewGtk(Browser* browser) 143 : star_image_(NULL), 144 starred_(false), 145 site_type_alignment_(NULL), 146 site_type_event_box_(NULL), 147 location_icon_image_(NULL), 148 drag_icon_(NULL), 149 enable_location_drag_(false), 150 security_info_label_(NULL), 151 tab_to_search_alignment_(NULL), 152 tab_to_search_box_(NULL), 153 tab_to_search_full_label_(NULL), 154 tab_to_search_partial_label_(NULL), 155 tab_to_search_hint_(NULL), 156 tab_to_search_hint_leading_label_(NULL), 157 tab_to_search_hint_icon_(NULL), 158 tab_to_search_hint_trailing_label_(NULL), 159 profile_(NULL), 160 command_updater_(browser->command_updater()), 161 toolbar_model_(browser->toolbar_model()), 162 browser_(browser), 163 disposition_(CURRENT_TAB), 164 transition_(PageTransition::TYPED), 165 first_run_bubble_(this), 166 popup_window_mode_(false), 167 theme_service_(NULL), 168 hbox_width_(0), 169 entry_box_width_(0), 170 show_selected_keyword_(false), 171 show_keyword_hint_(false) { 172} 173 174LocationBarViewGtk::~LocationBarViewGtk() { 175 // All of our widgets should have be children of / owned by the alignment. 176 star_.Destroy(); 177 hbox_.Destroy(); 178 content_setting_hbox_.Destroy(); 179 page_action_hbox_.Destroy(); 180} 181 182void LocationBarViewGtk::Init(bool popup_window_mode) { 183 popup_window_mode_ = popup_window_mode; 184 185 // Create the widget first, so we can pass it to the AutocompleteEditViewGtk. 186 hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding)); 187 gtk_container_set_border_width(GTK_CONTAINER(hbox_.get()), kHboxBorder); 188 // We will paint for the alignment, to paint the background and border. 189 gtk_widget_set_app_paintable(hbox_.get(), TRUE); 190 // Redraw the whole location bar when it changes size (e.g., when toggling 191 // the home button on/off. 192 gtk_widget_set_redraw_on_allocate(hbox_.get(), TRUE); 193 194 // Now initialize the AutocompleteEditViewGtk. 195 location_entry_.reset(new AutocompleteEditViewGtk(this, 196 toolbar_model_, 197 profile_, 198 command_updater_, 199 popup_window_mode_, 200 hbox_.get())); 201 location_entry_->Init(); 202 203 g_signal_connect(hbox_.get(), "expose-event", 204 G_CALLBACK(&HandleExposeThunk), this); 205 206 BuildSiteTypeArea(); 207 208 // Put |tab_to_search_box_|, |location_entry_|, and |tab_to_search_hint_| into 209 // a sub hbox, so that we can make this part horizontally shrinkable without 210 // affecting other elements in the location bar. 211 entry_box_ = gtk_hbox_new(FALSE, kInnerPadding); 212 gtk_widget_show(entry_box_); 213 gtk_widget_set_size_request(entry_box_, 0, -1); 214 gtk_box_pack_start(GTK_BOX(hbox_.get()), entry_box_, TRUE, TRUE, 0); 215 216 // We need to adjust the visibility of the search hint widgets according to 217 // the horizontal space in the |entry_box_|. 218 g_signal_connect(entry_box_, "size-allocate", 219 G_CALLBACK(&OnEntryBoxSizeAllocateThunk), this); 220 221 // Tab to search (the keyword box on the left hand side). 222 tab_to_search_full_label_ = gtk_label_new(NULL); 223 tab_to_search_partial_label_ = gtk_label_new(NULL); 224 GtkWidget* tab_to_search_label_hbox = gtk_hbox_new(FALSE, 0); 225 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox), 226 tab_to_search_full_label_, FALSE, FALSE, 0); 227 gtk_box_pack_start(GTK_BOX(tab_to_search_label_hbox), 228 tab_to_search_partial_label_, FALSE, FALSE, 0); 229 GtkWidget* tab_to_search_hbox = gtk_hbox_new(FALSE, 0); 230 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 231 tab_to_search_magnifier_ = gtk_image_new_from_pixbuf( 232 rb.GetPixbufNamed(IDR_KEYWORD_SEARCH_MAGNIFIER)); 233 gtk_box_pack_start(GTK_BOX(tab_to_search_hbox), tab_to_search_magnifier_, 234 FALSE, FALSE, 0); 235 gtk_util::CenterWidgetInHBox(tab_to_search_hbox, tab_to_search_label_hbox, 236 false, 0); 237 238 // This creates a box around the keyword text with a border, background color, 239 // and padding around the text. 240 tab_to_search_box_ = gtk_util::CreateGtkBorderBin( 241 tab_to_search_hbox, NULL, 1, 1, 1, 3); 242 gtk_widget_set_name(tab_to_search_box_, "chrome-tab-to-search-box"); 243 gtk_util::ActAsRoundedWindow(tab_to_search_box_, kKeywordBorderColor, 244 kCornerSize, 245 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL); 246 247 // Put the event box in an alignment to get the padding correct. 248 tab_to_search_alignment_ = gtk_alignment_new(0, 0, 1, 1); 249 gtk_container_add(GTK_CONTAINER(tab_to_search_alignment_), 250 tab_to_search_box_); 251 gtk_box_pack_start(GTK_BOX(entry_box_), tab_to_search_alignment_, 252 FALSE, FALSE, 0); 253 254 // Show all children widgets of |tab_to_search_box_| initially, except 255 // |tab_to_search_partial_label_|. 256 gtk_widget_show_all(tab_to_search_box_); 257 gtk_widget_hide(tab_to_search_partial_label_); 258 259 location_entry_alignment_ = gtk_alignment_new(0.0, 0.0, 1.0, 1.0); 260 gtk_container_add(GTK_CONTAINER(location_entry_alignment_), 261 location_entry_->GetNativeView()); 262 gtk_box_pack_start(GTK_BOX(entry_box_), location_entry_alignment_, 263 TRUE, TRUE, 0); 264 265 // Tab to search notification (the hint on the right hand side). 266 tab_to_search_hint_ = gtk_hbox_new(FALSE, 0); 267 gtk_widget_set_name(tab_to_search_hint_, "chrome-tab-to-search-hint"); 268 tab_to_search_hint_leading_label_ = gtk_label_new(NULL); 269 gtk_widget_set_sensitive(tab_to_search_hint_leading_label_, FALSE); 270 tab_to_search_hint_icon_ = gtk_image_new_from_pixbuf( 271 rb.GetPixbufNamed(IDR_LOCATION_BAR_KEYWORD_HINT_TAB)); 272 tab_to_search_hint_trailing_label_ = gtk_label_new(NULL); 273 gtk_widget_set_sensitive(tab_to_search_hint_trailing_label_, FALSE); 274 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_), 275 tab_to_search_hint_leading_label_, 276 FALSE, FALSE, 0); 277 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_), 278 tab_to_search_hint_icon_, 279 FALSE, FALSE, 0); 280 gtk_box_pack_start(GTK_BOX(tab_to_search_hint_), 281 tab_to_search_hint_trailing_label_, 282 FALSE, FALSE, 0); 283 // Show all children widgets of |tab_to_search_hint_| initially. 284 gtk_widget_show_all(tab_to_search_hint_); 285 gtk_widget_hide(tab_to_search_hint_); 286 // tab_to_search_hint_ gets hidden initially in OnChanged. Hiding it here 287 // doesn't work, someone is probably calling show_all on our parent box. 288 gtk_box_pack_end(GTK_BOX(entry_box_), tab_to_search_hint_, FALSE, FALSE, 0); 289 290 // We don't show the star in popups, app windows, etc. 291 if (browser_defaults::bookmarks_enabled && !ShouldOnlyShowLocation()) { 292 CreateStarButton(); 293 gtk_box_pack_end(GTK_BOX(hbox_.get()), star_.get(), FALSE, FALSE, 0); 294 } 295 296 content_setting_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding + 1)); 297 gtk_widget_set_name(content_setting_hbox_.get(), 298 "chrome-content-setting-hbox"); 299 gtk_box_pack_end(GTK_BOX(hbox_.get()), content_setting_hbox_.get(), 300 FALSE, FALSE, 1); 301 302 for (int i = 0; i < CONTENT_SETTINGS_NUM_TYPES; ++i) { 303 ContentSettingImageViewGtk* content_setting_view = 304 new ContentSettingImageViewGtk( 305 static_cast<ContentSettingsType>(i), this, profile_); 306 content_setting_views_.push_back(content_setting_view); 307 gtk_box_pack_end(GTK_BOX(content_setting_hbox_.get()), 308 content_setting_view->widget(), FALSE, FALSE, 0); 309 } 310 311 page_action_hbox_.Own(gtk_hbox_new(FALSE, kInnerPadding)); 312 gtk_widget_set_name(page_action_hbox_.get(), 313 "chrome-page-action-hbox"); 314 gtk_box_pack_end(GTK_BOX(hbox_.get()), page_action_hbox_.get(), 315 FALSE, FALSE, 0); 316 317 // Now that we've created the widget hierarchy, connect to the main |hbox_|'s 318 // size-allocate so we can do proper resizing and eliding on 319 // |security_info_label_|. 320 g_signal_connect(hbox_.get(), "size-allocate", 321 G_CALLBACK(&OnHboxSizeAllocateThunk), this); 322 323 registrar_.Add(this, 324 NotificationType::BROWSER_THEME_CHANGED, 325 NotificationService::AllSources()); 326 edit_bookmarks_enabled_.Init(prefs::kEditBookmarksEnabled, 327 profile_->GetPrefs(), this); 328 329 theme_service_ = GtkThemeService::GetFrom(profile_); 330 theme_service_->InitThemesFor(this); 331} 332 333void LocationBarViewGtk::BuildSiteTypeArea() { 334 location_icon_image_ = gtk_image_new(); 335 gtk_widget_set_name(location_icon_image_, "chrome-location-icon"); 336 337 GtkWidget* icon_alignment = gtk_alignment_new(0, 0, 1, 1); 338 gtk_alignment_set_padding(GTK_ALIGNMENT(icon_alignment), 0, 0, 2, 0); 339 gtk_container_add(GTK_CONTAINER(icon_alignment), location_icon_image_); 340 gtk_widget_show_all(icon_alignment); 341 342 security_info_label_ = gtk_label_new(NULL); 343 gtk_label_set_ellipsize(GTK_LABEL(security_info_label_), 344 PANGO_ELLIPSIZE_MIDDLE); 345 gtk_widget_modify_fg(GTK_WIDGET(security_info_label_), GTK_STATE_NORMAL, 346 &kEvSecureTextColor); 347 gtk_widget_set_name(security_info_label_, 348 "chrome-location-bar-security-info-label"); 349 350 GtkWidget* site_type_hbox = gtk_hbox_new(FALSE, 1); 351 gtk_box_pack_start(GTK_BOX(site_type_hbox), icon_alignment, 352 FALSE, FALSE, 0); 353 gtk_box_pack_start(GTK_BOX(site_type_hbox), security_info_label_, 354 FALSE, FALSE, 2); 355 356 site_type_event_box_ = gtk_event_box_new(); 357 gtk_widget_modify_bg(site_type_event_box_, GTK_STATE_NORMAL, 358 &kEvSecureBackgroundColor); 359 g_signal_connect(site_type_event_box_, "drag-data-get", 360 G_CALLBACK(&OnIconDragDataThunk), this); 361 g_signal_connect(site_type_event_box_, "drag-begin", 362 G_CALLBACK(&OnIconDragBeginThunk), this); 363 g_signal_connect(site_type_event_box_, "drag-end", 364 G_CALLBACK(&OnIconDragEndThunk), this); 365 366 // Make the event box not visible so it does not paint a background. 367 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_), 368 FALSE); 369 gtk_widget_set_name(site_type_event_box_, 370 "chrome-location-icon-eventbox"); 371 gtk_container_add(GTK_CONTAINER(site_type_event_box_), 372 site_type_hbox); 373 374 // Put the event box in an alignment to get the padding correct. 375 site_type_alignment_ = gtk_alignment_new(0, 0, 1, 1); 376 gtk_container_add(GTK_CONTAINER(site_type_alignment_), 377 site_type_event_box_); 378 gtk_box_pack_start(GTK_BOX(hbox_.get()), site_type_alignment_, 379 FALSE, FALSE, 0); 380 381 gtk_widget_set_tooltip_text(location_icon_image_, 382 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str()); 383 384 g_signal_connect(site_type_event_box_, "button-release-event", 385 G_CALLBACK(&OnIconReleasedThunk), this); 386} 387 388void LocationBarViewGtk::SetSiteTypeDragSource() { 389 bool enable = !location_entry()->IsEditingOrEmpty(); 390 if (enable_location_drag_ == enable) 391 return; 392 enable_location_drag_ = enable; 393 394 if (!enable) { 395 gtk_drag_source_unset(site_type_event_box_); 396 return; 397 } 398 399 gtk_drag_source_set(site_type_event_box_, GDK_BUTTON1_MASK, 400 NULL, 0, GDK_ACTION_COPY); 401 ui::SetSourceTargetListFromCodeMask(site_type_event_box_, 402 ui::TEXT_PLAIN | 403 ui::TEXT_URI_LIST | 404 ui::CHROME_NAMED_URL); 405} 406 407void LocationBarViewGtk::SetProfile(Profile* profile) { 408 profile_ = profile; 409} 410 411TabContents* LocationBarViewGtk::GetTabContents() const { 412 return browser_->GetSelectedTabContents(); 413} 414 415void LocationBarViewGtk::SetPreviewEnabledPageAction( 416 ExtensionAction *page_action, 417 bool preview_enabled) { 418 DCHECK(page_action); 419 UpdatePageActions(); 420 for (ScopedVector<PageActionViewGtk>::iterator iter = 421 page_action_views_.begin(); iter != page_action_views_.end(); 422 ++iter) { 423 if ((*iter)->page_action() == page_action) { 424 (*iter)->set_preview_enabled(preview_enabled); 425 UpdatePageActions(); 426 return; 427 } 428 } 429} 430 431GtkWidget* LocationBarViewGtk::GetPageActionWidget( 432 ExtensionAction *page_action) { 433 DCHECK(page_action); 434 for (ScopedVector<PageActionViewGtk>::iterator iter = 435 page_action_views_.begin(); 436 iter != page_action_views_.end(); 437 ++iter) { 438 if ((*iter)->page_action() == page_action) 439 return (*iter)->widget(); 440 } 441 return NULL; 442} 443 444void LocationBarViewGtk::Update(const TabContents* contents) { 445 UpdateStarIcon(); 446 UpdateSiteTypeArea(); 447 UpdateContentSettingsIcons(); 448 UpdatePageActions(); 449 location_entry_->Update(contents); 450 // The security level (background color) could have changed, etc. 451 if (theme_service_->UseGtkTheme()) { 452 // In GTK mode, we need our parent to redraw, as it draws the text entry 453 // border. 454 gtk_widget_queue_draw(widget()->parent); 455 } else { 456 gtk_widget_queue_draw(widget()); 457 } 458} 459 460void LocationBarViewGtk::OnAutocompleteAccept(const GURL& url, 461 WindowOpenDisposition disposition, 462 PageTransition::Type transition, 463 const GURL& alternate_nav_url) { 464 if (url.is_valid()) { 465 location_input_ = UTF8ToWide(url.spec()); 466 disposition_ = disposition; 467 transition_ = transition; 468 469 if (command_updater_) { 470 if (!alternate_nav_url.is_valid()) { 471 command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); 472 } else { 473 AlternateNavURLFetcher* fetcher = 474 new AlternateNavURLFetcher(alternate_nav_url); 475 // The AlternateNavURLFetcher will listen for the pending navigation 476 // notification that will be issued as a result of the "open URL." It 477 // will automatically install itself into that navigation controller. 478 command_updater_->ExecuteCommand(IDC_OPEN_CURRENT_URL); 479 if (fetcher->state() == AlternateNavURLFetcher::NOT_STARTED) { 480 // I'm not sure this should be reachable, but I'm not also sure enough 481 // that it shouldn't to stick in a NOTREACHED(). In any case, this is 482 // harmless. 483 delete fetcher; 484 } else { 485 // The navigation controller will delete the fetcher. 486 } 487 } 488 } 489 } 490} 491 492void LocationBarViewGtk::OnChanged() { 493 UpdateSiteTypeArea(); 494 495 const string16 keyword(location_entry_->model()->keyword()); 496 const bool is_keyword_hint = location_entry_->model()->is_keyword_hint(); 497 show_selected_keyword_ = !keyword.empty() && !is_keyword_hint; 498 show_keyword_hint_ = !keyword.empty() && is_keyword_hint; 499 500 if (show_selected_keyword_) 501 SetKeywordLabel(keyword); 502 503 if (show_keyword_hint_) 504 SetKeywordHintLabel(keyword); 505 506 AdjustChildrenVisibility(); 507} 508 509void LocationBarViewGtk::OnSelectionBoundsChanged() { 510 NOTIMPLEMENTED(); 511} 512 513void LocationBarViewGtk::CreateStarButton() { 514 star_image_ = gtk_image_new(); 515 516 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1); 517 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment), 0, 0, 518 0, kStarRightPadding); 519 gtk_container_add(GTK_CONTAINER(alignment), star_image_); 520 521 star_.Own(gtk_event_box_new()); 522 gtk_event_box_set_visible_window(GTK_EVENT_BOX(star_.get()), FALSE); 523 gtk_container_add(GTK_CONTAINER(star_.get()), alignment); 524 gtk_widget_show_all(star_.get()); 525 ViewIDUtil::SetID(star_.get(), VIEW_ID_STAR_BUTTON); 526 527 gtk_widget_set_tooltip_text(star_.get(), 528 l10n_util::GetStringUTF8(IDS_TOOLTIP_STAR).c_str()); 529 g_signal_connect(star_.get(), "button-press-event", 530 G_CALLBACK(OnStarButtonPressThunk), this); 531} 532 533void LocationBarViewGtk::OnInputInProgress(bool in_progress) { 534 // This is identical to the Windows code, except that we don't proxy the call 535 // back through the Toolbar, and just access the model here. 536 // The edit should make sure we're only notified when something changes. 537 DCHECK(toolbar_model_->input_in_progress() != in_progress); 538 539 toolbar_model_->set_input_in_progress(in_progress); 540 Update(NULL); 541} 542 543void LocationBarViewGtk::OnKillFocus() { 544} 545 546void LocationBarViewGtk::OnSetFocus() { 547 AccessibilityTextBoxInfo info( 548 profile_, 549 l10n_util::GetStringUTF8(IDS_ACCNAME_LOCATION).c_str(), 550 false); 551 NotificationService::current()->Notify( 552 NotificationType::ACCESSIBILITY_CONTROL_FOCUSED, 553 Source<Profile>(profile_), 554 Details<AccessibilityTextBoxInfo>(&info)); 555 556 // Update the keyword and search hint states. 557 OnChanged(); 558} 559 560SkBitmap LocationBarViewGtk::GetFavicon() const { 561 return GetTabContents()->GetFavicon(); 562} 563 564string16 LocationBarViewGtk::GetTitle() const { 565 return GetTabContents()->GetTitle(); 566} 567 568InstantController* LocationBarViewGtk::GetInstant() { 569 return browser_->instant(); 570} 571 572TabContentsWrapper* LocationBarViewGtk::GetTabContentsWrapper() const { 573 return browser_->GetSelectedTabContentsWrapper(); 574} 575 576void LocationBarViewGtk::ShowFirstRunBubble(FirstRun::BubbleType bubble_type) { 577 // We need the browser window to be shown before we can show the bubble, but 578 // we get called before that's happened. 579 Task* task = first_run_bubble_.NewRunnableMethod( 580 &LocationBarViewGtk::ShowFirstRunBubbleInternal, bubble_type); 581 MessageLoop::current()->PostTask(FROM_HERE, task); 582} 583 584void LocationBarViewGtk::SetSuggestedText(const string16& text, 585 InstantCompleteBehavior behavior) { 586 location_entry_->model()->SetSuggestedText(text, behavior); 587} 588 589std::wstring LocationBarViewGtk::GetInputString() const { 590 return location_input_; 591} 592 593WindowOpenDisposition LocationBarViewGtk::GetWindowOpenDisposition() const { 594 return disposition_; 595} 596 597PageTransition::Type LocationBarViewGtk::GetPageTransition() const { 598 return transition_; 599} 600 601void LocationBarViewGtk::AcceptInput() { 602 location_entry_->model()->AcceptInput(CURRENT_TAB, false); 603} 604 605void LocationBarViewGtk::FocusLocation(bool select_all) { 606 location_entry_->SetFocus(); 607 if (select_all) 608 location_entry_->SelectAll(true); 609} 610 611void LocationBarViewGtk::FocusSearch() { 612 location_entry_->SetFocus(); 613 location_entry_->SetForcedQuery(); 614} 615 616void LocationBarViewGtk::UpdateContentSettingsIcons() { 617 TabContents* tab_contents = GetTabContents(); 618 bool any_visible = false; 619 for (ScopedVector<ContentSettingImageViewGtk>::iterator i( 620 content_setting_views_.begin()); 621 i != content_setting_views_.end(); ++i) { 622 (*i)->UpdateFromTabContents( 623 toolbar_model_->input_in_progress() ? NULL : tab_contents); 624 any_visible = (*i)->IsVisible() || any_visible; 625 } 626 627 // If there are no visible content things, hide the top level box so it 628 // doesn't mess with padding. 629 if (any_visible) 630 gtk_widget_show(content_setting_hbox_.get()); 631 else 632 gtk_widget_hide(content_setting_hbox_.get()); 633} 634 635void LocationBarViewGtk::UpdatePageActions() { 636 std::vector<ExtensionAction*> page_actions; 637 ExtensionService* service = profile_->GetExtensionService(); 638 if (!service) 639 return; 640 641 // Find all the page actions. 642 for (size_t i = 0; i < service->extensions()->size(); ++i) { 643 if (service->extensions()->at(i)->page_action()) 644 page_actions.push_back(service->extensions()->at(i)->page_action()); 645 } 646 647 // Initialize on the first call, or re-inialize if more extensions have been 648 // loaded or added after startup. 649 if (page_actions.size() != page_action_views_.size()) { 650 page_action_views_.reset(); // Delete the old views (if any). 651 652 for (size_t i = 0; i < page_actions.size(); ++i) { 653 page_action_views_.push_back( 654 new PageActionViewGtk(this, profile_, page_actions[i])); 655 gtk_box_pack_end(GTK_BOX(page_action_hbox_.get()), 656 page_action_views_[i]->widget(), FALSE, FALSE, 0); 657 } 658 NotificationService::current()->Notify( 659 NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED, 660 Source<LocationBar>(this), 661 NotificationService::NoDetails()); 662 } 663 664 TabContents* contents = GetTabContents(); 665 if (!page_action_views_.empty() && contents) { 666 GURL url = GURL(WideToUTF8(toolbar_model_->GetText())); 667 668 for (size_t i = 0; i < page_action_views_.size(); i++) { 669 page_action_views_[i]->UpdateVisibility( 670 toolbar_model_->input_in_progress() ? NULL : contents, url); 671 } 672 } 673 674 // If there are no visible page actions, hide the hbox too, so that it does 675 // not affect the padding in the location bar. 676 if (PageActionVisibleCount() && !ShouldOnlyShowLocation()) 677 gtk_widget_show(page_action_hbox_.get()); 678 else 679 gtk_widget_hide(page_action_hbox_.get()); 680} 681 682void LocationBarViewGtk::InvalidatePageActions() { 683 size_t count_before = page_action_views_.size(); 684 page_action_views_.reset(); 685 if (page_action_views_.size() != count_before) { 686 NotificationService::current()->Notify( 687 NotificationType::EXTENSION_PAGE_ACTION_COUNT_CHANGED, 688 Source<LocationBar>(this), 689 NotificationService::NoDetails()); 690 } 691} 692 693void LocationBarViewGtk::SaveStateToContents(TabContents* contents) { 694 location_entry_->SaveStateToTab(contents); 695} 696 697void LocationBarViewGtk::Revert() { 698 location_entry_->RevertAll(); 699} 700 701const AutocompleteEditView* LocationBarViewGtk::location_entry() const { 702 return location_entry_.get(); 703} 704 705AutocompleteEditView* LocationBarViewGtk::location_entry() { 706 return location_entry_.get(); 707} 708 709LocationBarTesting* LocationBarViewGtk::GetLocationBarForTesting() { 710 return this; 711} 712 713int LocationBarViewGtk::PageActionCount() { 714 return page_action_views_.size(); 715} 716 717int LocationBarViewGtk::PageActionVisibleCount() { 718 int count = 0; 719 gtk_container_foreach(GTK_CONTAINER(page_action_hbox_.get()), 720 CountVisibleWidgets, &count); 721 return count; 722} 723 724ExtensionAction* LocationBarViewGtk::GetPageAction(size_t index) { 725 if (index >= page_action_views_.size()) { 726 NOTREACHED(); 727 return NULL; 728 } 729 730 return page_action_views_[index]->page_action(); 731} 732 733ExtensionAction* LocationBarViewGtk::GetVisiblePageAction(size_t index) { 734 size_t visible_index = 0; 735 for (size_t i = 0; i < page_action_views_.size(); ++i) { 736 if (page_action_views_[i]->IsVisible()) { 737 if (index == visible_index++) 738 return page_action_views_[i]->page_action(); 739 } 740 } 741 742 NOTREACHED(); 743 return NULL; 744} 745 746void LocationBarViewGtk::TestPageActionPressed(size_t index) { 747 if (index >= page_action_views_.size()) { 748 NOTREACHED(); 749 return; 750 } 751 752 page_action_views_[index]->TestActivatePageAction(); 753} 754 755void LocationBarViewGtk::Observe(NotificationType type, 756 const NotificationSource& source, 757 const NotificationDetails& details) { 758 if (type.value == NotificationType::PREF_CHANGED) { 759 UpdateStarIcon(); 760 return; 761 } 762 763 DCHECK_EQ(type.value, NotificationType::BROWSER_THEME_CHANGED); 764 765 if (theme_service_->UseGtkTheme()) { 766 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, NULL); 767 768 GdkColor border_color = theme_service_->GetGdkColor( 769 ThemeService::COLOR_FRAME); 770 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, border_color); 771 772 gtk_util::SetLabelColor(tab_to_search_full_label_, NULL); 773 gtk_util::SetLabelColor(tab_to_search_partial_label_, NULL); 774 gtk_util::SetLabelColor(tab_to_search_hint_leading_label_, NULL); 775 gtk_util::SetLabelColor(tab_to_search_hint_trailing_label_, NULL); 776 777 gtk_util::UndoForceFontSize(security_info_label_); 778 gtk_util::UndoForceFontSize(tab_to_search_full_label_); 779 gtk_util::UndoForceFontSize(tab_to_search_partial_label_); 780 gtk_util::UndoForceFontSize(tab_to_search_hint_leading_label_); 781 gtk_util::UndoForceFontSize(tab_to_search_hint_trailing_label_); 782 783 gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_), 784 0, 0, 0, 0); 785 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_), 786 1, 1, 1, 0); 787 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_), 788 1, 1, 1, 0); 789 } else { 790 gtk_widget_modify_bg(tab_to_search_box_, GTK_STATE_NORMAL, 791 &kKeywordBackgroundColor); 792 gtk_util::SetRoundedWindowBorderColor(tab_to_search_box_, 793 kKeywordBorderColor); 794 795 gtk_util::SetLabelColor(tab_to_search_full_label_, >k_util::kGdkBlack); 796 gtk_util::SetLabelColor(tab_to_search_partial_label_, >k_util::kGdkBlack); 797 gtk_util::SetLabelColor(tab_to_search_hint_leading_label_, 798 &kHintTextColor); 799 gtk_util::SetLabelColor(tab_to_search_hint_trailing_label_, 800 &kHintTextColor); 801 802 // Until we switch to vector graphics, force the font size of labels. 803 // 12.1px = 9pt @ 96dpi 804 gtk_util::ForceFontSizePixels(security_info_label_, 12.1); 805 gtk_util::ForceFontSizePixels(tab_to_search_full_label_, 806 browser_defaults::kAutocompleteEditFontPixelSize); 807 gtk_util::ForceFontSizePixels(tab_to_search_partial_label_, 808 browser_defaults::kAutocompleteEditFontPixelSize); 809 gtk_util::ForceFontSizePixels(tab_to_search_hint_leading_label_, 810 browser_defaults::kAutocompleteEditFontPixelSize); 811 gtk_util::ForceFontSizePixels(tab_to_search_hint_trailing_label_, 812 browser_defaults::kAutocompleteEditFontPixelSize); 813 814 const int top_bottom = popup_window_mode_ ? kBorderThickness : 0; 815 gtk_alignment_set_padding(GTK_ALIGNMENT(location_entry_alignment_), 816 kTopMargin + kBorderThickness, 817 kBottomMargin + kBorderThickness, 818 top_bottom, top_bottom); 819 gtk_alignment_set_padding(GTK_ALIGNMENT(tab_to_search_alignment_), 820 1, 1, 0, 0); 821 gtk_alignment_set_padding(GTK_ALIGNMENT(site_type_alignment_), 822 1, 1, 0, 0); 823 } 824 825 UpdateStarIcon(); 826 UpdateSiteTypeArea(); 827 UpdateContentSettingsIcons(); 828} 829 830gboolean LocationBarViewGtk::HandleExpose(GtkWidget* widget, 831 GdkEventExpose* event) { 832 // If we're not using GTK theming, draw our own border over the edge pixels 833 // of the background. 834 if (!profile_ || 835 !GtkThemeService::GetFrom(profile_)->UseGtkTheme()) { 836 int left, center, right; 837 if (popup_window_mode_) { 838 left = right = IDR_LOCATIONBG_POPUPMODE_EDGE; 839 center = IDR_LOCATIONBG_POPUPMODE_CENTER; 840 } else { 841 left = IDR_LOCATIONBG_L; 842 center = IDR_LOCATIONBG_C; 843 right = IDR_LOCATIONBG_R; 844 } 845 846 NineBox background(left, center, right, 847 0, 0, 0, 0, 0, 0); 848 background.RenderToWidget(widget); 849 } 850 851 return FALSE; // Continue propagating the expose. 852} 853 854void LocationBarViewGtk::UpdateSiteTypeArea() { 855 // The icon is always visible except when the |tab_to_search_alignment_| is 856 // visible. 857 if (!location_entry_->model()->keyword().empty() && 858 !location_entry_->model()->is_keyword_hint()) { 859 gtk_widget_hide(site_type_area()); 860 return; 861 } 862 863 int resource_id = location_entry_->GetIcon(); 864 gtk_image_set_from_pixbuf(GTK_IMAGE(location_icon_image_), 865 theme_service_->GetPixbufNamed(resource_id)); 866 867 if (toolbar_model_->GetSecurityLevel() == ToolbarModel::EV_SECURE) { 868 if (!gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) { 869 // Fun fact: If wee try to make |site_type_event_box_| act as a 870 // rounded window while it doesn't have a visible window, GTK interprets 871 // this as a sign that it should paint the skyline texture into the 872 // omnibox. 873 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_), 874 TRUE); 875 876 gtk_util::ActAsRoundedWindow(site_type_event_box_, 877 kEvSecureBorderColor, 878 kCornerSize, 879 gtk_util::ROUNDED_ALL, 880 gtk_util::BORDER_ALL); 881 } 882 883 std::wstring info_text = toolbar_model_->GetEVCertName(); 884 gtk_label_set_text(GTK_LABEL(security_info_label_), 885 WideToUTF8(info_text).c_str()); 886 887 UpdateEVCertificateLabelSize(); 888 889 gtk_widget_show(GTK_WIDGET(security_info_label_)); 890 } else { 891 if (gtk_util::IsActingAsRoundedWindow(site_type_event_box_)) { 892 gtk_util::StopActingAsRoundedWindow(site_type_event_box_); 893 894 gtk_event_box_set_visible_window(GTK_EVENT_BOX(site_type_event_box_), 895 FALSE); 896 } 897 898 gtk_widget_hide(GTK_WIDGET(security_info_label_)); 899 } 900 901 if (location_entry()->IsEditingOrEmpty()) { 902 // Do not show the tooltip if the user has been editing the location 903 // bar, or the location bar is at the NTP. 904 gtk_widget_set_tooltip_text(location_icon_image_, ""); 905 } else { 906 gtk_widget_set_tooltip_text(location_icon_image_, 907 l10n_util::GetStringUTF8(IDS_TOOLTIP_LOCATION_ICON).c_str()); 908 } 909 910 gtk_widget_show(site_type_area()); 911 912 SetSiteTypeDragSource(); 913} 914 915void LocationBarViewGtk::UpdateEVCertificateLabelSize() { 916 // Figure out the width of the average character. 917 PangoLayout* layout = gtk_label_get_layout(GTK_LABEL(security_info_label_)); 918 PangoContext* context = pango_layout_get_context(layout); 919 PangoFontMetrics* metrics = pango_context_get_metrics( 920 context, 921 gtk_widget_get_style(security_info_label_)->font_desc, 922 pango_context_get_language(context)); 923 int char_width = 924 pango_font_metrics_get_approximate_char_width(metrics) / PANGO_SCALE; 925 926 // The EV label should never take up more than half the hbox. We try to 927 // correct our inaccurate measurement units ("the average character width") 928 // by dividing more than an even 2. 929 int text_area = security_info_label_->allocation.width + 930 entry_box_->allocation.width; 931 int max_chars = static_cast<int>(static_cast<float>(text_area) / 932 static_cast<float>(char_width) / 2.75); 933 // Don't let the label be smaller than 10 characters so that the country 934 // code is always visible. 935 gtk_label_set_max_width_chars(GTK_LABEL(security_info_label_), 936 std::max(10, max_chars)); 937 938 pango_font_metrics_unref(metrics); 939} 940 941void LocationBarViewGtk::SetKeywordLabel(const string16& keyword) { 942 if (keyword.empty()) 943 return; 944 945 DCHECK(profile_); 946 if (!profile_->GetTemplateURLModel()) 947 return; 948 949 bool is_extension_keyword; 950 const string16 short_name = profile_->GetTemplateURLModel()-> 951 GetKeywordShortName(keyword, &is_extension_keyword); 952 int message_id = is_extension_keyword ? 953 IDS_OMNIBOX_EXTENSION_KEYWORD_TEXT : IDS_OMNIBOX_KEYWORD_TEXT; 954 string16 full_name = l10n_util::GetStringFUTF16(message_id, 955 short_name); 956 string16 partial_name = l10n_util::GetStringFUTF16( 957 message_id, 958 WideToUTF16Hack( 959 location_bar_util::CalculateMinString(UTF16ToWideHack(short_name)))); 960 gtk_label_set_text(GTK_LABEL(tab_to_search_full_label_), 961 UTF16ToUTF8(full_name).c_str()); 962 gtk_label_set_text(GTK_LABEL(tab_to_search_partial_label_), 963 UTF16ToUTF8(partial_name).c_str()); 964 965 if (last_keyword_ != keyword) { 966 last_keyword_ = keyword; 967 968 if (is_extension_keyword) { 969 const TemplateURL* template_url = 970 profile_->GetTemplateURLModel()->GetTemplateURLForKeyword(keyword); 971 const SkBitmap& bitmap = profile_->GetExtensionService()-> 972 GetOmniboxIcon(template_url->GetExtensionId()); 973 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap); 974 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), pixbuf); 975 g_object_unref(pixbuf); 976 } else { 977 ResourceBundle& rb = ResourceBundle::GetSharedInstance(); 978 gtk_image_set_from_pixbuf(GTK_IMAGE(tab_to_search_magnifier_), 979 rb.GetPixbufNamed(IDR_OMNIBOX_SEARCH)); 980 } 981 } 982} 983 984void LocationBarViewGtk::SetKeywordHintLabel(const string16& keyword) { 985 if (keyword.empty()) 986 return; 987 988 DCHECK(profile_); 989 if (!profile_->GetTemplateURLModel()) 990 return; 991 992 bool is_extension_keyword; 993 const string16 short_name = profile_->GetTemplateURLModel()-> 994 GetKeywordShortName(keyword, &is_extension_keyword); 995 int message_id = is_extension_keyword ? 996 IDS_OMNIBOX_EXTENSION_KEYWORD_HINT : IDS_OMNIBOX_KEYWORD_HINT; 997 std::vector<size_t> content_param_offsets; 998 const string16 keyword_hint = l10n_util::GetStringFUTF16( 999 message_id, 1000 string16(), 1001 short_name, 1002 &content_param_offsets); 1003 if (content_param_offsets.size() != 2) { 1004 // See comments on an identical NOTREACHED() in search_provider.cc. 1005 NOTREACHED(); 1006 return; 1007 } 1008 1009 std::string leading(UTF16ToUTF8( 1010 keyword_hint.substr(0, content_param_offsets.front()))); 1011 std::string trailing(UTF16ToUTF8( 1012 keyword_hint.substr(content_param_offsets.front()))); 1013 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_leading_label_), 1014 leading.c_str()); 1015 gtk_label_set_text(GTK_LABEL(tab_to_search_hint_trailing_label_), 1016 trailing.c_str()); 1017} 1018 1019void LocationBarViewGtk::ShowFirstRunBubbleInternal( 1020 FirstRun::BubbleType bubble_type) { 1021 if (!location_entry_.get() || !widget()->window) 1022 return; 1023 1024 gfx::Rect bounds = gtk_util::WidgetBounds(location_icon_image_); 1025 bounds.set_x(bounds.x() + kFirstRunBubbleLeftSpacing); 1026 1027 FirstRunBubble::Show(profile_, location_icon_image_, bounds, bubble_type); 1028} 1029 1030gboolean LocationBarViewGtk::OnIconReleased(GtkWidget* sender, 1031 GdkEventButton* event) { 1032 TabContents* tab = GetTabContents(); 1033 1034 if (event->button == 1) { 1035 // Do not show page info if the user has been editing the location 1036 // bar, or the location bar is at the NTP. 1037 if (location_entry()->IsEditingOrEmpty()) 1038 return FALSE; 1039 1040 // (0,0) event coordinates indicates that the release came at the end of 1041 // a drag. 1042 if (event->x == 0 && event->y == 0) 1043 return FALSE; 1044 1045 NavigationEntry* nav_entry = tab->controller().GetActiveEntry(); 1046 if (!nav_entry) { 1047 NOTREACHED(); 1048 return FALSE; 1049 } 1050 tab->ShowPageInfo(nav_entry->url(), nav_entry->ssl(), true); 1051 return TRUE; 1052 } else if (event->button == 2) { 1053 // When the user middle clicks on the location icon, try to open the 1054 // contents of the PRIMARY selection in the current tab. 1055 // If the click was outside our bounds, do nothing. 1056 if (!gtk_util::WidgetBounds(sender).Contains( 1057 gfx::Point(event->x, event->y))) { 1058 return FALSE; 1059 } 1060 1061 GURL url; 1062 if (!gtk_util::URLFromPrimarySelection(profile_, &url)) 1063 return FALSE; 1064 1065 tab->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::TYPED); 1066 return TRUE; 1067 } 1068 1069 return FALSE; 1070} 1071 1072void LocationBarViewGtk::OnIconDragData(GtkWidget* sender, 1073 GdkDragContext* context, 1074 GtkSelectionData* data, 1075 guint info, guint time) { 1076 TabContents* tab = GetTabContents(); 1077 if (!tab) 1078 return; 1079 ui::WriteURLWithName(data, tab->GetURL(), tab->GetTitle(), info); 1080} 1081 1082void LocationBarViewGtk::OnIconDragBegin(GtkWidget* sender, 1083 GdkDragContext* context) { 1084 SkBitmap favicon = GetFavicon(); 1085 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&favicon); 1086 if (!pixbuf) 1087 return; 1088 drag_icon_ = bookmark_utils::GetDragRepresentation(pixbuf, 1089 GetTitle(), theme_service_); 1090 g_object_unref(pixbuf); 1091 gtk_drag_set_icon_widget(context, drag_icon_, 0, 0); 1092} 1093 1094void LocationBarViewGtk::OnIconDragEnd(GtkWidget* sender, 1095 GdkDragContext* context) { 1096 DCHECK(drag_icon_); 1097 gtk_widget_destroy(drag_icon_); 1098 drag_icon_ = NULL; 1099} 1100 1101void LocationBarViewGtk::OnHboxSizeAllocate(GtkWidget* sender, 1102 GtkAllocation* allocation) { 1103 if (hbox_width_ != allocation->width) { 1104 hbox_width_ = allocation->width; 1105 UpdateEVCertificateLabelSize(); 1106 } 1107} 1108 1109void LocationBarViewGtk::OnEntryBoxSizeAllocate(GtkWidget* sender, 1110 GtkAllocation* allocation) { 1111 if (entry_box_width_ != allocation->width) { 1112 entry_box_width_ = allocation->width; 1113 AdjustChildrenVisibility(); 1114 } 1115} 1116 1117gboolean LocationBarViewGtk::OnStarButtonPress(GtkWidget* widget, 1118 GdkEventButton* event) { 1119 browser_->ExecuteCommand(IDC_BOOKMARK_PAGE); 1120 return FALSE; 1121} 1122 1123void LocationBarViewGtk::ShowStarBubble(const GURL& url, 1124 bool newly_bookmarked) { 1125 if (!star_.get()) 1126 return; 1127 1128 BookmarkBubbleGtk::Show(star_.get(), profile_, url, newly_bookmarked); 1129} 1130 1131void LocationBarViewGtk::SetStarred(bool starred) { 1132 if (starred == starred_) 1133 return; 1134 1135 starred_ = starred; 1136 UpdateStarIcon(); 1137} 1138 1139void LocationBarViewGtk::UpdateStarIcon() { 1140 if (!star_.get()) 1141 return; 1142 bool star_enabled = !toolbar_model_->input_in_progress() && 1143 edit_bookmarks_enabled_.GetValue(); 1144 command_updater_->UpdateCommandEnabled(IDC_BOOKMARK_PAGE, star_enabled); 1145 if (star_enabled) { 1146 gtk_widget_show_all(star_.get()); 1147 gtk_image_set_from_pixbuf(GTK_IMAGE(star_image_), 1148 theme_service_->GetPixbufNamed( 1149 starred_ ? IDR_STAR_LIT : IDR_STAR)); 1150 } else { 1151 gtk_widget_hide_all(star_.get()); 1152 } 1153} 1154 1155bool LocationBarViewGtk::ShouldOnlyShowLocation() { 1156 return browser_->type() != Browser::TYPE_NORMAL; 1157} 1158 1159void LocationBarViewGtk::AdjustChildrenVisibility() { 1160 int text_width = location_entry_->TextWidth(); 1161 int available_width = entry_box_width_ - text_width - kInnerPadding; 1162 1163 // Only one of |tab_to_search_alignment_| and |tab_to_search_hint_| can be 1164 // visible at the same time. 1165 if (!show_selected_keyword_ && GTK_WIDGET_VISIBLE(tab_to_search_alignment_)) 1166 gtk_widget_hide(tab_to_search_alignment_); 1167 else if (!show_keyword_hint_ && GTK_WIDGET_VISIBLE(tab_to_search_hint_)) 1168 gtk_widget_hide(tab_to_search_hint_); 1169 1170 if (show_selected_keyword_) { 1171 GtkRequisition box, full_label, partial_label; 1172 gtk_widget_size_request(tab_to_search_box_, &box); 1173 gtk_widget_size_request(tab_to_search_full_label_, &full_label); 1174 gtk_widget_size_request(tab_to_search_partial_label_, &partial_label); 1175 int full_partial_width_diff = full_label.width - partial_label.width; 1176 int full_box_width; 1177 int partial_box_width; 1178 if (GTK_WIDGET_VISIBLE(tab_to_search_full_label_)) { 1179 full_box_width = box.width; 1180 partial_box_width = full_box_width - full_partial_width_diff; 1181 } else { 1182 partial_box_width = box.width; 1183 full_box_width = partial_box_width + full_partial_width_diff; 1184 } 1185 1186 if (partial_box_width >= entry_box_width_ - kInnerPadding) { 1187 gtk_widget_hide(tab_to_search_alignment_); 1188 } else if (full_box_width >= available_width) { 1189 gtk_widget_hide(tab_to_search_full_label_); 1190 gtk_widget_show(tab_to_search_partial_label_); 1191 gtk_widget_show(tab_to_search_alignment_); 1192 } else if (full_box_width < available_width) { 1193 gtk_widget_hide(tab_to_search_partial_label_); 1194 gtk_widget_show(tab_to_search_full_label_); 1195 gtk_widget_show(tab_to_search_alignment_); 1196 } 1197 } else if (show_keyword_hint_) { 1198 GtkRequisition leading, icon, trailing; 1199 gtk_widget_size_request(tab_to_search_hint_leading_label_, &leading); 1200 gtk_widget_size_request(tab_to_search_hint_icon_, &icon); 1201 gtk_widget_size_request(tab_to_search_hint_trailing_label_, &trailing); 1202 int full_width = leading.width + icon.width + trailing.width; 1203 1204 if (icon.width >= entry_box_width_ - kInnerPadding) { 1205 gtk_widget_hide(tab_to_search_hint_); 1206 } else if (full_width >= available_width) { 1207 gtk_widget_hide(tab_to_search_hint_leading_label_); 1208 gtk_widget_hide(tab_to_search_hint_trailing_label_); 1209 gtk_widget_show(tab_to_search_hint_); 1210 } else if (full_width < available_width) { 1211 gtk_widget_show(tab_to_search_hint_leading_label_); 1212 gtk_widget_show(tab_to_search_hint_trailing_label_); 1213 gtk_widget_show(tab_to_search_hint_); 1214 } 1215 } 1216} 1217 1218//////////////////////////////////////////////////////////////////////////////// 1219// LocationBarViewGtk::ContentSettingImageViewGtk 1220LocationBarViewGtk::ContentSettingImageViewGtk::ContentSettingImageViewGtk( 1221 ContentSettingsType content_type, 1222 const LocationBarViewGtk* parent, 1223 Profile* profile) 1224 : content_setting_image_model_( 1225 ContentSettingImageModel::CreateContentSettingImageModel( 1226 content_type)), 1227 alignment_(gtk_alignment_new(0, 0, 1, 1)), 1228 event_box_(gtk_event_box_new()), 1229 hbox_(gtk_hbox_new(FALSE, kInnerPadding)), 1230 image_(gtk_image_new()), 1231 label_(gtk_label_new(NULL)), 1232 parent_(parent), 1233 profile_(profile), 1234 info_bubble_(NULL), 1235 animation_(this), 1236 method_factory_(this) { 1237 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment_.get()), 1, 1, 0, 0); 1238 gtk_container_add(GTK_CONTAINER(alignment_.get()), event_box_.get()); 1239 1240 // Make the event box not visible so it does not paint a background. 1241 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); 1242 g_signal_connect(event_box_.get(), "button-press-event", 1243 G_CALLBACK(&OnButtonPressedThunk), this); 1244 g_signal_connect(event_box_.get(), "expose-event", 1245 G_CALLBACK(&OnExposeThunk), this); 1246 1247 gtk_widget_set_no_show_all(label_.get(), TRUE); 1248 gtk_label_set_line_wrap(GTK_LABEL(label_.get()), FALSE); 1249 1250 gtk_box_pack_start(GTK_BOX(hbox_), image_.get(), FALSE, FALSE, 0); 1251 gtk_box_pack_start(GTK_BOX(hbox_), label_.get(), FALSE, FALSE, 0); 1252 1253 // The +1 accounts for the pixel that is devoted to drawing the border. 1254 gtk_container_set_border_width(GTK_CONTAINER(hbox_), kHboxBorder + 1); 1255 1256 gtk_container_add(GTK_CONTAINER(event_box_.get()), hbox_); 1257 gtk_widget_hide(widget()); 1258 1259 animation_.SetSlideDuration(kContentSettingImageAnimationTime); 1260} 1261 1262LocationBarViewGtk::ContentSettingImageViewGtk::~ContentSettingImageViewGtk() { 1263 image_.Destroy(); 1264 label_.Destroy(); 1265 event_box_.Destroy(); 1266 alignment_.Destroy(); 1267 1268 if (info_bubble_) 1269 info_bubble_->Close(); 1270} 1271 1272void LocationBarViewGtk::ContentSettingImageViewGtk::UpdateFromTabContents( 1273 TabContents* tab_contents) { 1274 content_setting_image_model_->UpdateFromTabContents(tab_contents); 1275 if (!content_setting_image_model_->is_visible()) { 1276 gtk_widget_hide(widget()); 1277 return; 1278 } 1279 1280 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), 1281 GtkThemeService::GetFrom(profile_)->GetPixbufNamed( 1282 content_setting_image_model_->get_icon())); 1283 1284 gtk_widget_set_tooltip_text(widget(), 1285 content_setting_image_model_->get_tooltip().c_str()); 1286 gtk_widget_show_all(widget()); 1287 1288 TabSpecificContentSettings* content_settings = tab_contents ? 1289 tab_contents->GetTabSpecificContentSettings() : NULL; 1290 if (!content_settings || content_settings->IsBlockageIndicated( 1291 content_setting_image_model_->get_content_settings_type())) 1292 return; 1293 1294 // The content blockage was not yet indicated to the user. Start indication 1295 // animation and clear "not yet shown" flag. 1296 content_settings->SetBlockageHasBeenIndicated( 1297 content_setting_image_model_->get_content_settings_type()); 1298 1299 int label_string_id = 1300 content_setting_image_model_->explanatory_string_id(); 1301 // Check if the animation is enabled and if the string for animation is 1302 // available. If there's no string for the content type, we don't animate. 1303 if (CommandLine::ForCurrentProcess()->HasSwitch( 1304 switches::kDisableBlockContentAnimation) || !label_string_id) 1305 return; 1306 1307 gtk_label_set_text(GTK_LABEL(label_.get()), 1308 l10n_util::GetStringUTF8(label_string_id).c_str()); 1309 StartAnimating(); 1310} 1311 1312void LocationBarViewGtk::ContentSettingImageViewGtk::StartAnimating() { 1313 if (animation_.IsShowing() || animation_.IsClosing()) 1314 return; 1315 1316 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), TRUE); 1317 gtk_util::ActAsRoundedWindow(event_box_.get(), kContentSettingBorderColor, 1318 kCornerSize, 1319 gtk_util::ROUNDED_ALL, gtk_util::BORDER_ALL); 1320 1321 gtk_widget_set_size_request(label_.get(), -1, -1); 1322 gtk_widget_size_request(label_.get(), &label_req_); 1323 gtk_widget_set_size_request(label_.get(), 0, -1); 1324 gtk_widget_show(label_.get()); 1325 1326 animation_.Show(); 1327} 1328 1329void LocationBarViewGtk::ContentSettingImageViewGtk::CloseAnimation() { 1330 animation_.Hide(); 1331} 1332 1333void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationProgressed( 1334 const ui::Animation* animation) { 1335 gtk_widget_set_size_request( 1336 label_.get(), 1337 animation->GetCurrentValue() * label_req_.width, 1338 -1); 1339} 1340 1341void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationEnded( 1342 const ui::Animation* animation) { 1343 if (animation_.IsShowing()) { 1344 MessageLoop::current()->PostDelayedTask(FROM_HERE, 1345 method_factory_.NewRunnableMethod( 1346 &ContentSettingImageViewGtk::CloseAnimation), 1347 kContentSettingImageDisplayTime); 1348 } else { 1349 gtk_widget_hide(label_.get()); 1350 gtk_util::StopActingAsRoundedWindow(event_box_.get()); 1351 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); 1352 } 1353} 1354 1355void LocationBarViewGtk::ContentSettingImageViewGtk::AnimationCanceled( 1356 const ui::Animation* animation) { 1357} 1358 1359gboolean LocationBarViewGtk::ContentSettingImageViewGtk::OnButtonPressed( 1360 GtkWidget* sender, GdkEvent* event) { 1361 TabContents* tab_contents = parent_->GetTabContents(); 1362 if (!tab_contents) 1363 return TRUE; 1364 const ContentSettingsType content_settings_type = 1365 content_setting_image_model_->get_content_settings_type(); 1366 if (content_settings_type == CONTENT_SETTINGS_TYPE_PRERENDER) 1367 return TRUE; 1368 GURL url = tab_contents->GetURL(); 1369 std::wstring display_host; 1370 net::AppendFormattedHost(url, 1371 UTF8ToWide(profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)), 1372 &display_host, 1373 NULL, NULL); 1374 1375 info_bubble_ = new ContentSettingBubbleGtk( 1376 sender, this, 1377 ContentSettingBubbleModel::CreateContentSettingBubbleModel( 1378 tab_contents, profile_, content_settings_type), 1379 profile_, tab_contents); 1380 return TRUE; 1381} 1382 1383gboolean LocationBarViewGtk::ContentSettingImageViewGtk::OnExpose( 1384 GtkWidget* sender, GdkEventExpose* event) { 1385 if (!(animation_.IsShowing() || animation_.IsClosing())) 1386 return FALSE; 1387 1388 const int height = sender->allocation.height; 1389 1390 cairo_t* cr = gdk_cairo_create(GDK_DRAWABLE(sender->window)); 1391 gdk_cairo_rectangle(cr, &event->area); 1392 cairo_clip(cr); 1393 1394 cairo_pattern_t* pattern = cairo_pattern_create_linear(0, 0, 0, height); 1395 1396 cairo_pattern_add_color_stop_rgb(pattern, 0.0, 1397 kContentSettingTopColor[0], 1398 kContentSettingTopColor[1], 1399 kContentSettingTopColor[2]); 1400 cairo_pattern_add_color_stop_rgb(pattern, 1.0, 1401 kContentSettingBottomColor[0], 1402 kContentSettingBottomColor[1], 1403 kContentSettingBottomColor[2]); 1404 cairo_set_source(cr, pattern); 1405 cairo_paint(cr); 1406 cairo_pattern_destroy(pattern); 1407 cairo_destroy(cr); 1408 1409 return FALSE; 1410} 1411 1412void LocationBarViewGtk::ContentSettingImageViewGtk::InfoBubbleClosing( 1413 InfoBubbleGtk* info_bubble, 1414 bool closed_by_escape) { 1415 info_bubble_ = NULL; 1416} 1417 1418//////////////////////////////////////////////////////////////////////////////// 1419// LocationBarViewGtk::PageActionViewGtk 1420 1421LocationBarViewGtk::PageActionViewGtk::PageActionViewGtk( 1422 LocationBarViewGtk* owner, Profile* profile, 1423 ExtensionAction* page_action) 1424 : owner_(NULL), 1425 profile_(profile), 1426 page_action_(page_action), 1427 last_icon_pixbuf_(NULL), 1428 tracker_(this), 1429 preview_enabled_(false) { 1430 event_box_.Own(gtk_event_box_new()); 1431 gtk_widget_set_size_request(event_box_.get(), 1432 Extension::kPageActionIconMaxSize, 1433 Extension::kPageActionIconMaxSize); 1434 1435 // Make the event box not visible so it does not paint a background. 1436 gtk_event_box_set_visible_window(GTK_EVENT_BOX(event_box_.get()), FALSE); 1437 g_signal_connect(event_box_.get(), "button-press-event", 1438 G_CALLBACK(&OnButtonPressedThunk), this); 1439 g_signal_connect_after(event_box_.get(), "expose-event", 1440 G_CALLBACK(OnExposeEventThunk), this); 1441 1442 image_.Own(gtk_image_new()); 1443 gtk_container_add(GTK_CONTAINER(event_box_.get()), image_.get()); 1444 1445 const Extension* extension = profile->GetExtensionService()-> 1446 GetExtensionById(page_action->extension_id(), false); 1447 DCHECK(extension); 1448 1449 // Load all the icons declared in the manifest. This is the contents of the 1450 // icons array, plus the default_icon property, if any. 1451 std::vector<std::string> icon_paths(*page_action->icon_paths()); 1452 if (!page_action_->default_icon_path().empty()) 1453 icon_paths.push_back(page_action_->default_icon_path()); 1454 1455 for (std::vector<std::string>::iterator iter = icon_paths.begin(); 1456 iter != icon_paths.end(); ++iter) { 1457 tracker_.LoadImage(extension, extension->GetResource(*iter), 1458 gfx::Size(Extension::kPageActionIconMaxSize, 1459 Extension::kPageActionIconMaxSize), 1460 ImageLoadingTracker::DONT_CACHE); 1461 } 1462 1463 // We set the owner last of all so that we can determine whether we are in 1464 // the process of initializing this class or not. 1465 owner_ = owner; 1466} 1467 1468LocationBarViewGtk::PageActionViewGtk::~PageActionViewGtk() { 1469 image_.Destroy(); 1470 event_box_.Destroy(); 1471 for (PixbufMap::iterator iter = pixbufs_.begin(); iter != pixbufs_.end(); 1472 ++iter) { 1473 g_object_unref(iter->second); 1474 } 1475 if (last_icon_pixbuf_) 1476 g_object_unref(last_icon_pixbuf_); 1477} 1478 1479void LocationBarViewGtk::PageActionViewGtk::UpdateVisibility( 1480 TabContents* contents, const GURL& url) { 1481 // Save this off so we can pass it back to the extension when the action gets 1482 // executed. See PageActionImageView::OnMousePressed. 1483 current_tab_id_ = contents ? ExtensionTabUtil::GetTabId(contents) : -1; 1484 current_url_ = url; 1485 1486 bool visible = contents && 1487 (preview_enabled_ || page_action_->GetIsVisible(current_tab_id_)); 1488 if (visible) { 1489 // Set the tooltip. 1490 gtk_widget_set_tooltip_text(event_box_.get(), 1491 page_action_->GetTitle(current_tab_id_).c_str()); 1492 1493 // Set the image. 1494 // It can come from three places. In descending order of priority: 1495 // - The developer can set it dynamically by path or bitmap. It will be in 1496 // page_action_->GetIcon(). 1497 // - The developer can set it dyanmically by index. It will be in 1498 // page_action_->GetIconIndex(). 1499 // - It can be set in the manifest by path. It will be in page_action_-> 1500 // default_icon_path(). 1501 1502 // First look for a dynamically set bitmap. 1503 SkBitmap icon = page_action_->GetIcon(current_tab_id_); 1504 GdkPixbuf* pixbuf = NULL; 1505 if (!icon.isNull()) { 1506 if (icon.pixelRef() != last_icon_skbitmap_.pixelRef()) { 1507 if (last_icon_pixbuf_) 1508 g_object_unref(last_icon_pixbuf_); 1509 last_icon_skbitmap_ = icon; 1510 last_icon_pixbuf_ = gfx::GdkPixbufFromSkBitmap(&icon); 1511 } 1512 DCHECK(last_icon_pixbuf_); 1513 pixbuf = last_icon_pixbuf_; 1514 } else { 1515 // Otherwise look for a dynamically set index, or fall back to the 1516 // default path. 1517 int icon_index = page_action_->GetIconIndex(current_tab_id_); 1518 std::string icon_path = (icon_index < 0) ? 1519 page_action_->default_icon_path() : 1520 page_action_->icon_paths()->at(icon_index); 1521 if (!icon_path.empty()) { 1522 PixbufMap::iterator iter = pixbufs_.find(icon_path); 1523 if (iter != pixbufs_.end()) 1524 pixbuf = iter->second; 1525 } 1526 } 1527 // The pixbuf might not be loaded yet. 1528 if (pixbuf) 1529 gtk_image_set_from_pixbuf(GTK_IMAGE(image_.get()), pixbuf); 1530 } 1531 1532 bool old_visible = IsVisible(); 1533 if (visible) 1534 gtk_widget_show_all(event_box_.get()); 1535 else 1536 gtk_widget_hide_all(event_box_.get()); 1537 1538 if (visible != old_visible) { 1539 NotificationService::current()->Notify( 1540 NotificationType::EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED, 1541 Source<ExtensionAction>(page_action_), 1542 Details<TabContents>(contents)); 1543 } 1544} 1545 1546void LocationBarViewGtk::PageActionViewGtk::OnImageLoaded( 1547 SkBitmap* image, const ExtensionResource& resource, int index) { 1548 // We loaded icons()->size() icons, plus one extra if the page action had 1549 // a default icon. 1550 int total_icons = static_cast<int>(page_action_->icon_paths()->size()); 1551 if (!page_action_->default_icon_path().empty()) 1552 total_icons++; 1553 DCHECK(index < total_icons); 1554 1555 // Map the index of the loaded image back to its name. If we ever get an 1556 // index greater than the number of icons, it must be the default icon. 1557 if (image) { 1558 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(image); 1559 if (index < static_cast<int>(page_action_->icon_paths()->size())) 1560 pixbufs_[page_action_->icon_paths()->at(index)] = pixbuf; 1561 else 1562 pixbufs_[page_action_->default_icon_path()] = pixbuf; 1563 } 1564 1565 // If we have no owner, that means this class is still being constructed and 1566 // we should not UpdatePageActions, since it leads to the PageActions being 1567 // destroyed again and new ones recreated (causing an infinite loop). 1568 if (owner_) 1569 owner_->UpdatePageActions(); 1570} 1571 1572void LocationBarViewGtk::PageActionViewGtk::TestActivatePageAction() { 1573 GdkEventButton event = {}; 1574 event.button = 1; 1575 OnButtonPressed(widget(), &event); 1576} 1577 1578void LocationBarViewGtk::PageActionViewGtk::InspectPopup( 1579 ExtensionAction* action) { 1580 ShowPopup(true); 1581} 1582 1583bool LocationBarViewGtk::PageActionViewGtk::ShowPopup(bool devtools) { 1584 if (!page_action_->HasPopup(current_tab_id_)) 1585 return false; 1586 1587 ExtensionPopupGtk::Show( 1588 page_action_->GetPopupUrl(current_tab_id_), 1589 owner_->browser_, 1590 event_box_.get(), 1591 devtools); 1592 return true; 1593} 1594 1595gboolean LocationBarViewGtk::PageActionViewGtk::OnButtonPressed( 1596 GtkWidget* sender, 1597 GdkEventButton* event) { 1598 if (event->button != 3) { 1599 if (!ShowPopup(false)) { 1600 ExtensionService* service = profile_->GetExtensionService(); 1601 service->browser_event_router()->PageActionExecuted( 1602 profile_, 1603 page_action_->extension_id(), 1604 page_action_->id(), 1605 current_tab_id_, 1606 current_url_.spec(), 1607 event->button); 1608 } 1609 } else { 1610 const Extension* extension = profile_->GetExtensionService()-> 1611 GetExtensionById(page_action()->extension_id(), false); 1612 1613 if (extension->ShowConfigureContextMenus()) { 1614 context_menu_model_ = 1615 new ExtensionContextMenuModel(extension, owner_->browser_, this); 1616 context_menu_.reset( 1617 new MenuGtk(NULL, context_menu_model_.get())); 1618 context_menu_->PopupForWidget(sender, event->button, event->time); 1619 } 1620 } 1621 1622 return TRUE; 1623} 1624 1625gboolean LocationBarViewGtk::PageActionViewGtk::OnExposeEvent( 1626 GtkWidget* widget, GdkEventExpose* event) { 1627 TabContents* contents = owner_->GetTabContents(); 1628 if (!contents) 1629 return FALSE; 1630 1631 int tab_id = ExtensionTabUtil::GetTabId(contents); 1632 if (tab_id < 0) 1633 return FALSE; 1634 1635 std::string badge_text = page_action_->GetBadgeText(tab_id); 1636 if (badge_text.empty()) 1637 return FALSE; 1638 1639 gfx::CanvasSkiaPaint canvas(event, false); 1640 gfx::Rect bounding_rect(widget->allocation); 1641 page_action_->PaintBadge(&canvas, bounding_rect, tab_id); 1642 return FALSE; 1643} 1644