| 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/passwords_page_gtk.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "app/l10n_util.h" | |
| 10 #include "base/stl_util-inl.h" | |
| 11 #include "base/utf_string_conversions.h" | |
| 12 #include "chrome/browser/gtk/gtk_tree.h" | |
| 13 #include "chrome/browser/gtk/gtk_util.h" | |
| 14 #include "chrome/browser/prefs/pref_service.h" | |
| 15 #include "chrome/browser/profiles/profile.h" | |
| 16 #include "chrome/common/notification_details.h" | |
| 17 #include "chrome/common/notification_type.h" | |
| 18 #include "chrome/common/pref_names.h" | |
| 19 #include "chrome/common/url_constants.h" | |
| 20 #include "gfx/gtk_util.h" | |
| 21 #include "grit/app_resources.h" | |
| 22 #include "grit/chromium_strings.h" | |
| 23 #include "grit/generated_resources.h" | |
| 24 #include "net/base/net_util.h" | |
| 25 | |
| 26 namespace { | |
| 27 | |
| 28 // Initial width of the first column. | |
| 29 const int kSiteColumnInitialSize = 265; | |
| 30 | |
| 31 // Column ids for |password_list_store_|. | |
| 32 enum { | |
| 33 COL_SITE, | |
| 34 COL_USERNAME, | |
| 35 COL_COUNT, | |
| 36 }; | |
| 37 | |
| 38 } // anonymous namespace | |
| 39 | |
| 40 /////////////////////////////////////////////////////////////////////////////// | |
| 41 // PasswordsPageGtk, public: | |
| 42 | |
| 43 PasswordsPageGtk::PasswordsPageGtk(Profile* profile) | |
| 44 : populater(this), password_showing_(false), profile_(profile) { | |
| 45 allow_show_passwords_.Init(prefs::kPasswordManagerAllowShowPasswords, | |
| 46 profile->GetPrefs(), | |
| 47 this); | |
| 48 | |
| 49 remove_button_ = gtk_button_new_with_label( | |
| 50 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_REMOVE_BUTTON).c_str()); | |
| 51 gtk_widget_set_sensitive(remove_button_, FALSE); | |
| 52 g_signal_connect(remove_button_, "clicked", | |
| 53 G_CALLBACK(OnRemoveButtonClickedThunk), this); | |
| 54 remove_all_button_ = gtk_button_new_with_label(l10n_util::GetStringUTF8( | |
| 55 IDS_PASSWORDS_PAGE_VIEW_REMOVE_ALL_BUTTON).c_str()); | |
| 56 gtk_widget_set_sensitive(remove_all_button_, FALSE); | |
| 57 g_signal_connect(remove_all_button_, "clicked", | |
| 58 G_CALLBACK(OnRemoveAllButtonClickedThunk), this); | |
| 59 | |
| 60 // We start with the "hide password" text but change it in the realize event. | |
| 61 show_password_button_ = gtk_button_new_with_label( | |
| 62 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON).c_str()); | |
| 63 gtk_widget_set_no_show_all(show_password_button_, true); | |
| 64 gtk_widget_set_sensitive(show_password_button_, FALSE); | |
| 65 g_signal_connect(show_password_button_, "clicked", | |
| 66 G_CALLBACK(OnShowPasswordButtonClickedThunk), this); | |
| 67 g_signal_connect(show_password_button_, "realize", | |
| 68 G_CALLBACK(OnShowPasswordButtonRealizedThunk), this); | |
| 69 | |
| 70 password_ = gtk_label_new(""); | |
| 71 gtk_label_set_selectable(GTK_LABEL(password_), TRUE); | |
| 72 gtk_widget_set_no_show_all(password_, true); | |
| 73 | |
| 74 GtkWidget* buttons = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); | |
| 75 gtk_box_pack_start(GTK_BOX(buttons), remove_button_, FALSE, FALSE, 0); | |
| 76 gtk_box_pack_start(GTK_BOX(buttons), remove_all_button_, FALSE, FALSE, 0); | |
| 77 gtk_box_pack_start(GTK_BOX(buttons), show_password_button_, FALSE, FALSE, 0); | |
| 78 gtk_box_pack_start(GTK_BOX(buttons), password_, FALSE, FALSE, 0); | |
| 79 | |
| 80 GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); | |
| 81 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), | |
| 82 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); | |
| 83 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window), | |
| 84 GTK_SHADOW_ETCHED_IN); | |
| 85 | |
| 86 // Sets password_tree_ among other things. | |
| 87 InitPasswordTree(); | |
| 88 gtk_container_add(GTK_CONTAINER(scroll_window), password_tree_); | |
| 89 | |
| 90 page_ = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); | |
| 91 gtk_container_set_border_width(GTK_CONTAINER(page_), | |
| 92 gtk_util::kContentAreaBorder); | |
| 93 gtk_box_pack_end(GTK_BOX(page_), buttons, FALSE, FALSE, 0); | |
| 94 gtk_box_pack_end(GTK_BOX(page_), scroll_window, TRUE, TRUE, 0); | |
| 95 | |
| 96 // Initialize UI state based on current preference values. | |
| 97 OnPrefChanged(prefs::kPasswordManagerAllowShowPasswords); | |
| 98 } | |
| 99 | |
| 100 PasswordsPageGtk::~PasswordsPageGtk() { | |
| 101 STLDeleteElements(&password_list_); | |
| 102 } | |
| 103 | |
| 104 /////////////////////////////////////////////////////////////////////////////// | |
| 105 // PasswordsPageGtk, private: | |
| 106 | |
| 107 void PasswordsPageGtk::InitPasswordTree() { | |
| 108 password_list_store_ = gtk_list_store_new(COL_COUNT, | |
| 109 G_TYPE_STRING, | |
| 110 G_TYPE_STRING); | |
| 111 password_list_sort_ = gtk_tree_model_sort_new_with_model( | |
| 112 GTK_TREE_MODEL(password_list_store_)); | |
| 113 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(password_list_sort_), | |
| 114 COL_SITE, CompareSite, this, NULL); | |
| 115 gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(password_list_sort_), | |
| 116 COL_USERNAME, CompareUsername, this, NULL); | |
| 117 password_tree_ = gtk_tree_view_new_with_model(password_list_sort_); | |
| 118 g_object_unref(password_list_store_); | |
| 119 g_object_unref(password_list_sort_); | |
| 120 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(password_tree_), TRUE); | |
| 121 | |
| 122 password_selection_ = gtk_tree_view_get_selection( | |
| 123 GTK_TREE_VIEW(password_tree_)); | |
| 124 gtk_tree_selection_set_mode(password_selection_, | |
| 125 GTK_SELECTION_SINGLE); | |
| 126 g_signal_connect(password_selection_, "changed", | |
| 127 G_CALLBACK(OnPasswordSelectionChangedThunk), this); | |
| 128 | |
| 129 GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( | |
| 130 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SITE_COLUMN).c_str(), | |
| 131 gtk_cell_renderer_text_new(), | |
| 132 "text", COL_SITE, | |
| 133 NULL); | |
| 134 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); | |
| 135 gtk_tree_view_column_set_resizable(column, TRUE); | |
| 136 gtk_tree_view_column_set_fixed_width(column, kSiteColumnInitialSize); | |
| 137 gtk_tree_view_column_set_sort_column_id(column, COL_SITE); | |
| 138 gtk_tree_view_append_column(GTK_TREE_VIEW(password_tree_), column); | |
| 139 | |
| 140 column = gtk_tree_view_column_new_with_attributes( | |
| 141 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_USERNAME_COLUMN).c_str(), | |
| 142 gtk_cell_renderer_text_new(), | |
| 143 "text", COL_USERNAME, | |
| 144 NULL); | |
| 145 gtk_tree_view_column_set_sort_column_id(column, COL_USERNAME); | |
| 146 gtk_tree_view_append_column(GTK_TREE_VIEW(password_tree_), column); | |
| 147 populater.populate(); | |
| 148 } | |
| 149 | |
| 150 PasswordStore* PasswordsPageGtk::GetPasswordStore() { | |
| 151 return profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); | |
| 152 } | |
| 153 | |
| 154 void PasswordsPageGtk::SetPasswordList( | |
| 155 const std::vector<webkit_glue::PasswordForm*>& result) { | |
| 156 std::string languages = | |
| 157 profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); | |
| 158 gtk_list_store_clear(password_list_store_); | |
| 159 STLDeleteElements(&password_list_); | |
| 160 password_list_ = result; | |
| 161 for (size_t i = 0; i < result.size(); ++i) { | |
| 162 GtkTreeIter iter; | |
| 163 gtk_list_store_insert_with_values(password_list_store_, &iter, (gint) i, | |
| 164 COL_SITE, | |
| 165 UTF16ToUTF8(net::FormatUrl(result[i]->origin, languages)).c_str(), | |
| 166 COL_USERNAME, UTF16ToUTF8(result[i]->username_value).c_str(), -1); | |
| 167 } | |
| 168 gtk_widget_set_sensitive(remove_all_button_, result.size() > 0); | |
| 169 } | |
| 170 | |
| 171 void PasswordsPageGtk::HidePassword() { | |
| 172 password_showing_ = false; | |
| 173 gtk_label_set_text(GTK_LABEL(password_), ""); | |
| 174 gtk_button_set_label(GTK_BUTTON(show_password_button_), | |
| 175 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); | |
| 176 } | |
| 177 | |
| 178 void PasswordsPageGtk::Observe(NotificationType type, | |
| 179 const NotificationSource& source, | |
| 180 const NotificationDetails& details) { | |
| 181 DCHECK_EQ(NotificationType::PREF_CHANGED, type.value); | |
| 182 const std::string* pref_name = Details<std::string>(details).ptr(); | |
| 183 OnPrefChanged(*pref_name); | |
| 184 } | |
| 185 | |
| 186 void PasswordsPageGtk::OnPrefChanged(const std::string& pref_name) { | |
| 187 if (pref_name == prefs::kPasswordManagerAllowShowPasswords) { | |
| 188 if (allow_show_passwords_.GetValue()) { | |
| 189 gtk_widget_show(show_password_button_); | |
| 190 gtk_widget_show(password_); | |
| 191 } else { | |
| 192 HidePassword(); | |
| 193 gtk_widget_hide(show_password_button_); | |
| 194 gtk_widget_hide(password_); | |
| 195 } | |
| 196 } else { | |
| 197 NOTREACHED(); | |
| 198 } | |
| 199 } | |
| 200 | |
| 201 void PasswordsPageGtk::OnRemoveButtonClicked(GtkWidget* widget) { | |
| 202 GtkTreeIter iter; | |
| 203 if (!gtk_tree_selection_get_selected(password_selection_, | |
| 204 NULL, &iter)) { | |
| 205 NOTREACHED(); | |
| 206 return; | |
| 207 } | |
| 208 | |
| 209 GtkTreePath* path = gtk_tree_model_get_path( | |
| 210 GTK_TREE_MODEL(password_list_sort_), &iter); | |
| 211 gint index = gtk_tree::GetTreeSortChildRowNumForPath( | |
| 212 password_list_sort_, path); | |
| 213 gtk_tree_path_free(path); | |
| 214 | |
| 215 GtkTreeIter child_iter; | |
| 216 gtk_tree_model_sort_convert_iter_to_child_iter( | |
| 217 GTK_TREE_MODEL_SORT(password_list_sort_), &child_iter, &iter); | |
| 218 | |
| 219 // Remove from GTK list, DB, and vector. | |
| 220 gtk_list_store_remove(password_list_store_, &child_iter); | |
| 221 GetPasswordStore()->RemoveLogin(*password_list_[index]); | |
| 222 delete password_list_[index]; | |
| 223 password_list_.erase(password_list_.begin() + index); | |
| 224 | |
| 225 gtk_widget_set_sensitive(remove_all_button_, password_list_.size() > 0); | |
| 226 } | |
| 227 | |
| 228 void PasswordsPageGtk::OnRemoveAllButtonClicked(GtkWidget* widget) { | |
| 229 GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(page_)); | |
| 230 GtkWidget* confirm = gtk_message_dialog_new( | |
| 231 window, | |
| 232 static_cast<GtkDialogFlags>( | |
| 233 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), | |
| 234 GTK_MESSAGE_QUESTION, | |
| 235 GTK_BUTTONS_YES_NO, | |
| 236 "%s", | |
| 237 l10n_util::GetStringUTF8( | |
| 238 IDS_PASSWORDS_PAGE_VIEW_TEXT_DELETE_ALL_PASSWORDS).c_str()); | |
| 239 gtk_util::ApplyMessageDialogQuirks(confirm); | |
| 240 gtk_window_set_title(GTK_WINDOW(confirm), l10n_util::GetStringUTF8( | |
| 241 IDS_PASSWORDS_PAGE_VIEW_CAPTION_DELETE_ALL_PASSWORDS).c_str()); | |
| 242 g_signal_connect(confirm, "response", | |
| 243 G_CALLBACK(OnRemoveAllConfirmResponseThunk), this); | |
| 244 gtk_widget_show_all(confirm); | |
| 245 } | |
| 246 | |
| 247 void PasswordsPageGtk::OnRemoveAllConfirmResponse(GtkWidget* confirm, | |
| 248 gint response) { | |
| 249 bool confirmed = false; | |
| 250 switch (response) { | |
| 251 case GTK_RESPONSE_YES: | |
| 252 confirmed = true; | |
| 253 break; | |
| 254 default: | |
| 255 break; | |
| 256 } | |
| 257 gtk_widget_destroy(confirm); | |
| 258 if (!confirmed) | |
| 259 return; | |
| 260 | |
| 261 // Remove from GTK list, DB, and vector. | |
| 262 PasswordStore* store = GetPasswordStore(); | |
| 263 gtk_list_store_clear(password_list_store_); | |
| 264 for (size_t i = 0; i < password_list_.size(); ++i) | |
| 265 store->RemoveLogin(*password_list_[i]); | |
| 266 STLDeleteElements(&password_list_); | |
| 267 gtk_widget_set_sensitive(remove_all_button_, FALSE); | |
| 268 } | |
| 269 | |
| 270 void PasswordsPageGtk::OnShowPasswordButtonClicked(GtkWidget* widget) { | |
| 271 if (password_showing_ || !allow_show_passwords_.GetValue()) { | |
| 272 // Hide the password. | |
| 273 HidePassword(); | |
| 274 return; | |
| 275 } | |
| 276 // Show the password. | |
| 277 password_showing_ = true; | |
| 278 GtkTreeIter iter; | |
| 279 if (!gtk_tree_selection_get_selected(password_selection_, | |
| 280 NULL, &iter)) { | |
| 281 NOTREACHED(); | |
| 282 return; | |
| 283 } | |
| 284 GtkTreePath* path = gtk_tree_model_get_path( | |
| 285 GTK_TREE_MODEL(password_list_sort_), &iter); | |
| 286 gint index = gtk_tree::GetTreeSortChildRowNumForPath( | |
| 287 password_list_sort_, path); | |
| 288 gtk_tree_path_free(path); | |
| 289 std::string pass = UTF16ToUTF8(password_list_[index]->password_value); | |
| 290 gtk_label_set_text(GTK_LABEL(password_), pass.c_str()); | |
| 291 gtk_button_set_label(GTK_BUTTON(show_password_button_), | |
| 292 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON).c_str()); | |
| 293 } | |
| 294 | |
| 295 void PasswordsPageGtk::OnShowPasswordButtonRealized(GtkWidget* widget) { | |
| 296 // We have just realized the "show password" button, so we can now accurately | |
| 297 // find out how big it needs to be in order to accomodate both the "show" and | |
| 298 // "hide" labels. (This requires font information to work correctly.) The | |
| 299 // button starts with the "hide" label so we only have to change it once. | |
| 300 GtkRequisition hide_size, show_size; | |
| 301 // Get the size request of the button with the "hide password" text. | |
| 302 gtk_widget_size_request(show_password_button_, &hide_size); | |
| 303 gtk_button_set_label(GTK_BUTTON(show_password_button_), | |
| 304 l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); | |
| 305 // Get the size request of the button with the "show password" text. | |
| 306 gtk_widget_size_request(show_password_button_, &show_size); | |
| 307 // Determine the maximum width and height. | |
| 308 if (hide_size.width > show_size.width) | |
| 309 show_size.width = hide_size.width; | |
| 310 if (hide_size.height > show_size.height) | |
| 311 show_size.height = hide_size.height; | |
| 312 // Force the button to be large enough for both labels. | |
| 313 gtk_widget_set_size_request(show_password_button_, show_size.width, | |
| 314 show_size.height); | |
| 315 } | |
| 316 | |
| 317 void PasswordsPageGtk::OnPasswordSelectionChanged(GtkTreeSelection* selection) { | |
| 318 // No matter how the selection changed, we want to hide the old password. | |
| 319 HidePassword(); | |
| 320 | |
| 321 GtkTreeIter iter; | |
| 322 if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { | |
| 323 gtk_widget_set_sensitive(show_password_button_, FALSE); | |
| 324 gtk_widget_set_sensitive(remove_button_, FALSE); | |
| 325 return; | |
| 326 } | |
| 327 gtk_widget_set_sensitive(show_password_button_, TRUE); | |
| 328 gtk_widget_set_sensitive(remove_button_, TRUE); | |
| 329 } | |
| 330 | |
| 331 // static | |
| 332 gint PasswordsPageGtk::CompareSite(GtkTreeModel* model, | |
| 333 GtkTreeIter* a, GtkTreeIter* b, | |
| 334 gpointer window) { | |
| 335 int row1 = gtk_tree::GetRowNumForIter(model, a); | |
| 336 int row2 = gtk_tree::GetRowNumForIter(model, b); | |
| 337 PasswordsPageGtk* page = reinterpret_cast<PasswordsPageGtk*>(window); | |
| 338 return page->password_list_[row1]->origin.spec().compare( | |
| 339 page->password_list_[row2]->origin.spec()); | |
| 340 } | |
| 341 | |
| 342 // static | |
| 343 gint PasswordsPageGtk::CompareUsername(GtkTreeModel* model, | |
| 344 GtkTreeIter* a, GtkTreeIter* b, | |
| 345 gpointer window) { | |
| 346 int row1 = gtk_tree::GetRowNumForIter(model, a); | |
| 347 int row2 = gtk_tree::GetRowNumForIter(model, b); | |
| 348 PasswordsPageGtk* page = reinterpret_cast<PasswordsPageGtk*>(window); | |
| 349 return page->password_list_[row1]->username_value.compare( | |
| 350 page->password_list_[row2]->username_value); | |
| 351 } | |
| 352 | |
| 353 void PasswordsPageGtk::PasswordListPopulater::populate() { | |
| 354 DCHECK(!pending_login_query_); | |
| 355 PasswordStore* store = page_->GetPasswordStore(); | |
| 356 if (store != NULL) | |
| 357 pending_login_query_ = store->GetAutofillableLogins(this); | |
| 358 else | |
| 359 LOG(ERROR) << "No password store! Cannot display passwords."; | |
| 360 } | |
| 361 | |
| 362 void PasswordsPageGtk::PasswordListPopulater::OnPasswordStoreRequestDone( | |
| 363 int handle, const std::vector<webkit_glue::PasswordForm*>& result) { | |
| 364 DCHECK_EQ(pending_login_query_, handle); | |
| 365 pending_login_query_ = 0; | |
| 366 page_->SetPasswordList(result); | |
| 367 } | |
| OLD | NEW |