| OLD | NEW |
| (Empty) |
| 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/options/cookies_view.h" | |
| 6 | |
| 7 #include <gdk/gdkkeysyms.h> | |
| 8 #include <set> | |
| 9 #include <string> | |
| 10 | |
| 11 #include "base/message_loop.h" | |
| 12 #include "base/string_util.h" | |
| 13 #include "chrome/browser/cookies_tree_model.h" | |
| 14 #include "chrome/browser/profiles/profile.h" | |
| 15 #include "chrome/browser/ui/gtk/gtk_util.h" | |
| 16 #include "grit/generated_resources.h" | |
| 17 #include "grit/locale_settings.h" | |
| 18 #include "ui/base/l10n/l10n_util.h" | |
| 19 #include "ui/gfx/gtk_util.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 // Initial size for dialog. | |
| 24 const int kDialogDefaultWidth = 550; | |
| 25 const int kDialogDefaultHeight = 550; | |
| 26 | |
| 27 // Delay after entering filter text before filtering occurs. | |
| 28 const int kSearchFilterDelayMs = 500; | |
| 29 | |
| 30 // Response ids for our custom buttons. | |
| 31 enum { | |
| 32 RESPONSE_REMOVE = 1, | |
| 33 RESPONSE_REMOVE_ALL | |
| 34 }; | |
| 35 | |
| 36 // The currently open cookie manager, if any. | |
| 37 CookiesView* instance_ = NULL; | |
| 38 | |
| 39 } // namespace | |
| 40 | |
| 41 CookiesView::~CookiesView() { | |
| 42 if (destroy_dialog_in_destructor_) | |
| 43 gtk_widget_destroy(dialog_); | |
| 44 } | |
| 45 | |
| 46 // static | |
| 47 void CookiesView::Show( | |
| 48 GtkWindow* parent, | |
| 49 Profile* profile, | |
| 50 BrowsingDataDatabaseHelper* browsing_data_database_helper, | |
| 51 BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper, | |
| 52 BrowsingDataAppCacheHelper* browsing_data_appcache_helper, | |
| 53 BrowsingDataIndexedDBHelper* browsing_data_indexed_db_helper) { | |
| 54 DCHECK(profile); | |
| 55 DCHECK(browsing_data_database_helper); | |
| 56 DCHECK(browsing_data_local_storage_helper); | |
| 57 DCHECK(browsing_data_appcache_helper); | |
| 58 DCHECK(browsing_data_indexed_db_helper); | |
| 59 | |
| 60 // If there's already an existing editor window, activate it. | |
| 61 if (instance_) { | |
| 62 gtk_util::PresentWindow(instance_->dialog_, 0); | |
| 63 } else { | |
| 64 instance_ = new CookiesView(parent, | |
| 65 profile, | |
| 66 browsing_data_database_helper, | |
| 67 browsing_data_local_storage_helper, | |
| 68 browsing_data_appcache_helper, | |
| 69 browsing_data_indexed_db_helper); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 CookiesView::CookiesView( | |
| 74 GtkWindow* parent, | |
| 75 Profile* profile, | |
| 76 BrowsingDataDatabaseHelper* browsing_data_database_helper, | |
| 77 BrowsingDataLocalStorageHelper* browsing_data_local_storage_helper, | |
| 78 BrowsingDataAppCacheHelper* browsing_data_appcache_helper, | |
| 79 BrowsingDataIndexedDBHelper* browsing_data_indexed_db_helper) | |
| 80 : profile_(profile), | |
| 81 browsing_data_database_helper_(browsing_data_database_helper), | |
| 82 browsing_data_local_storage_helper_(browsing_data_local_storage_helper), | |
| 83 browsing_data_appcache_helper_(browsing_data_appcache_helper), | |
| 84 browsing_data_indexed_db_helper_(browsing_data_indexed_db_helper), | |
| 85 filter_update_factory_(this), | |
| 86 destroy_dialog_in_destructor_(false) { | |
| 87 Init(parent); | |
| 88 | |
| 89 gtk_util::ShowDialogWithLocalizedSize(dialog_, | |
| 90 IDS_COOKIES_DIALOG_WIDTH_CHARS, | |
| 91 -1, | |
| 92 true); | |
| 93 | |
| 94 gtk_chrome_cookie_view_clear(GTK_CHROME_COOKIE_VIEW(cookie_display_)); | |
| 95 } | |
| 96 | |
| 97 void CookiesView::TestDestroySynchronously() { | |
| 98 g_signal_handler_disconnect(dialog_, destroy_handler_); | |
| 99 destroy_dialog_in_destructor_ = true; | |
| 100 } | |
| 101 | |
| 102 void CookiesView::Init(GtkWindow* parent) { | |
| 103 dialog_ = gtk_dialog_new_with_buttons( | |
| 104 l10n_util::GetStringUTF8( | |
| 105 IDS_COOKIES_WEBSITE_PERMISSIONS_WINDOW_TITLE).c_str(), | |
| 106 parent, | |
| 107 static_cast<GtkDialogFlags>(GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR), | |
| 108 GTK_STOCK_CLOSE, | |
| 109 GTK_RESPONSE_CLOSE, | |
| 110 NULL); | |
| 111 // Allow browser windows to go in front of the options dialog in metacity. | |
| 112 gtk_window_set_type_hint(GTK_WINDOW(dialog_), GDK_WINDOW_TYPE_HINT_NORMAL); | |
| 113 gtk_util::SetWindowIcon(GTK_WINDOW(dialog_)); | |
| 114 | |
| 115 remove_button_ = gtk_util::AddButtonToDialog( | |
| 116 dialog_, | |
| 117 gfx::ConvertAcceleratorsFromWindowsStyle( | |
| 118 l10n_util::GetStringUTF8(IDS_COOKIES_REMOVE_LABEL)).c_str(), | |
| 119 GTK_STOCK_REMOVE, | |
| 120 RESPONSE_REMOVE); | |
| 121 gtk_button_set_use_underline(GTK_BUTTON(remove_button_), TRUE); | |
| 122 gtk_button_box_set_child_secondary( | |
| 123 GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), | |
| 124 remove_button_, | |
| 125 TRUE); | |
| 126 | |
| 127 remove_all_button_ = gtk_util::AddButtonToDialog( | |
| 128 dialog_, | |
| 129 gfx::ConvertAcceleratorsFromWindowsStyle( | |
| 130 l10n_util::GetStringUTF8(IDS_COOKIES_REMOVE_ALL_LABEL)).c_str(), | |
| 131 GTK_STOCK_CLEAR, | |
| 132 RESPONSE_REMOVE_ALL); | |
| 133 gtk_button_set_use_underline(GTK_BUTTON(remove_all_button_), TRUE); | |
| 134 gtk_button_box_set_child_secondary( | |
| 135 GTK_BUTTON_BOX(GTK_DIALOG(dialog_)->action_area), | |
| 136 remove_all_button_, | |
| 137 TRUE); | |
| 138 | |
| 139 gtk_dialog_set_default_response(GTK_DIALOG(dialog_), GTK_RESPONSE_CLOSE); | |
| 140 gtk_window_set_default_size(GTK_WINDOW(dialog_), kDialogDefaultWidth, | |
| 141 kDialogDefaultHeight); | |
| 142 gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox), | |
| 143 gtk_util::kContentAreaSpacing); | |
| 144 g_signal_connect(dialog_, "response", G_CALLBACK(OnResponseThunk), this); | |
| 145 destroy_handler_ = g_signal_connect(dialog_, "destroy", | |
| 146 G_CALLBACK(OnWindowDestroyThunk), this); | |
| 147 | |
| 148 // Filtering controls. | |
| 149 GtkWidget* filter_hbox = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); | |
| 150 filter_entry_ = gtk_entry_new(); | |
| 151 g_signal_connect(filter_entry_, "activate", | |
| 152 G_CALLBACK(OnFilterEntryActivatedThunk), this); | |
| 153 g_signal_connect(filter_entry_, "changed", | |
| 154 G_CALLBACK(OnFilterEntryChangedThunk), this); | |
| 155 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_entry_, | |
| 156 TRUE, TRUE, 0); | |
| 157 filter_clear_button_ = gtk_button_new_with_mnemonic( | |
| 158 gfx::ConvertAcceleratorsFromWindowsStyle( | |
| 159 l10n_util::GetStringUTF8(IDS_COOKIES_CLEAR_SEARCH_LABEL)).c_str()); | |
| 160 g_signal_connect(filter_clear_button_, "clicked", | |
| 161 G_CALLBACK(OnFilterClearButtonClickedThunk), this); | |
| 162 gtk_box_pack_start(GTK_BOX(filter_hbox), filter_clear_button_, | |
| 163 FALSE, FALSE, 0); | |
| 164 | |
| 165 GtkWidget* filter_controls = gtk_util::CreateLabeledControlsGroup(NULL, | |
| 166 l10n_util::GetStringUTF8(IDS_COOKIES_SEARCH_LABEL).c_str(), filter_hbox, | |
| 167 NULL); | |
| 168 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), filter_controls, | |
| 169 FALSE, FALSE, 0); | |
| 170 | |
| 171 // Cookie list. | |
| 172 GtkWidget* cookie_list_vbox = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); | |
| 173 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), cookie_list_vbox, | |
| 174 TRUE, TRUE, 0); | |
| 175 | |
| 176 description_label_ = gtk_label_new( | |
| 177 l10n_util::GetStringUTF8(IDS_COOKIES_INFO_LABEL).c_str()); | |
| 178 gtk_misc_set_alignment(GTK_MISC(description_label_), 0, 0.5); | |
| 179 gtk_box_pack_start(GTK_BOX(cookie_list_vbox), description_label_, | |
| 180 FALSE, FALSE, 0); | |
| 181 | |
| 182 GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); | |
| 183 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), | |
| 184 GTK_POLICY_AUTOMATIC, | |
| 185 GTK_POLICY_AUTOMATIC); | |
| 186 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window), | |
| 187 GTK_SHADOW_ETCHED_IN); | |
| 188 gtk_box_pack_start(GTK_BOX(cookie_list_vbox), scroll_window, TRUE, TRUE, 0); | |
| 189 | |
| 190 cookies_tree_model_.reset(new CookiesTreeModel( | |
| 191 profile_->GetRequestContext()->GetCookieStore()->GetCookieMonster(), | |
| 192 browsing_data_database_helper_, | |
| 193 browsing_data_local_storage_helper_, | |
| 194 NULL, | |
| 195 browsing_data_appcache_helper_, | |
| 196 browsing_data_indexed_db_helper_)); | |
| 197 cookies_tree_adapter_.reset( | |
| 198 new gtk_tree::TreeAdapter(this, cookies_tree_model_.get())); | |
| 199 tree_ = gtk_tree_view_new_with_model( | |
| 200 GTK_TREE_MODEL(cookies_tree_adapter_->tree_store())); | |
| 201 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_), FALSE); | |
| 202 gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(tree_), TRUE); | |
| 203 gtk_container_add(GTK_CONTAINER(scroll_window), tree_); | |
| 204 | |
| 205 GtkTreeViewColumn* title_column = gtk_tree_view_column_new(); | |
| 206 GtkCellRenderer* pixbuf_renderer = gtk_cell_renderer_pixbuf_new(); | |
| 207 gtk_tree_view_column_pack_start(title_column, pixbuf_renderer, FALSE); | |
| 208 gtk_tree_view_column_add_attribute(title_column, pixbuf_renderer, "pixbuf", | |
| 209 gtk_tree::TreeAdapter::COL_ICON); | |
| 210 GtkCellRenderer* title_renderer = gtk_cell_renderer_text_new(); | |
| 211 gtk_tree_view_column_pack_start(title_column, title_renderer, TRUE); | |
| 212 gtk_tree_view_column_add_attribute(title_column, title_renderer, "text", | |
| 213 gtk_tree::TreeAdapter::COL_TITLE); | |
| 214 gtk_tree_view_column_set_title( | |
| 215 title_column, l10n_util::GetStringUTF8( | |
| 216 IDS_COOKIES_DOMAIN_COLUMN_HEADER).c_str()); | |
| 217 gtk_tree_view_append_column(GTK_TREE_VIEW(tree_), title_column); | |
| 218 g_signal_connect(tree_, "key-press-event", | |
| 219 G_CALLBACK(OnTreeViewKeyPressThunk), this); | |
| 220 g_signal_connect(tree_, "row-expanded", | |
| 221 G_CALLBACK(OnTreeViewRowExpandedThunk), this); | |
| 222 | |
| 223 selection_ = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_)); | |
| 224 gtk_tree_selection_set_mode(selection_, GTK_SELECTION_SINGLE); | |
| 225 g_signal_connect(selection_, "changed", | |
| 226 G_CALLBACK(OnTreeViewSelectionChangeThunk), this); | |
| 227 | |
| 228 cookie_display_ = gtk_chrome_cookie_view_new(FALSE); | |
| 229 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog_)->vbox), | |
| 230 cookie_display_, FALSE, FALSE, 0); | |
| 231 | |
| 232 // Populate the view. | |
| 233 cookies_tree_adapter_->Init(); | |
| 234 SetInitialTreeState(); | |
| 235 EnableControls(); | |
| 236 } | |
| 237 | |
| 238 void CookiesView::SetInitialTreeState() { | |
| 239 if (cookies_tree_model_->GetChildCount(cookies_tree_model_->GetRoot())) | |
| 240 gtk_tree::SelectAndFocusRowNum(0, GTK_TREE_VIEW(tree_)); | |
| 241 } | |
| 242 | |
| 243 void CookiesView::EnableControls() { | |
| 244 GtkTreeIter iter; | |
| 245 bool selected = gtk_tree_selection_get_selected(selection_, NULL, &iter); | |
| 246 gtk_widget_set_sensitive(remove_button_, selected); | |
| 247 gtk_widget_set_sensitive( | |
| 248 remove_all_button_, | |
| 249 cookies_tree_model_->GetChildCount(cookies_tree_model_->GetRoot())); | |
| 250 | |
| 251 const gchar* filter_text = gtk_entry_get_text(GTK_ENTRY(filter_entry_)); | |
| 252 gtk_widget_set_sensitive(filter_clear_button_, filter_text && *filter_text); | |
| 253 | |
| 254 if (selected) { | |
| 255 CookieTreeNode::DetailedInfo detailed_info = | |
| 256 static_cast<CookieTreeNode*>( | |
| 257 cookies_tree_adapter_->GetNode(&iter))->GetDetailedInfo(); | |
| 258 if (detailed_info.node_type == CookieTreeNode::DetailedInfo::TYPE_COOKIE) { | |
| 259 gtk_chrome_cookie_view_display_cookie( | |
| 260 GTK_CHROME_COOKIE_VIEW(cookie_display_), | |
| 261 detailed_info.cookie->Domain(), | |
| 262 *detailed_info.cookie); | |
| 263 } else if (detailed_info.node_type == | |
| 264 CookieTreeNode::DetailedInfo::TYPE_DATABASE) { | |
| 265 gtk_chrome_cookie_view_display_database( | |
| 266 GTK_CHROME_COOKIE_VIEW(cookie_display_), | |
| 267 *detailed_info.database_info); | |
| 268 } else if (detailed_info.node_type == | |
| 269 CookieTreeNode::DetailedInfo::TYPE_LOCAL_STORAGE) { | |
| 270 gtk_chrome_cookie_view_display_local_storage( | |
| 271 GTK_CHROME_COOKIE_VIEW(cookie_display_), | |
| 272 *detailed_info.local_storage_info); | |
| 273 } else if (detailed_info.node_type == | |
| 274 CookieTreeNode::DetailedInfo::TYPE_APPCACHE) { | |
| 275 gtk_chrome_cookie_view_display_app_cache( | |
| 276 GTK_CHROME_COOKIE_VIEW(cookie_display_), | |
| 277 *detailed_info.appcache_info); | |
| 278 } else if (detailed_info.node_type == | |
| 279 CookieTreeNode::DetailedInfo::TYPE_INDEXED_DB) { | |
| 280 gtk_chrome_cookie_view_display_indexed_db( | |
| 281 GTK_CHROME_COOKIE_VIEW(cookie_display_), | |
| 282 *detailed_info.indexed_db_info); | |
| 283 } else { | |
| 284 gtk_chrome_cookie_view_clear(GTK_CHROME_COOKIE_VIEW(cookie_display_)); | |
| 285 } | |
| 286 } else { | |
| 287 gtk_chrome_cookie_view_clear(GTK_CHROME_COOKIE_VIEW(cookie_display_)); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void CookiesView::RemoveSelectedItems() { | |
| 292 GtkTreeIter iter; | |
| 293 bool selected = gtk_tree_selection_get_selected(selection_, NULL, &iter); | |
| 294 if (selected) { | |
| 295 GtkTreePath* path = gtk_tree_model_get_path( | |
| 296 GTK_TREE_MODEL(cookies_tree_adapter_->tree_store()), | |
| 297 &iter); | |
| 298 CookieTreeNode* node = static_cast<CookieTreeNode*>( | |
| 299 cookies_tree_adapter_->GetNode(&iter)); | |
| 300 cookies_tree_model_->DeleteCookieNode(node); | |
| 301 // After removing a node, try to select the "next" node. | |
| 302 // We call gtk_tree_model_get_iter to check if there is still a node at the | |
| 303 // pointed to by path. If not, we try to select the previous node in that | |
| 304 // subtree. If that subtree is empty, we then try to select the parent. | |
| 305 if (gtk_tree_model_get_iter( | |
| 306 GTK_TREE_MODEL(cookies_tree_adapter_->tree_store()), | |
| 307 &iter, | |
| 308 path)) { | |
| 309 gtk_tree_selection_select_iter(selection_, &iter); | |
| 310 } else if (gtk_tree_path_prev(path)) { | |
| 311 gtk_tree_selection_select_path(selection_, path); | |
| 312 } else if (gtk_tree_path_up(path)) { | |
| 313 gtk_tree_selection_select_path(selection_, path); | |
| 314 } | |
| 315 gtk_tree_path_free(path); | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 void CookiesView::OnAnyModelUpdateStart() { | |
| 320 g_signal_handlers_block_by_func( | |
| 321 selection_, reinterpret_cast<gpointer>(OnTreeViewSelectionChangeThunk), | |
| 322 this); | |
| 323 } | |
| 324 | |
| 325 void CookiesView::OnAnyModelUpdate() { | |
| 326 g_signal_handlers_unblock_by_func( | |
| 327 selection_, reinterpret_cast<gpointer>(OnTreeViewSelectionChangeThunk), | |
| 328 this); | |
| 329 EnableControls(); | |
| 330 } | |
| 331 | |
| 332 void CookiesView::OnResponse(GtkWidget* dialog, int response_id) { | |
| 333 if (response_id == RESPONSE_REMOVE) { | |
| 334 RemoveSelectedItems(); | |
| 335 } else if (response_id == RESPONSE_REMOVE_ALL) { | |
| 336 cookies_tree_model_->DeleteAllStoredObjects(); | |
| 337 } else { | |
| 338 gtk_widget_destroy(dialog); | |
| 339 } | |
| 340 } | |
| 341 | |
| 342 void CookiesView::OnWindowDestroy(GtkWidget* widget) { | |
| 343 instance_ = NULL; | |
| 344 MessageLoop::current()->DeleteSoon(FROM_HERE, this); | |
| 345 } | |
| 346 | |
| 347 void CookiesView::OnTreeViewSelectionChange(GtkWidget* selection) { | |
| 348 EnableControls(); | |
| 349 } | |
| 350 | |
| 351 gboolean CookiesView::OnTreeViewKeyPress(GtkWidget* tree_view, | |
| 352 GdkEventKey* key) { | |
| 353 if (key->keyval == GDK_Delete) { | |
| 354 RemoveSelectedItems(); | |
| 355 return TRUE; | |
| 356 } | |
| 357 return FALSE; | |
| 358 } | |
| 359 | |
| 360 void CookiesView::OnTreeViewRowExpanded(GtkWidget* tree_view, | |
| 361 GtkTreeIter* iter, | |
| 362 GtkTreePath* path) { | |
| 363 // When a row in the tree is expanded, expand all the children too. | |
| 364 g_signal_handlers_block_by_func( | |
| 365 tree_view, reinterpret_cast<gpointer>(OnTreeViewRowExpandedThunk), this); | |
| 366 gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, TRUE); | |
| 367 g_signal_handlers_unblock_by_func( | |
| 368 tree_view, reinterpret_cast<gpointer>(OnTreeViewRowExpandedThunk), this); | |
| 369 } | |
| 370 | |
| 371 void CookiesView::UpdateFilterResults() { | |
| 372 const gchar* text = gtk_entry_get_text(GTK_ENTRY(filter_entry_)); | |
| 373 if (text) { | |
| 374 cookies_tree_model_->UpdateSearchResults(UTF8ToWide(text)); | |
| 375 SetInitialTreeState(); | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 void CookiesView::OnFilterEntryActivated(GtkWidget* entry) { | |
| 380 filter_update_factory_.RevokeAll(); | |
| 381 UpdateFilterResults(); | |
| 382 } | |
| 383 | |
| 384 void CookiesView::OnFilterEntryChanged(GtkWidget* editable) { | |
| 385 filter_update_factory_.RevokeAll(); | |
| 386 MessageLoop::current()->PostDelayedTask(FROM_HERE, | |
| 387 filter_update_factory_.NewRunnableMethod( | |
| 388 &CookiesView::UpdateFilterResults), kSearchFilterDelayMs); | |
| 389 EnableControls(); | |
| 390 } | |
| 391 | |
| 392 void CookiesView::OnFilterClearButtonClicked(GtkWidget* button) { | |
| 393 gtk_entry_set_text(GTK_ENTRY(filter_entry_), ""); | |
| 394 filter_update_factory_.RevokeAll(); | |
| 395 UpdateFilterResults(); | |
| 396 } | |
| OLD | NEW |