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/options/cookies_view.h" | |
6 | |
7 #include <gdk/gdkkeysyms.h> | |
8 #include <set> | |
9 #include <string> | |
10 | |
11 #include "app/l10n_util.h" | |
12 #include "base/message_loop.h" | |
13 #include "base/string_util.h" | |
14 #include "chrome/browser/cookies_tree_model.h" | |
15 #include "chrome/browser/gtk/gtk_util.h" | |
16 #include "chrome/browser/profiles/profile.h" | |
17 #include "gfx/gtk_util.h" | |
18 #include "grit/generated_resources.h" | |
19 #include "grit/locale_settings.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 |