From 748bfd9c6d88897e3345a9598b52be8e7bf0eaf9 Mon Sep 17 00:00:00 2001 From: "mdm@chromium.org" Date: Wed, 29 Jul 2009 18:20:03 +0000 Subject: Linux: add the password manager UI. BUG=8205 TEST=go to wrench->options->personal stuff->show saved passwords, it works Review URL: http://codereview.chromium.org/159522 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21973 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/browser/gtk/options/content_page_gtk.cc | 3 +- chrome/browser/gtk/options/exceptions_page_gtk.cc | 226 +++++++++++++ chrome/browser/gtk/options/exceptions_page_gtk.h | 90 ++++++ chrome/browser/gtk/options/options_window_gtk.cc | 36 +-- .../gtk/options/passwords_exceptions_window_gtk.cc | 140 +++++++++ .../gtk/options/passwords_exceptions_window_gtk.h | 12 + chrome/browser/gtk/options/passwords_page_gtk.cc | 349 +++++++++++++++++++++ chrome/browser/gtk/options/passwords_page_gtk.h | 106 +++++++ chrome/chrome.gyp | 6 + 9 files changed, 949 insertions(+), 19 deletions(-) create mode 100644 chrome/browser/gtk/options/exceptions_page_gtk.cc create mode 100644 chrome/browser/gtk/options/exceptions_page_gtk.h create mode 100644 chrome/browser/gtk/options/passwords_exceptions_window_gtk.cc create mode 100644 chrome/browser/gtk/options/passwords_exceptions_window_gtk.h create mode 100644 chrome/browser/gtk/options/passwords_page_gtk.cc create mode 100644 chrome/browser/gtk/options/passwords_page_gtk.h diff --git a/chrome/browser/gtk/options/content_page_gtk.cc b/chrome/browser/gtk/options/content_page_gtk.cc index 1ababe2..3d8d9f6 100644 --- a/chrome/browser/gtk/options/content_page_gtk.cc +++ b/chrome/browser/gtk/options/content_page_gtk.cc @@ -10,6 +10,7 @@ #include "chrome/browser/gtk/clear_browsing_data_dialog_gtk.h" #include "chrome/browser/gtk/import_dialog_gtk.h" #include "chrome/browser/gtk/options/options_layout_gtk.h" +#include "chrome/browser/gtk/options/passwords_exceptions_window_gtk.h" #include "chrome/common/gtk_util.h" #include "chrome/common/pref_names.h" #include "chrome/common/pref_service.h" @@ -223,7 +224,7 @@ void ContentPageGtk::OnResetDefaultThemeButtonClicked(GtkButton* widget, // static void ContentPageGtk::OnPasswordsExceptionsButtonClicked(GtkButton* widget, ContentPageGtk* page) { - NOTIMPLEMENTED(); + ShowPasswordsExceptionsWindow(page->profile()); } // static diff --git a/chrome/browser/gtk/options/exceptions_page_gtk.cc b/chrome/browser/gtk/options/exceptions_page_gtk.cc new file mode 100644 index 0000000..c0b5597 --- /dev/null +++ b/chrome/browser/gtk/options/exceptions_page_gtk.cc @@ -0,0 +1,226 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/options/exceptions_page_gtk.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/gfx/gtk_util.h" +#include "chrome/common/gtk_util.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/url_constants.h" +#include "grit/app_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "net/base/net_util.h" + +namespace { + +// Column ids for |exception_list_store_|. +enum { + COL_SITE, + COL_COUNT, +}; + +// Get the row number corresponding to |path|. +gint GetRowNumForPath(GtkTreePath* path) { + gint* indices = gtk_tree_path_get_indices(path); + if (!indices) { + NOTREACHED(); + return -1; + } + return indices[0]; +} + +// Get the row number corresponding to |iter|. +gint GetRowNumForIter(GtkTreeModel* model, GtkTreeIter* iter) { + GtkTreePath* path = gtk_tree_model_get_path(model, iter); + int row = GetRowNumForPath(path); + gtk_tree_path_free(path); + return row; +} + +// Get the row number in the child tree model corresponding to |sort_path| in +// the parent tree model. +gint GetTreeSortChildRowNumForPath(GtkTreeModel* sort_model, + GtkTreePath* sort_path) { + GtkTreePath *child_path = gtk_tree_model_sort_convert_path_to_child_path( + GTK_TREE_MODEL_SORT(sort_model), sort_path); + int row = GetRowNumForPath(child_path); + gtk_tree_path_free(child_path); + return row; +} + +} // anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// +// ExceptionsPageGtk, public: + +ExceptionsPageGtk::ExceptionsPageGtk(Profile* profile) + : populater(this), profile_(profile) { + + remove_button_ = gtk_button_new_with_label( + l10n_util::GetStringUTF8(IDS_EXCEPTIONS_PAGE_VIEW_REMOVE_BUTTON).c_str()); + gtk_widget_set_sensitive(remove_button_, FALSE); + g_signal_connect(G_OBJECT(remove_button_), "clicked", + G_CALLBACK(OnRemoveButtonClicked), this); + remove_all_button_ = gtk_button_new_with_label(l10n_util::GetStringUTF8( + IDS_EXCEPTIONS_PAGE_VIEW_REMOVE_ALL_BUTTON).c_str()); + gtk_widget_set_sensitive(remove_all_button_, FALSE); + g_signal_connect(G_OBJECT(remove_all_button_), "clicked", + G_CALLBACK(OnRemoveAllButtonClicked), this); + + GtkWidget* buttons = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + gtk_box_pack_start(GTK_BOX(buttons), remove_button_, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(buttons), remove_all_button_, FALSE, FALSE, 0); + + GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window), + GTK_SHADOW_ETCHED_IN); + + // Sets exception_tree_ among other things. + InitExceptionTree(); + gtk_container_add(GTK_CONTAINER(scroll_window), exception_tree_); + + page_ = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); + gtk_container_set_border_width(GTK_CONTAINER(page_), + gtk_util::kContentAreaBorder); + gtk_box_pack_end(GTK_BOX(page_), buttons, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(page_), scroll_window, TRUE, TRUE, 0); +} + +ExceptionsPageGtk::~ExceptionsPageGtk() { +} + +/////////////////////////////////////////////////////////////////////////////// +// ExceptionsPageGtk, private: + +void ExceptionsPageGtk::InitExceptionTree() { + exception_list_store_ = gtk_list_store_new(COL_COUNT, G_TYPE_STRING); + exception_list_sort_ = gtk_tree_model_sort_new_with_model( + GTK_TREE_MODEL(exception_list_store_)); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(exception_list_sort_), + COL_SITE, CompareSite, this, NULL); + exception_tree_ = gtk_tree_view_new_with_model(exception_list_sort_); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(exception_tree_), TRUE); + + exception_selection_ = gtk_tree_view_get_selection( + GTK_TREE_VIEW(exception_tree_)); + gtk_tree_selection_set_mode(exception_selection_, + GTK_SELECTION_SINGLE); + g_signal_connect(G_OBJECT(exception_selection_), "changed", + G_CALLBACK(OnExceptionSelectionChanged), this); + + GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SITE_COLUMN).c_str(), + gtk_cell_renderer_text_new(), + "text", COL_SITE, + NULL); + gtk_tree_view_column_set_sort_column_id(column, COL_SITE); + gtk_tree_view_append_column(GTK_TREE_VIEW(exception_tree_), column); + + populater.populate(); +} + +PasswordStore* ExceptionsPageGtk::GetPasswordStore() { + return profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); +} + +void ExceptionsPageGtk::SetExceptionList( + const std::vector& result) { + std::wstring languages = + profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); + gtk_list_store_clear(exception_list_store_); + exception_list_.resize(result.size()); + for (size_t i = 0; i < result.size(); ++i) { + exception_list_[i] = *result[i]; + std::wstring formatted = net::FormatUrl(result[i]->origin, languages, + false, UnescapeRule::NONE, + NULL, NULL); + std::string site = WideToUTF8(formatted); + GtkTreeIter iter; + gtk_list_store_insert_with_values(exception_list_store_, &iter, (gint) i, + COL_SITE, site.c_str(), -1); + } + gtk_widget_set_sensitive(remove_all_button_, result.size() > 0); +} + +// static +void ExceptionsPageGtk::OnRemoveButtonClicked(GtkButton* widget, + ExceptionsPageGtk* page) { + GtkTreeIter iter; + if (!gtk_tree_selection_get_selected(page->exception_selection_, + NULL, &iter)) { + NOTREACHED(); + return; + } + + GtkTreePath* path = gtk_tree_model_get_path( + GTK_TREE_MODEL(page->exception_list_sort_), &iter); + gint index = GetTreeSortChildRowNumForPath(page->exception_list_sort_, path); + gtk_tree_path_free(path); + + GtkTreeIter child_iter; + gtk_tree_model_sort_convert_iter_to_child_iter( + GTK_TREE_MODEL_SORT(page->exception_list_sort_), &child_iter, &iter); + + // Remove from GTK list, DB, and vector. + gtk_list_store_remove(page->exception_list_store_, &child_iter); + page->GetPasswordStore()->RemoveLogin(page->exception_list_[index]); + page->exception_list_.erase(page->exception_list_.begin() + index); + + gtk_widget_set_sensitive(page->remove_all_button_, + page->exception_list_.size() > 0); +} + +// static +void ExceptionsPageGtk::OnRemoveAllButtonClicked(GtkButton* widget, + ExceptionsPageGtk* page) { + // Remove from GTK list, DB, and vector. + PasswordStore* store = page->GetPasswordStore(); + gtk_list_store_clear(page->exception_list_store_); + for (size_t i = 0; i < page->exception_list_.size(); ++i) { + store->RemoveLogin(page->exception_list_[i]); + } + page->exception_list_.clear(); + gtk_widget_set_sensitive(page->remove_all_button_, FALSE); +} + +// static +void ExceptionsPageGtk::OnExceptionSelectionChanged(GtkTreeSelection* selection, + ExceptionsPageGtk* page) { + GtkTreeIter iter; + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + gtk_widget_set_sensitive(page->remove_button_, FALSE); + return; + } + gtk_widget_set_sensitive(page->remove_button_, TRUE); +} + +// static +gint ExceptionsPageGtk::CompareSite(GtkTreeModel* model, + GtkTreeIter* a, GtkTreeIter* b, + gpointer window) { + int row1 = GetRowNumForIter(model, a); + int row2 = GetRowNumForIter(model, b); + ExceptionsPageGtk* page = reinterpret_cast(window); + return page->exception_list_[row1].origin.spec().compare( + page->exception_list_[row2].origin.spec()); +} + +void ExceptionsPageGtk::ExceptionListPopulater::populate() { + DCHECK(!pending_login_query_); + PasswordStore* store = page_->GetPasswordStore(); + pending_login_query_ = store->GetBlacklistLogins(this); +} + +void ExceptionsPageGtk::ExceptionListPopulater::OnPasswordStoreRequestDone( + int handle, const std::vector& result) { + DCHECK_EQ(pending_login_query_, handle); + pending_login_query_ = 0; + page_->SetExceptionList(result); +} diff --git a/chrome/browser/gtk/options/exceptions_page_gtk.h b/chrome/browser/gtk/options/exceptions_page_gtk.h new file mode 100644 index 0000000..ae3142f --- /dev/null +++ b/chrome/browser/gtk/options/exceptions_page_gtk.h @@ -0,0 +1,90 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GTK_OPTIONS_EXCEPTIONS_PAGE_GTK_H_ +#define CHROME_BROWSER_GTK_OPTIONS_EXCEPTIONS_PAGE_GTK_H_ + +#include + +#include + +#include "chrome/browser/password_manager/password_store.h" +#include "chrome/browser/profile.h" + +class ExceptionsPageGtk { + public: + explicit ExceptionsPageGtk(Profile* profile); + ~ExceptionsPageGtk(); + + GtkWidget* get_page_widget() const { + return page_; + } + + private: + // Initialize the exception tree widget, setting the member variables. + void InitExceptionTree(); + + // The password store associated with the currently active profile. + PasswordStore* GetPasswordStore(); + + // Sets the exception list contents to the given data. + void SetExceptionList(const std::vector& result); + + // Callback for the remove button. + static void OnRemoveButtonClicked(GtkButton* widget, ExceptionsPageGtk* page); + + // Callback for the remove all button. + static void OnRemoveAllButtonClicked(GtkButton* widget, + ExceptionsPageGtk* page); + + // Callback for selection changed events. + static void OnExceptionSelectionChanged(GtkTreeSelection* selection, + ExceptionsPageGtk* page); + + // Sorting function. + static gint CompareSite(GtkTreeModel* model, + GtkTreeIter* a, GtkTreeIter* b, + gpointer window); + + // A short class to mediate requests to the password store. + class ExceptionListPopulater : public PasswordStoreConsumer { + public: + explicit ExceptionListPopulater(ExceptionsPageGtk* page) : page_(page) { + } + + // Send a query to the password store to populate an ExceptionsPageGtk. + void populate(); + + // Send the password store's reply back to the ExceptionsPageGtk. + virtual void OnPasswordStoreRequestDone( + int handle, const std::vector& result); + + private: + ExceptionsPageGtk* page_; + int pending_login_query_; + }; + + // Password store consumer for populating the exception list. + ExceptionListPopulater populater; + + // Widgets for the buttons. + GtkWidget* remove_button_; + GtkWidget* remove_all_button_; + + // Widgets for the exception table. + GtkWidget* exception_tree_; + GtkListStore* exception_list_store_; + GtkTreeModel* exception_list_sort_; + GtkTreeSelection* exception_selection_; + + // The parent GtkHBox widget. + GtkWidget* page_; + + Profile* profile_; + std::vector exception_list_; + + DISALLOW_COPY_AND_ASSIGN(ExceptionsPageGtk); +}; + +#endif // CHROME_BROWSER_GTK_OPTIONS_EXCEPTIONS_PAGE_GTK_H_ diff --git a/chrome/browser/gtk/options/options_window_gtk.cc b/chrome/browser/gtk/options/options_window_gtk.cc index d155d38..86e188a 100644 --- a/chrome/browser/gtk/options/options_window_gtk.cc +++ b/chrome/browser/gtk/options/options_window_gtk.cc @@ -2,10 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include - #include "chrome/browser/options_window.h" +#include + #include "app/l10n_util.h" #include "base/message_loop.h" #include "chrome/browser/browser_process.h" @@ -38,15 +38,15 @@ class OptionsWindowGtk { private: static void OnSwitchPage(GtkNotebook* notebook, GtkNotebookPage* page, - guint page_num, OptionsWindowGtk* options_window); + guint page_num, OptionsWindowGtk* window); static void OnWindowDestroy(GtkWidget* widget, OptionsWindowGtk* window); // The options dialog. - GtkWidget *dialog_; + GtkWidget* dialog_; // The container of the option pages. - GtkWidget *notebook_; + GtkWidget* notebook_; // The Profile associated with these options. Profile* profile_; @@ -66,7 +66,8 @@ class OptionsWindowGtk { DISALLOW_COPY_AND_ASSIGN(OptionsWindowGtk); }; -static OptionsWindowGtk* instance_ = NULL; +// The singleton options window object. +static OptionsWindowGtk* options_window = NULL; /////////////////////////////////////////////////////////////////////////////// // OptionsWindowGtk, public: @@ -133,8 +134,8 @@ OptionsWindowGtk::OptionsWindowGtk(Profile* profile) gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_); - DCHECK( - gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)) == OPTIONS_PAGE_COUNT); + DCHECK_EQ( + gtk_notebook_get_n_pages(GTK_NOTEBOOK(notebook_)), OPTIONS_PAGE_COUNT); // Need to show the notebook before connecting switch-page signal, otherwise // we'll immediately get a signal switching to page 0 and overwrite our @@ -145,8 +146,7 @@ OptionsWindowGtk::OptionsWindowGtk(Profile* profile) // We only have one button and don't do any special handling, so just hook it // directly to gtk_widget_destroy. - g_signal_connect_swapped(dialog_, "response", G_CALLBACK(gtk_widget_destroy), - dialog_); + g_signal_connect(dialog_, "response", G_CALLBACK(gtk_widget_destroy), NULL); g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroy), this); } @@ -183,17 +183,17 @@ void OptionsWindowGtk::ShowOptionsPage(OptionsPage page, void OptionsWindowGtk::OnSwitchPage(GtkNotebook* notebook, GtkNotebookPage* page, guint page_num, - OptionsWindowGtk* options_window) { + OptionsWindowGtk* window) { int index = page_num; DCHECK(index > OPTIONS_PAGE_DEFAULT && index < OPTIONS_PAGE_COUNT); - options_window->last_selected_page_.SetValue(index); + window->last_selected_page_.SetValue(index); } // static void OptionsWindowGtk::OnWindowDestroy(GtkWidget* widget, - OptionsWindowGtk* options_window) { - instance_ = NULL; - MessageLoop::current()->DeleteSoon(FROM_HERE, options_window); + OptionsWindowGtk* window) { + options_window = NULL; + MessageLoop::current()->DeleteSoon(FROM_HERE, window); } /////////////////////////////////////////////////////////////////////////////// @@ -205,8 +205,8 @@ void ShowOptionsWindow(OptionsPage page, DCHECK(profile); // If there's already an existing options window, activate it and switch to // the specified page. - if (!instance_) { - instance_ = new OptionsWindowGtk(profile); + if (!options_window) { + options_window = new OptionsWindowGtk(profile); } - instance_->ShowOptionsPage(page, highlight_group); + options_window->ShowOptionsPage(page, highlight_group); } diff --git a/chrome/browser/gtk/options/passwords_exceptions_window_gtk.cc b/chrome/browser/gtk/options/passwords_exceptions_window_gtk.cc new file mode 100644 index 0000000..195a662 --- /dev/null +++ b/chrome/browser/gtk/options/passwords_exceptions_window_gtk.cc @@ -0,0 +1,140 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/options/passwords_exceptions_window_gtk.h" + +#include + +#include "app/l10n_util.h" +#include "base/message_loop.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/gtk/options/exceptions_page_gtk.h" +#include "chrome/browser/gtk/options/passwords_page_gtk.h" +#include "chrome/browser/options_window.h" +#include "chrome/browser/profile.h" +#include "chrome/common/gtk_util.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" + +namespace { + +// Initial width of the passwords and exceptions window. +const int kPasswordsExceptionsWindowInitialWidth = 530; + +} // anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// +// PasswordsExceptionsWindowGtk +// +// The contents of the Passwords and Exceptions dialog window. + +class PasswordsExceptionsWindowGtk { + public: + explicit PasswordsExceptionsWindowGtk(Profile* profile); + ~PasswordsExceptionsWindowGtk(); + + void Show(); + + private: + static void OnWindowDestroy(GtkWidget* widget, + PasswordsExceptionsWindowGtk* window); + + // The passwords and exceptions dialog. + GtkWidget *dialog_; + + // The container of the password and exception pages. + GtkWidget *notebook_; + + // The Profile associated with these passwords and exceptions. + Profile* profile_; + + // The passwords page. + PasswordsPageGtk passwords_page_; + + // The exceptions page. + ExceptionsPageGtk exceptions_page_; + + DISALLOW_COPY_AND_ASSIGN(PasswordsExceptionsWindowGtk); +}; + +// The singleton passwords and exceptions window object. +static PasswordsExceptionsWindowGtk* passwords_exceptions_window = NULL; + +/////////////////////////////////////////////////////////////////////////////// +// PasswordsExceptionsWindowGtk, public: + +PasswordsExceptionsWindowGtk::PasswordsExceptionsWindowGtk(Profile* profile) + : profile_(profile), + passwords_page_(profile_), + exceptions_page_(profile_) { + dialog_ = gtk_dialog_new_with_buttons( + l10n_util::GetStringUTF8(IDS_PASSWORDS_EXCEPTIONS_WINDOW_TITLE).c_str(), + // Passwords and exceptions window is shared between all browser windows. + NULL, + // Non-modal. + GTK_DIALOG_NO_SEPARATOR, + GTK_STOCK_CLOSE, + GTK_RESPONSE_CLOSE, + NULL); + gtk_window_set_default_size(GTK_WINDOW(dialog_), + kPasswordsExceptionsWindowInitialWidth, -1); + gtk_box_set_spacing(GTK_BOX(GTK_DIALOG(dialog_)->vbox), + gtk_util::kContentAreaSpacing); + gtk_util::SetWindowIcon(GTK_WINDOW(dialog_)); + + notebook_ = gtk_notebook_new(); + + gtk_notebook_append_page( + GTK_NOTEBOOK(notebook_), + passwords_page_.get_page_widget(), + gtk_label_new(l10n_util::GetStringUTF8( + IDS_PASSWORDS_SHOW_PASSWORDS_TAB_TITLE).c_str())); + + gtk_notebook_append_page( + GTK_NOTEBOOK(notebook_), + exceptions_page_.get_page_widget(), + gtk_label_new(l10n_util::GetStringUTF8( + IDS_PASSWORDS_EXCEPTIONS_TAB_TITLE).c_str())); + + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog_)->vbox), notebook_); + + // We only have one button and don't do any special handling, so just hook it + // directly to gtk_widget_destroy. + g_signal_connect(dialog_, "response", G_CALLBACK(gtk_widget_destroy), NULL); + + g_signal_connect(dialog_, "destroy", G_CALLBACK(OnWindowDestroy), this); + + gtk_widget_show_all(dialog_); +} + +PasswordsExceptionsWindowGtk::~PasswordsExceptionsWindowGtk() { +} + +void PasswordsExceptionsWindowGtk::Show() { + // Bring options window to front if it already existed and isn't already + // in front + gtk_window_present(GTK_WINDOW(dialog_)); +} + +/////////////////////////////////////////////////////////////////////////////// +// PasswordsExceptionsWindowGtk, private: + +// static +void PasswordsExceptionsWindowGtk::OnWindowDestroy(GtkWidget* widget, + PasswordsExceptionsWindowGtk* window) { + passwords_exceptions_window = NULL; + MessageLoop::current()->DeleteSoon(FROM_HERE, window); +} + +/////////////////////////////////////////////////////////////////////////////// +// Factory/finder method: + +void ShowPasswordsExceptionsWindow(Profile* profile) { + DCHECK(profile); + // If there's already an existing passwords and exceptions window, use it. + if (!passwords_exceptions_window) { + passwords_exceptions_window = new PasswordsExceptionsWindowGtk(profile); + } + passwords_exceptions_window->Show(); +} diff --git a/chrome/browser/gtk/options/passwords_exceptions_window_gtk.h b/chrome/browser/gtk/options/passwords_exceptions_window_gtk.h new file mode 100644 index 0000000..cca03f9 --- /dev/null +++ b/chrome/browser/gtk/options/passwords_exceptions_window_gtk.h @@ -0,0 +1,12 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GTK_OPTIONS_PASSWORDS_EXCEPTIONS_WINDOW_H_ +#define CHROME_BROWSER_GTK_OPTIONS_PASSWORDS_EXCEPTIONS_WINDOW_H_ + +class Profile; + +void ShowPasswordsExceptionsWindow(Profile* profile); + +#endif // #ifndef CHROME_BROWSER_GTK_OPTIONS_PASSWORDS_EXCEPTIONS_WINDOW_H_ diff --git a/chrome/browser/gtk/options/passwords_page_gtk.cc b/chrome/browser/gtk/options/passwords_page_gtk.cc new file mode 100644 index 0000000..4d6692c --- /dev/null +++ b/chrome/browser/gtk/options/passwords_page_gtk.cc @@ -0,0 +1,349 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/gtk/options/passwords_page_gtk.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/gfx/gtk_util.h" +#include "chrome/common/gtk_util.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/url_constants.h" +#include "grit/app_resources.h" +#include "grit/chromium_strings.h" +#include "grit/generated_resources.h" +#include "net/base/net_util.h" + +namespace { + +// Initial width of the first column. +const int kSiteColumnInitialSize = 212; + +// Column ids for |password_list_store_|. +enum { + COL_SITE, + COL_USERNAME, + COL_COUNT, +}; + +// Get the row number corresponding to |path|. +gint GetRowNumForPath(GtkTreePath* path) { + gint* indices = gtk_tree_path_get_indices(path); + if (!indices) { + NOTREACHED(); + return -1; + } + return indices[0]; +} + +// Get the row number corresponding to |iter|. +gint GetRowNumForIter(GtkTreeModel* model, GtkTreeIter* iter) { + GtkTreePath* path = gtk_tree_model_get_path(model, iter); + int row = GetRowNumForPath(path); + gtk_tree_path_free(path); + return row; +} + +// Get the row number in the child tree model corresponding to |sort_path| in +// the parent tree model. +gint GetTreeSortChildRowNumForPath(GtkTreeModel* sort_model, + GtkTreePath* sort_path) { + GtkTreePath *child_path = gtk_tree_model_sort_convert_path_to_child_path( + GTK_TREE_MODEL_SORT(sort_model), sort_path); + int row = GetRowNumForPath(child_path); + gtk_tree_path_free(child_path); + return row; +} + +} // anonymous namespace + +/////////////////////////////////////////////////////////////////////////////// +// PasswordsPageGtk, public: + +PasswordsPageGtk::PasswordsPageGtk(Profile* profile) + : populater(this), password_showing_(false), profile_(profile) { + + remove_button_ = gtk_button_new_with_label( + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_REMOVE_BUTTON).c_str()); + gtk_widget_set_sensitive(remove_button_, FALSE); + g_signal_connect(G_OBJECT(remove_button_), "clicked", + G_CALLBACK(OnRemoveButtonClicked), this); + remove_all_button_ = gtk_button_new_with_label(l10n_util::GetStringUTF8( + IDS_PASSWORDS_PAGE_VIEW_REMOVE_ALL_BUTTON).c_str()); + gtk_widget_set_sensitive(remove_all_button_, FALSE); + g_signal_connect(G_OBJECT(remove_all_button_), "clicked", + G_CALLBACK(OnRemoveAllButtonClicked), this); + + show_password_button_ = gtk_button_new_with_label( + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON).c_str()); + GtkRequisition hide_size, show_size; + // Get the size request of the button with the "hide password" text. + gtk_widget_size_request(show_password_button_, &hide_size); + gtk_button_set_label(GTK_BUTTON(show_password_button_), + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); + // Get the size request of the button with the "show password" text. + gtk_widget_size_request(show_password_button_, &show_size); + // Determine the maximum width and height. + if (hide_size.width > show_size.width) + show_size.width = hide_size.width; + if (hide_size.height > show_size.height) + show_size.height = hide_size.height; + // Force the button to be large enough for both labels. + gtk_widget_set_size_request(show_password_button_, show_size.width, + show_size.height); + gtk_widget_set_sensitive(show_password_button_, FALSE); + g_signal_connect(G_OBJECT(show_password_button_), "clicked", + G_CALLBACK(OnShowPasswordButtonClicked), this); + + password_ = gtk_label_new(""); + + GtkWidget* buttons = gtk_vbox_new(FALSE, gtk_util::kControlSpacing); + gtk_box_pack_start(GTK_BOX(buttons), remove_button_, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(buttons), remove_all_button_, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(buttons), show_password_button_, FALSE, FALSE, 0); + gtk_box_pack_start(GTK_BOX(buttons), password_, FALSE, FALSE, 0); + + GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window), + GTK_SHADOW_ETCHED_IN); + + // Sets password_tree_ among other things. + InitPasswordTree(); + gtk_container_add(GTK_CONTAINER(scroll_window), password_tree_); + + page_ = gtk_hbox_new(FALSE, gtk_util::kControlSpacing); + gtk_container_set_border_width(GTK_CONTAINER(page_), + gtk_util::kContentAreaBorder); + gtk_box_pack_end(GTK_BOX(page_), buttons, FALSE, FALSE, 0); + gtk_box_pack_end(GTK_BOX(page_), scroll_window, TRUE, TRUE, 0); +} + +PasswordsPageGtk::~PasswordsPageGtk() { +} + +/////////////////////////////////////////////////////////////////////////////// +// PasswordsPageGtk, private: + +void PasswordsPageGtk::InitPasswordTree() { + password_list_store_ = gtk_list_store_new(COL_COUNT, + G_TYPE_STRING, + G_TYPE_STRING); + password_list_sort_ = gtk_tree_model_sort_new_with_model( + GTK_TREE_MODEL(password_list_store_)); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(password_list_sort_), + COL_SITE, CompareSite, this, NULL); + gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(password_list_sort_), + COL_USERNAME, CompareUsername, this, NULL); + password_tree_ = gtk_tree_view_new_with_model(password_list_sort_); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(password_tree_), TRUE); + + password_selection_ = gtk_tree_view_get_selection( + GTK_TREE_VIEW(password_tree_)); + gtk_tree_selection_set_mode(password_selection_, + GTK_SELECTION_SINGLE); + g_signal_connect(G_OBJECT(password_selection_), "changed", + G_CALLBACK(OnPasswordSelectionChanged), this); + + GtkTreeViewColumn* column = gtk_tree_view_column_new_with_attributes( + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SITE_COLUMN).c_str(), + gtk_cell_renderer_text_new(), + "text", COL_SITE, + NULL); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_fixed_width(column, kSiteColumnInitialSize); + gtk_tree_view_column_set_sort_column_id(column, COL_SITE); + gtk_tree_view_append_column(GTK_TREE_VIEW(password_tree_), column); + + column = gtk_tree_view_column_new_with_attributes( + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_USERNAME_COLUMN).c_str(), + gtk_cell_renderer_text_new(), + "text", COL_USERNAME, + NULL); + gtk_tree_view_column_set_sort_column_id(column, COL_USERNAME); + gtk_tree_view_append_column(GTK_TREE_VIEW(password_tree_), column); + populater.populate(); +} + +PasswordStore* PasswordsPageGtk::GetPasswordStore() { + return profile_->GetPasswordStore(Profile::EXPLICIT_ACCESS); +} + +void PasswordsPageGtk::SetPasswordList( + const std::vector& result) { + std::wstring languages = + profile_->GetPrefs()->GetString(prefs::kAcceptLanguages); + gtk_list_store_clear(password_list_store_); + password_list_.resize(result.size()); + for (size_t i = 0; i < result.size(); ++i) { + password_list_[i] = *result[i]; + std::wstring formatted = net::FormatUrl(result[i]->origin, languages, + false, UnescapeRule::NONE, + NULL, NULL); + std::string site = WideToUTF8(formatted); + std::string user = WideToUTF8(result[i]->username_value); + GtkTreeIter iter; + gtk_list_store_insert_with_values(password_list_store_, &iter, (gint) i, + COL_SITE, site.c_str(), + COL_USERNAME, user.c_str(), -1); + } + gtk_widget_set_sensitive(remove_all_button_, result.size() > 0); +} + +// static +void PasswordsPageGtk::OnRemoveButtonClicked(GtkButton* widget, + PasswordsPageGtk* page) { + GtkTreeIter iter; + if (!gtk_tree_selection_get_selected(page->password_selection_, + NULL, &iter)) { + NOTREACHED(); + return; + } + + GtkTreePath* path = gtk_tree_model_get_path( + GTK_TREE_MODEL(page->password_list_sort_), &iter); + gint index = GetTreeSortChildRowNumForPath(page->password_list_sort_, path); + gtk_tree_path_free(path); + + GtkTreeIter child_iter; + gtk_tree_model_sort_convert_iter_to_child_iter( + GTK_TREE_MODEL_SORT(page->password_list_sort_), &child_iter, &iter); + + // Remove from GTK list, DB, and vector. + gtk_list_store_remove(page->password_list_store_, &child_iter); + page->GetPasswordStore()->RemoveLogin(page->password_list_[index]); + page->password_list_.erase(page->password_list_.begin() + index); + + gtk_widget_set_sensitive(page->remove_all_button_, + page->password_list_.size() > 0); +} + +// static +void PasswordsPageGtk::OnRemoveAllButtonClicked(GtkButton* widget, + PasswordsPageGtk* page) { + GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(page->page_)); + GtkWidget* confirm = gtk_message_dialog_new(window, + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_QUESTION, + GTK_BUTTONS_YES_NO, + l10n_util::GetStringUTF8( + IDS_PASSWORDS_PAGE_VIEW_TEXT_DELETE_ALL_PASSWORDS).c_str()); + gtk_window_set_modal(GTK_WINDOW(confirm), TRUE); + gtk_window_set_title(GTK_WINDOW(confirm), l10n_util::GetStringUTF8( + IDS_PASSWORDS_PAGE_VIEW_CAPTION_DELETE_ALL_PASSWORDS).c_str()); + g_signal_connect(confirm, "response", G_CALLBACK(OnRemoveAllConfirmResponse), + page); + gtk_widget_show_all(confirm); +} + +// static +void PasswordsPageGtk::OnRemoveAllConfirmResponse(GtkDialog* confirm, + gint response, + PasswordsPageGtk* page) { + bool confirmed = false; + switch (response) { + case GTK_RESPONSE_YES: + confirmed = true; + break; + default: + break; + } + gtk_widget_destroy(GTK_WIDGET(confirm)); + if (!confirmed) + return; + + // Remove from GTK list, DB, and vector. + PasswordStore* store = page->GetPasswordStore(); + gtk_list_store_clear(page->password_list_store_); + for (size_t i = 0; i < page->password_list_.size(); ++i) { + store->RemoveLogin(page->password_list_[i]); + } + page->password_list_.clear(); + gtk_widget_set_sensitive(page->remove_all_button_, FALSE); +} + +// static +void PasswordsPageGtk::OnShowPasswordButtonClicked(GtkButton* widget, + PasswordsPageGtk* page) { + page->password_showing_ = !page->password_showing_; + if (!page->password_showing_) { + // Hide the password. + gtk_label_set_text(GTK_LABEL(page->password_), ""); + gtk_button_set_label(GTK_BUTTON(page->show_password_button_), + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); + return; + } + // Show the password. + GtkTreeIter iter; + if (!gtk_tree_selection_get_selected(page->password_selection_, + NULL, &iter)) { + NOTREACHED(); + return; + } + GtkTreePath* path = gtk_tree_model_get_path( + GTK_TREE_MODEL(page->password_list_sort_), &iter); + gint index = GetTreeSortChildRowNumForPath(page->password_list_sort_, path); + gtk_tree_path_free(path); + std::string pass = WideToUTF8(page->password_list_[index].password_value); + gtk_label_set_text(GTK_LABEL(page->password_), pass.c_str()); + gtk_button_set_label(GTK_BUTTON(page->show_password_button_), + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_HIDE_BUTTON).c_str()); +} + +// static +void PasswordsPageGtk::OnPasswordSelectionChanged(GtkTreeSelection* selection, + PasswordsPageGtk* page) { + // No matter how the selection changed, we want to hide the old password. + gtk_label_set_text(GTK_LABEL(page->password_), ""); + gtk_button_set_label(GTK_BUTTON(page->show_password_button_), + l10n_util::GetStringUTF8(IDS_PASSWORDS_PAGE_VIEW_SHOW_BUTTON).c_str()); + page->password_showing_ = false; + + GtkTreeIter iter; + if (!gtk_tree_selection_get_selected(selection, NULL, &iter)) { + gtk_widget_set_sensitive(page->show_password_button_, FALSE); + gtk_widget_set_sensitive(page->remove_button_, FALSE); + return; + } + gtk_widget_set_sensitive(page->show_password_button_, TRUE); + gtk_widget_set_sensitive(page->remove_button_, TRUE); +} + +// static +gint PasswordsPageGtk::CompareSite(GtkTreeModel* model, + GtkTreeIter* a, GtkTreeIter* b, + gpointer window) { + int row1 = GetRowNumForIter(model, a); + int row2 = GetRowNumForIter(model, b); + PasswordsPageGtk* page = reinterpret_cast(window); + return page->password_list_[row1].origin.spec().compare( + page->password_list_[row2].origin.spec()); +} + +// static +gint PasswordsPageGtk::CompareUsername(GtkTreeModel* model, + GtkTreeIter* a, GtkTreeIter* b, + gpointer window) { + int row1 = GetRowNumForIter(model, a); + int row2 = GetRowNumForIter(model, b); + PasswordsPageGtk* page = reinterpret_cast(window); + return page->password_list_[row1].username_value.compare( + page->password_list_[row2].username_value); +} + +void PasswordsPageGtk::PasswordListPopulater::populate() { + DCHECK(!pending_login_query_); + PasswordStore* store = page_->GetPasswordStore(); + pending_login_query_ = store->GetAutofillableLogins(this); +} + +void PasswordsPageGtk::PasswordListPopulater::OnPasswordStoreRequestDone( + int handle, const std::vector& result) { + DCHECK_EQ(pending_login_query_, handle); + pending_login_query_ = 0; + page_->SetPasswordList(result); +} diff --git a/chrome/browser/gtk/options/passwords_page_gtk.h b/chrome/browser/gtk/options/passwords_page_gtk.h new file mode 100644 index 0000000..06a0dcb --- /dev/null +++ b/chrome/browser/gtk/options/passwords_page_gtk.h @@ -0,0 +1,106 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_GTK_OPTIONS_PASSWORDS_PAGE_GTK_H_ +#define CHROME_BROWSER_GTK_OPTIONS_PASSWORDS_PAGE_GTK_H_ + +#include + +#include + +#include "chrome/browser/password_manager/password_store.h" +#include "chrome/browser/profile.h" + +class PasswordsPageGtk { + public: + explicit PasswordsPageGtk(Profile* profile); + ~PasswordsPageGtk(); + + GtkWidget* get_page_widget() const { + return page_; + } + + private: + // Initialize the password tree widget, setting the member variables. + void InitPasswordTree(); + + // The password store associated with the currently active profile. + PasswordStore* GetPasswordStore(); + + // Sets the password list contents to the given data. + void SetPasswordList(const std::vector& result); + + // Callback for the remove button. + static void OnRemoveButtonClicked(GtkButton* widget, PasswordsPageGtk* page); + + // Callback for the remove all button. + static void OnRemoveAllButtonClicked(GtkButton* widget, + PasswordsPageGtk* page); + + // Callback for the remove all confirmation response. + static void OnRemoveAllConfirmResponse(GtkDialog* confirm, gint response, + PasswordsPageGtk* page); + + // Callback for the show password button. + static void OnShowPasswordButtonClicked(GtkButton* widget, + PasswordsPageGtk* page); + + // Callback for selection changed events. + static void OnPasswordSelectionChanged(GtkTreeSelection* selection, + PasswordsPageGtk* page); + + // Sorting functions. + static gint CompareSite(GtkTreeModel* model, + GtkTreeIter* a, GtkTreeIter* b, + gpointer window); + static gint CompareUsername(GtkTreeModel* model, + GtkTreeIter* a, GtkTreeIter* b, + gpointer window); + + // A short class to mediate requests to the password store. + class PasswordListPopulater : public PasswordStoreConsumer { + public: + explicit PasswordListPopulater(PasswordsPageGtk* page) : page_(page) { + } + + // Send a query to the password store to populate a PasswordsPageGtk. + void populate(); + + // Send the password store's reply back to the PasswordsPageGtk. + virtual void OnPasswordStoreRequestDone( + int handle, const std::vector& result); + + private: + PasswordsPageGtk* page_; + int pending_login_query_; + }; + + // Password store consumer for populating the password list. + PasswordListPopulater populater; + + // Widgets for the buttons. + GtkWidget* remove_button_; + GtkWidget* remove_all_button_; + GtkWidget* show_password_button_; + + // Widget for the shown password + GtkWidget* password_; + bool password_showing_; + + // Widgets for the password table. + GtkWidget* password_tree_; + GtkListStore* password_list_store_; + GtkTreeModel* password_list_sort_; + GtkTreeSelection* password_selection_; + + // The parent GtkHBox widget and GtkWindow window. + GtkWidget* page_; + + Profile* profile_; + std::vector password_list_; + + DISALLOW_COPY_AND_ASSIGN(PasswordsPageGtk); +}; + +#endif // CHROME_BROWSER_GTK_OPTIONS_PASSWORDS_PAGE_GTK_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 252358b..f015bd8 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -1138,6 +1138,12 @@ 'browser/gtk/options/options_layout_gtk.cc', 'browser/gtk/options/options_layout_gtk.h', 'browser/gtk/options/options_window_gtk.cc', + 'browser/gtk/options/exceptions_page_gtk.cc', + 'browser/gtk/options/exceptions_page_gtk.h', + 'browser/gtk/options/passwords_exceptions_window_gtk.cc', + 'browser/gtk/options/passwords_exceptions_window_gtk.h', + 'browser/gtk/options/passwords_page_gtk.cc', + 'browser/gtk/options/passwords_page_gtk.h', 'browser/gtk/options/url_picker_dialog_gtk.cc', 'browser/gtk/options/url_picker_dialog_gtk.h', 'browser/gtk/sad_tab_gtk.cc', -- cgit v1.1