summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/gtk/options/content_page_gtk.cc3
-rw-r--r--chrome/browser/gtk/options/exceptions_page_gtk.cc226
-rw-r--r--chrome/browser/gtk/options/exceptions_page_gtk.h90
-rw-r--r--chrome/browser/gtk/options/options_window_gtk.cc36
-rw-r--r--chrome/browser/gtk/options/passwords_exceptions_window_gtk.cc140
-rw-r--r--chrome/browser/gtk/options/passwords_exceptions_window_gtk.h12
-rw-r--r--chrome/browser/gtk/options/passwords_page_gtk.cc349
-rw-r--r--chrome/browser/gtk/options/passwords_page_gtk.h106
-rw-r--r--chrome/chrome.gyp6
9 files changed, 949 insertions, 19 deletions
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<webkit_glue::PasswordForm*>& 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<ExceptionsPageGtk*>(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<webkit_glue::PasswordForm*>& 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 <gtk/gtk.h>
+
+#include <vector>
+
+#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<webkit_glue::PasswordForm*>& 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<webkit_glue::PasswordForm*>& 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<webkit_glue::PasswordForm> 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 <gtk/gtk.h>
-
#include "chrome/browser/options_window.h"
+#include <gtk/gtk.h>
+
#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 <gtk/gtk.h>
+
+#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<webkit_glue::PasswordForm*>& 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<PasswordsPageGtk*>(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<PasswordsPageGtk*>(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<webkit_glue::PasswordForm*>& 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 <gtk/gtk.h>
+
+#include <vector>
+
+#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<webkit_glue::PasswordForm*>& 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<webkit_glue::PasswordForm*>& 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<webkit_glue::PasswordForm> 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',