| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 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/gtk/content_setting_bubble_gtk.h" | |
| 6 | |
| 7 #include <set> | |
| 8 #include <string> | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "app/l10n_util.h" | |
| 12 #include "app/text_elider.h" | |
| 13 #include "base/i18n/rtl.h" | |
| 14 #include "base/utf_string_conversions.h" | |
| 15 #include "chrome/browser/blocked_content_container.h" | |
| 16 #include "chrome/browser/content_settings/host_content_settings_map.h" | |
| 17 #include "chrome/browser/content_setting_bubble_model.h" | |
| 18 #include "chrome/browser/gtk/gtk_chrome_link_button.h" | |
| 19 #include "chrome/browser/gtk/gtk_theme_provider.h" | |
| 20 #include "chrome/browser/gtk/gtk_util.h" | |
| 21 #include "chrome/browser/gtk/options/content_settings_window_gtk.h" | |
| 22 #include "chrome/browser/profiles/profile.h" | |
| 23 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 24 #include "chrome/common/content_settings.h" | |
| 25 #include "chrome/common/notification_source.h" | |
| 26 #include "chrome/common/notification_type.h" | |
| 27 #include "gfx/gtk_util.h" | |
| 28 #include "grit/app_resources.h" | |
| 29 #include "grit/generated_resources.h" | |
| 30 #include "webkit/plugins/npapi/plugin_list.h" | |
| 31 | |
| 32 namespace { | |
| 33 | |
| 34 // Padding between content and edge of info bubble. | |
| 35 const int kContentBorder = 7; | |
| 36 | |
| 37 // The maximum width of a title entry in the content box. We elide anything | |
| 38 // longer than this. | |
| 39 const int kMaxLinkPixelSize = 500; | |
| 40 | |
| 41 std::string BuildElidedText(const std::string& input) { | |
| 42 return UTF16ToUTF8(gfx::ElideText( | |
| 43 UTF8ToUTF16(input), | |
| 44 gfx::Font(), | |
| 45 kMaxLinkPixelSize, | |
| 46 false)); | |
| 47 } | |
| 48 | |
| 49 } // namespace | |
| 50 | |
| 51 ContentSettingBubbleGtk::ContentSettingBubbleGtk( | |
| 52 GtkWidget* anchor, | |
| 53 InfoBubbleGtkDelegate* delegate, | |
| 54 ContentSettingBubbleModel* content_setting_bubble_model, | |
| 55 Profile* profile, | |
| 56 TabContents* tab_contents) | |
| 57 : anchor_(anchor), | |
| 58 profile_(profile), | |
| 59 tab_contents_(tab_contents), | |
| 60 delegate_(delegate), | |
| 61 content_setting_bubble_model_(content_setting_bubble_model), | |
| 62 info_bubble_(NULL) { | |
| 63 registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, | |
| 64 Source<TabContents>(tab_contents)); | |
| 65 BuildBubble(); | |
| 66 } | |
| 67 | |
| 68 ContentSettingBubbleGtk::~ContentSettingBubbleGtk() { | |
| 69 } | |
| 70 | |
| 71 void ContentSettingBubbleGtk::Close() { | |
| 72 if (info_bubble_) | |
| 73 info_bubble_->Close(); | |
| 74 } | |
| 75 | |
| 76 void ContentSettingBubbleGtk::InfoBubbleClosing(InfoBubbleGtk* info_bubble, | |
| 77 bool closed_by_escape) { | |
| 78 delegate_->InfoBubbleClosing(info_bubble, closed_by_escape); | |
| 79 delete this; | |
| 80 } | |
| 81 | |
| 82 void ContentSettingBubbleGtk::Observe(NotificationType type, | |
| 83 const NotificationSource& source, | |
| 84 const NotificationDetails& details) { | |
| 85 DCHECK(type == NotificationType::TAB_CONTENTS_DESTROYED); | |
| 86 DCHECK(source == Source<TabContents>(tab_contents_)); | |
| 87 tab_contents_ = NULL; | |
| 88 } | |
| 89 | |
| 90 void ContentSettingBubbleGtk::BuildBubble() { | |
| 91 GtkThemeProvider* theme_provider = GtkThemeProvider::GetFrom(profile_); | |
| 92 | |
| 93 GtkWidget* bubble_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); | |
| 94 gtk_container_set_border_width(GTK_CONTAINER(bubble_content), kContentBorder); | |
| 95 | |
| 96 const ContentSettingBubbleModel::BubbleContent& content = | |
| 97 content_setting_bubble_model_->bubble_content(); | |
| 98 if (!content.title.empty()) { | |
| 99 // Add the content label. | |
| 100 GtkWidget* label = gtk_label_new(content.title.c_str()); | |
| 101 gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); | |
| 102 gtk_box_pack_start(GTK_BOX(bubble_content), label, FALSE, FALSE, 0); | |
| 103 } | |
| 104 | |
| 105 const std::set<std::string>& plugins = content.resource_identifiers; | |
| 106 if (!plugins.empty()) { | |
| 107 GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); | |
| 108 | |
| 109 for (std::set<std::string>::const_iterator it = plugins.begin(); | |
| 110 it != plugins.end(); ++it) { | |
| 111 std::string name = UTF16ToUTF8( | |
| 112 webkit::npapi::PluginList::Singleton()->GetPluginGroupName(*it)); | |
| 113 if (name.empty()) | |
| 114 name = *it; | |
| 115 | |
| 116 GtkWidget* label = gtk_label_new(BuildElidedText(name).c_str()); | |
| 117 GtkWidget* label_box = gtk_hbox_new(FALSE, 0); | |
| 118 gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0); | |
| 119 | |
| 120 gtk_box_pack_start(GTK_BOX(list_content), | |
| 121 label_box, | |
| 122 FALSE, FALSE, 0); | |
| 123 } | |
| 124 gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE, | |
| 125 gtk_util::kControlSpacing); | |
| 126 } | |
| 127 | |
| 128 if (content_setting_bubble_model_->content_type() == | |
| 129 CONTENT_SETTINGS_TYPE_POPUPS) { | |
| 130 const std::vector<ContentSettingBubbleModel::PopupItem>& popup_items = | |
| 131 content.popup_items; | |
| 132 GtkWidget* table = gtk_table_new(popup_items.size(), 2, FALSE); | |
| 133 int row = 0; | |
| 134 for (std::vector<ContentSettingBubbleModel::PopupItem>::const_iterator | |
| 135 i(popup_items.begin()); i != popup_items.end(); ++i, ++row) { | |
| 136 GtkWidget* image = gtk_image_new(); | |
| 137 if (!i->bitmap.empty()) { | |
| 138 GdkPixbuf* icon_pixbuf = gfx::GdkPixbufFromSkBitmap(&i->bitmap); | |
| 139 gtk_image_set_from_pixbuf(GTK_IMAGE(image), icon_pixbuf); | |
| 140 g_object_unref(icon_pixbuf); | |
| 141 | |
| 142 // We stuff the image in an event box so we can trap mouse clicks on the | |
| 143 // image (and launch the popup). | |
| 144 GtkWidget* event_box = gtk_event_box_new(); | |
| 145 gtk_container_add(GTK_CONTAINER(event_box), image); | |
| 146 | |
| 147 popup_icons_[event_box] = i -popup_items.begin(); | |
| 148 g_signal_connect(event_box, "button_press_event", | |
| 149 G_CALLBACK(OnPopupIconButtonPressThunk), this); | |
| 150 gtk_table_attach(GTK_TABLE(table), event_box, 0, 1, row, row + 1, | |
| 151 GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2, | |
| 152 gtk_util::kControlSpacing / 2); | |
| 153 } | |
| 154 | |
| 155 GtkWidget* button = gtk_chrome_link_button_new( | |
| 156 BuildElidedText(i->title).c_str()); | |
| 157 popup_links_[button] = i -popup_items.begin(); | |
| 158 g_signal_connect(button, "clicked", G_CALLBACK(OnPopupLinkClickedThunk), | |
| 159 this); | |
| 160 gtk_table_attach(GTK_TABLE(table), button, 1, 2, row, row + 1, | |
| 161 GTK_FILL, GTK_FILL, gtk_util::kControlSpacing / 2, | |
| 162 gtk_util::kControlSpacing / 2); | |
| 163 } | |
| 164 | |
| 165 gtk_box_pack_start(GTK_BOX(bubble_content), table, FALSE, FALSE, 0); | |
| 166 } | |
| 167 | |
| 168 if (content_setting_bubble_model_->content_type() != | |
| 169 CONTENT_SETTINGS_TYPE_COOKIES) { | |
| 170 const ContentSettingBubbleModel::RadioGroup& radio_group = | |
| 171 content.radio_group; | |
| 172 for (ContentSettingBubbleModel::RadioItems::const_iterator i = | |
| 173 radio_group.radio_items.begin(); | |
| 174 i != radio_group.radio_items.end(); ++i) { | |
| 175 std::string elided = BuildElidedText(*i); | |
| 176 GtkWidget* radio = | |
| 177 radio_group_gtk_.empty() ? | |
| 178 gtk_radio_button_new_with_label(NULL, elided.c_str()) : | |
| 179 gtk_radio_button_new_with_label_from_widget( | |
| 180 GTK_RADIO_BUTTON(radio_group_gtk_[0]), | |
| 181 elided.c_str()); | |
| 182 gtk_box_pack_start(GTK_BOX(bubble_content), radio, FALSE, FALSE, 0); | |
| 183 if (i - radio_group.radio_items.begin() == radio_group.default_item) { | |
| 184 // We must set the default value before we attach the signal handlers | |
| 185 // or pain occurs. | |
| 186 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio), TRUE); | |
| 187 } | |
| 188 radio_group_gtk_.push_back(radio); | |
| 189 } | |
| 190 for (std::vector<GtkWidget*>::const_iterator i = radio_group_gtk_.begin(); | |
| 191 i != radio_group_gtk_.end(); ++i) { | |
| 192 // We can attach signal handlers now that all defaults are set. | |
| 193 g_signal_connect(*i, "toggled", G_CALLBACK(OnRadioToggledThunk), this); | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 for (std::vector<ContentSettingBubbleModel::DomainList>::const_iterator i = | |
| 198 content.domain_lists.begin(); | |
| 199 i != content.domain_lists.end(); ++i) { | |
| 200 // Put each list into its own vbox to allow spacing between lists. | |
| 201 GtkWidget* list_content = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); | |
| 202 | |
| 203 GtkWidget* label = gtk_label_new(BuildElidedText(i->title).c_str()); | |
| 204 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); | |
| 205 GtkWidget* label_box = gtk_hbox_new(FALSE, 0); | |
| 206 gtk_box_pack_start(GTK_BOX(label_box), label, FALSE, FALSE, 0); | |
| 207 gtk_box_pack_start(GTK_BOX(list_content), label_box, FALSE, FALSE, 0); | |
| 208 for (std::set<std::string>::const_iterator j = i->hosts.begin(); | |
| 209 j != i->hosts.end(); ++j) { | |
| 210 gtk_box_pack_start(GTK_BOX(list_content), | |
| 211 gtk_util::IndentWidget(gtk_util::CreateBoldLabel(*j)), | |
| 212 FALSE, FALSE, 0); | |
| 213 } | |
| 214 gtk_box_pack_start(GTK_BOX(bubble_content), list_content, FALSE, FALSE, | |
| 215 gtk_util::kControlSpacing); | |
| 216 } | |
| 217 | |
| 218 if (!content.custom_link.empty()) { | |
| 219 GtkWidget* custom_link_box = gtk_hbox_new(FALSE, 0); | |
| 220 GtkWidget* custom_link = NULL; | |
| 221 if (content.custom_link_enabled) { | |
| 222 custom_link = gtk_chrome_link_button_new(content.custom_link.c_str()); | |
| 223 g_signal_connect(custom_link, "clicked", | |
| 224 G_CALLBACK(OnCustomLinkClickedThunk), this); | |
| 225 } else { | |
| 226 custom_link = gtk_label_new(content.custom_link.c_str()); | |
| 227 gtk_misc_set_alignment(GTK_MISC(custom_link), 0, 0.5); | |
| 228 } | |
| 229 DCHECK(custom_link); | |
| 230 gtk_box_pack_start(GTK_BOX(custom_link_box), custom_link, FALSE, FALSE, 0); | |
| 231 gtk_box_pack_start(GTK_BOX(bubble_content), custom_link_box, | |
| 232 FALSE, FALSE, 0); | |
| 233 } | |
| 234 | |
| 235 gtk_box_pack_start(GTK_BOX(bubble_content), gtk_hseparator_new(), | |
| 236 FALSE, FALSE, 0); | |
| 237 | |
| 238 GtkWidget* bottom_box = gtk_hbox_new(FALSE, 0); | |
| 239 | |
| 240 GtkWidget* manage_link = | |
| 241 gtk_chrome_link_button_new(content.manage_link.c_str()); | |
| 242 g_signal_connect(manage_link, "clicked", G_CALLBACK(OnManageLinkClickedThunk), | |
| 243 this); | |
| 244 gtk_box_pack_start(GTK_BOX(bottom_box), manage_link, FALSE, FALSE, 0); | |
| 245 | |
| 246 GtkWidget* button = gtk_button_new_with_label( | |
| 247 l10n_util::GetStringUTF8(IDS_DONE).c_str()); | |
| 248 g_signal_connect(button, "clicked", G_CALLBACK(OnCloseButtonClickedThunk), | |
| 249 this); | |
| 250 gtk_box_pack_end(GTK_BOX(bottom_box), button, FALSE, FALSE, 0); | |
| 251 | |
| 252 gtk_box_pack_start(GTK_BOX(bubble_content), bottom_box, FALSE, FALSE, 0); | |
| 253 gtk_widget_grab_focus(bottom_box); | |
| 254 gtk_widget_grab_focus(button); | |
| 255 | |
| 256 InfoBubbleGtk::ArrowLocationGtk arrow_location = | |
| 257 !base::i18n::IsRTL() ? | |
| 258 InfoBubbleGtk::ARROW_LOCATION_TOP_RIGHT : | |
| 259 InfoBubbleGtk::ARROW_LOCATION_TOP_LEFT; | |
| 260 info_bubble_ = InfoBubbleGtk::Show( | |
| 261 anchor_, | |
| 262 NULL, | |
| 263 bubble_content, | |
| 264 arrow_location, | |
| 265 true, // match_system_theme | |
| 266 true, // grab_input | |
| 267 theme_provider, | |
| 268 this); | |
| 269 } | |
| 270 | |
| 271 void ContentSettingBubbleGtk::OnPopupIconButtonPress( | |
| 272 GtkWidget* icon_event_box, | |
| 273 GdkEventButton* event) { | |
| 274 PopupMap::iterator i(popup_icons_.find(icon_event_box)); | |
| 275 DCHECK(i != popup_icons_.end()); | |
| 276 content_setting_bubble_model_->OnPopupClicked(i->second); | |
| 277 // The views interface implicitly closes because of the launching of a new | |
| 278 // window; we need to do that explicitly. | |
| 279 Close(); | |
| 280 } | |
| 281 | |
| 282 void ContentSettingBubbleGtk::OnPopupLinkClicked(GtkWidget* button) { | |
| 283 PopupMap::iterator i(popup_links_.find(button)); | |
| 284 DCHECK(i != popup_links_.end()); | |
| 285 content_setting_bubble_model_->OnPopupClicked(i->second); | |
| 286 // The views interface implicitly closes because of the launching of a new | |
| 287 // window; we need to do that explicitly. | |
| 288 Close(); | |
| 289 } | |
| 290 | |
| 291 void ContentSettingBubbleGtk::OnRadioToggled(GtkWidget* widget) { | |
| 292 for (ContentSettingBubbleGtk::RadioGroupGtk::const_iterator i = | |
| 293 radio_group_gtk_.begin(); | |
| 294 i != radio_group_gtk_.end(); ++i) { | |
| 295 if (widget == *i) { | |
| 296 content_setting_bubble_model_->OnRadioClicked( | |
| 297 i - radio_group_gtk_.begin()); | |
| 298 return; | |
| 299 } | |
| 300 } | |
| 301 NOTREACHED() << "unknown radio toggled"; | |
| 302 } | |
| 303 | |
| 304 void ContentSettingBubbleGtk::OnCloseButtonClicked(GtkWidget *button) { | |
| 305 Close(); | |
| 306 } | |
| 307 | |
| 308 void ContentSettingBubbleGtk::OnCustomLinkClicked(GtkWidget* button) { | |
| 309 content_setting_bubble_model_->OnCustomLinkClicked(); | |
| 310 Close(); | |
| 311 } | |
| 312 | |
| 313 void ContentSettingBubbleGtk::OnManageLinkClicked(GtkWidget* button) { | |
| 314 content_setting_bubble_model_->OnManageLinkClicked(); | |
| 315 Close(); | |
| 316 } | |
| OLD | NEW |