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 <gtk/gtk.h> 6 7 #include "build/build_config.h" 8 9 #include "base/i18n/rtl.h" 10 #include "base/utf_string_conversions.h" 11 #include "chrome/browser/google/google_util.h" 12 #include "chrome/browser/page_info_model.h" 13 #include "chrome/browser/page_info_window.h" 14 #include "chrome/browser/ui/browser_list.h" 15 #include "chrome/browser/ui/gtk/browser_toolbar_gtk.h" 16 #include "chrome/browser/ui/gtk/browser_window_gtk.h" 17 #include "chrome/browser/ui/gtk/gtk_chrome_link_button.h" 18 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 19 #include "chrome/browser/ui/gtk/gtk_util.h" 20 #include "chrome/browser/ui/gtk/info_bubble_gtk.h" 21 #include "chrome/browser/ui/gtk/location_bar_view_gtk.h" 22 #include "chrome/common/url_constants.h" 23 #include "content/browser/certificate_viewer.h" 24 #include "content/common/notification_observer.h" 25 #include "content/common/notification_registrar.h" 26 #include "content/common/notification_service.h" 27 #include "googleurl/src/gurl.h" 28 #include "grit/generated_resources.h" 29 #include "grit/locale_settings.h" 30 #include "ui/base/l10n/l10n_util.h" 31 32 class Profile; 33 34 namespace { 35 36 class PageInfoBubbleGtk : public PageInfoModel::PageInfoModelObserver, 37 public InfoBubbleGtkDelegate, 38 public NotificationObserver { 39 public: 40 PageInfoBubbleGtk(gfx::NativeWindow parent, 41 Profile* profile, 42 const GURL& url, 43 const NavigationEntry::SSLStatus& ssl, 44 bool show_history); 45 virtual ~PageInfoBubbleGtk(); 46 47 // PageInfoModelObserver implementation: 48 virtual void ModelChanged(); 49 50 // NotificationObserver implementation: 51 virtual void Observe(NotificationType type, 52 const NotificationSource& source, 53 const NotificationDetails& details); 54 55 // InfoBubbleGtkDelegate implementation: 56 virtual void InfoBubbleClosing(InfoBubbleGtk* info_bubble, 57 bool closed_by_escape); 58 59 private: 60 // Layouts the different sections retrieved from the model. 61 void InitContents(); 62 63 // Returns a widget that contains the UI for the passed |section|. 64 GtkWidget* CreateSection(const PageInfoModel::SectionInfo& section); 65 66 // Link button callbacks. 67 CHROMEGTK_CALLBACK_0(PageInfoBubbleGtk, void, OnViewCertLinkClicked); 68 CHROMEGTK_CALLBACK_0(PageInfoBubbleGtk, void, OnHelpLinkClicked); 69 70 // The model containing the different sections to display. 71 PageInfoModel model_; 72 73 // The url for this dialog. Should be unique among active dialogs. 74 GURL url_; 75 76 // The id of the certificate for this page. 77 int cert_id_; 78 79 // Parent window. 80 GtkWindow* parent_; 81 82 // The virtual box containing the sections. 83 GtkWidget* contents_; 84 85 // The widget relative to which we are positioned. 86 GtkWidget* anchor_; 87 88 // Provides colors and stuff. 89 GtkThemeService* theme_service_; 90 91 // The various elements in the interface we keep track of for theme changes. 92 std::vector<GtkWidget*> labels_; 93 std::vector<GtkWidget*> links_; 94 95 InfoBubbleGtk* bubble_; 96 97 NotificationRegistrar registrar_; 98 99 DISALLOW_COPY_AND_ASSIGN(PageInfoBubbleGtk); 100 }; 101 102 PageInfoBubbleGtk::PageInfoBubbleGtk(gfx::NativeWindow parent, 103 Profile* profile, 104 const GURL& url, 105 const NavigationEntry::SSLStatus& ssl, 106 bool show_history) 107 : ALLOW_THIS_IN_INITIALIZER_LIST(model_(profile, url, ssl, 108 show_history, this)), 109 url_(url), 110 cert_id_(ssl.cert_id()), 111 parent_(parent), 112 contents_(NULL), 113 theme_service_(GtkThemeService::GetFrom(profile)) { 114 BrowserWindowGtk* browser_window = 115 BrowserWindowGtk::GetBrowserWindowForNativeWindow(parent); 116 117 anchor_ = browser_window-> 118 GetToolbar()->GetLocationBarView()->location_icon_widget(); 119 120 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, 121 NotificationService::AllSources()); 122 123 InitContents(); 124 125 InfoBubbleGtk::ArrowLocationGtk arrow_location = base::i18n::IsRTL() ? 126 InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT : 127 InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT; 128 bubble_ = InfoBubbleGtk::Show(anchor_, 129 NULL, // |rect| 130 contents_, 131 arrow_location, 132 true, // |match_system_theme| 133 true, // |grab_input| 134 theme_service_, 135 this); // |delegate| 136 if (!bubble_) { 137 NOTREACHED(); 138 return; 139 } 140 } 141 142 PageInfoBubbleGtk::~PageInfoBubbleGtk() { 143 } 144 145 void PageInfoBubbleGtk::ModelChanged() { 146 InitContents(); 147 } 148 149 void PageInfoBubbleGtk::Observe(NotificationType type, 150 const NotificationSource& source, 151 const NotificationDetails& details) { 152 DCHECK(type == NotificationType::BROWSER_THEME_CHANGED); 153 154 for (std::vector<GtkWidget*>::iterator it = links_.begin(); 155 it != links_.end(); ++it) { 156 gtk_chrome_link_button_set_use_gtk_theme( 157 GTK_CHROME_LINK_BUTTON(*it), 158 theme_service_->UseGtkTheme()); 159 } 160 161 if (theme_service_->UseGtkTheme()) { 162 for (std::vector<GtkWidget*>::iterator it = labels_.begin(); 163 it != labels_.end(); ++it) { 164 gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, NULL); 165 } 166 } else { 167 for (std::vector<GtkWidget*>::iterator it = labels_.begin(); 168 it != labels_.end(); ++it) { 169 gtk_widget_modify_fg(*it, GTK_STATE_NORMAL, >k_util::kGdkBlack); 170 } 171 } 172 } 173 174 void PageInfoBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble, 175 bool closed_by_escape) { 176 delete this; 177 } 178 179 void PageInfoBubbleGtk::InitContents() { 180 if (!contents_) { 181 contents_ = gtk_vbox_new(FALSE, gtk_util::kContentAreaSpacing); 182 gtk_container_set_border_width(GTK_CONTAINER(contents_), 183 gtk_util::kContentAreaBorder); 184 } else { 185 labels_.clear(); 186 links_.clear(); 187 gtk_util::RemoveAllChildren(contents_); 188 } 189 190 for (int i = 0; i < model_.GetSectionCount(); i++) { 191 gtk_box_pack_start(GTK_BOX(contents_), 192 CreateSection(model_.GetSectionInfo(i)), 193 FALSE, FALSE, 0); 194 gtk_box_pack_start(GTK_BOX(contents_), 195 gtk_hseparator_new(), 196 FALSE, FALSE, 0); 197 } 198 199 GtkWidget* help_link = gtk_chrome_link_button_new( 200 l10n_util::GetStringUTF8(IDS_PAGE_INFO_HELP_CENTER_LINK).c_str()); 201 links_.push_back(help_link); 202 GtkWidget* help_link_hbox = gtk_hbox_new(FALSE, 0); 203 // Stick it in an hbox so it doesn't expand to the whole width. 204 gtk_box_pack_start(GTK_BOX(help_link_hbox), help_link, FALSE, FALSE, 0); 205 gtk_box_pack_start(GTK_BOX(contents_), help_link_hbox, FALSE, FALSE, 0); 206 g_signal_connect(help_link, "clicked", 207 G_CALLBACK(OnHelpLinkClickedThunk), this); 208 209 theme_service_->InitThemesFor(this); 210 gtk_widget_show_all(contents_); 211 } 212 213 GtkWidget* PageInfoBubbleGtk::CreateSection( 214 const PageInfoModel::SectionInfo& section) { 215 GtkWidget* section_box = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); 216 217 GdkPixbuf* pixbuf = *model_.GetIconImage(section.icon_id); 218 if (pixbuf) { 219 GtkWidget* image = gtk_image_new_from_pixbuf(pixbuf); 220 gtk_box_pack_start(GTK_BOX(section_box), image, FALSE, FALSE, 0); 221 gtk_misc_set_alignment(GTK_MISC(image), 0, 0); 222 } 223 224 GtkWidget* vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); 225 gtk_box_pack_start(GTK_BOX(section_box), vbox, TRUE, TRUE, 0); 226 227 if (!section.headline.empty()) { 228 GtkWidget* label = gtk_label_new(UTF16ToUTF8(section.headline).c_str()); 229 gtk_label_set_selectable(GTK_LABEL(label), TRUE); 230 labels_.push_back(label); 231 PangoAttrList* attributes = pango_attr_list_new(); 232 pango_attr_list_insert(attributes, 233 pango_attr_weight_new(PANGO_WEIGHT_BOLD)); 234 gtk_label_set_attributes(GTK_LABEL(label), attributes); 235 pango_attr_list_unref(attributes); 236 gtk_util::SetLabelWidth(label, 400); 237 // Allow linebreaking in the middle of words if necessary, so that extremely 238 // long hostnames (longer than one line) will still be completely shown. 239 gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR); 240 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 241 } 242 GtkWidget* label = gtk_label_new(UTF16ToUTF8(section.description).c_str()); 243 gtk_label_set_selectable(GTK_LABEL(label), TRUE); 244 labels_.push_back(label); 245 gtk_util::SetLabelWidth(label, 400); 246 // Allow linebreaking in the middle of words if necessary, so that extremely 247 // long hostnames (longer than one line) will still be completely shown. 248 gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD_CHAR); 249 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); 250 251 if (section.type == PageInfoModel::SECTION_INFO_IDENTITY && cert_id_ > 0) { 252 GtkWidget* view_cert_link = gtk_chrome_link_button_new( 253 l10n_util::GetStringUTF8(IDS_PAGEINFO_CERT_INFO_BUTTON).c_str()); 254 links_.push_back(view_cert_link); 255 GtkWidget* cert_link_hbox = gtk_hbox_new(FALSE, 0); 256 // Stick it in an hbox so it doesn't expand to the whole width. 257 gtk_box_pack_start(GTK_BOX(cert_link_hbox), view_cert_link, 258 FALSE, FALSE, 0); 259 gtk_box_pack_start(GTK_BOX(vbox), cert_link_hbox, FALSE, FALSE, 0); 260 g_signal_connect(view_cert_link, "clicked", 261 G_CALLBACK(OnViewCertLinkClickedThunk), this); 262 } 263 264 return section_box; 265 } 266 267 void PageInfoBubbleGtk::OnViewCertLinkClicked(GtkWidget* widget) { 268 ShowCertificateViewerByID(GTK_WINDOW(parent_), cert_id_); 269 bubble_->Close(); 270 } 271 272 void PageInfoBubbleGtk::OnHelpLinkClicked(GtkWidget* widget) { 273 GURL url = google_util::AppendGoogleLocaleParam( 274 GURL(chrome::kPageInfoHelpCenterURL)); 275 Browser* browser = BrowserList::GetLastActive(); 276 browser->OpenURL(url, GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); 277 bubble_->Close(); 278 } 279 280 } // namespace 281 282 namespace browser { 283 284 void ShowPageInfoBubble(gfx::NativeWindow parent, 285 Profile* profile, 286 const GURL& url, 287 const NavigationEntry::SSLStatus& ssl, 288 bool show_history) { 289 new PageInfoBubbleGtk(parent, profile, url, ssl, show_history); 290 } 291 292 } // namespace browser 293