Home | History | Annotate | Download | only in gtk
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/gtk/collected_cookies_gtk.h"
      6 
      7 #include <string>
      8 #include "base/prefs/pref_service.h"
      9 #include "chrome/browser/browsing_data/browsing_data_appcache_helper.h"
     10 #include "chrome/browser/browsing_data/browsing_data_cookie_helper.h"
     11 #include "chrome/browser/browsing_data/browsing_data_database_helper.h"
     12 #include "chrome/browser/browsing_data/browsing_data_file_system_helper.h"
     13 #include "chrome/browser/browsing_data/browsing_data_indexed_db_helper.h"
     14 #include "chrome/browser/browsing_data/browsing_data_local_storage_helper.h"
     15 #include "chrome/browser/browsing_data/browsing_data_server_bound_cert_helper.h"
     16 #include "chrome/browser/browsing_data/cookies_tree_model.h"
     17 #include "chrome/browser/browsing_data/local_data_container.h"
     18 #include "chrome/browser/chrome_notification_types.h"
     19 #include "chrome/browser/content_settings/cookie_settings.h"
     20 #include "chrome/browser/content_settings/local_shared_objects_container.h"
     21 #include "chrome/browser/content_settings/tab_specific_content_settings.h"
     22 #include "chrome/browser/infobars/infobar_service.h"
     23 #include "chrome/browser/profiles/profile.h"
     24 #include "chrome/browser/ui/browser_dialogs.h"
     25 #include "chrome/browser/ui/collected_cookies_infobar_delegate.h"
     26 #include "chrome/browser/ui/gtk/constrained_window_gtk.h"
     27 #include "chrome/browser/ui/gtk/gtk_chrome_cookie_view.h"
     28 #include "chrome/browser/ui/gtk/gtk_util.h"
     29 #include "chrome/common/pref_names.h"
     30 #include "components/web_modal/web_contents_modal_dialog_manager.h"
     31 #include "content/public/browser/notification_source.h"
     32 #include "content/public/browser/web_contents.h"
     33 #include "content/public/browser/web_contents_view.h"
     34 #include "grit/generated_resources.h"
     35 #include "net/cookies/canonical_cookie.h"
     36 #include "ui/base/gtk/gtk_hig_constants.h"
     37 #include "ui/base/l10n/l10n_util.h"
     38 
     39 using web_modal::WebContentsModalDialogManager;
     40 
     41 namespace {
     42 // Width and height of the cookie tree view.
     43 const int kTreeViewWidth = 450;
     44 const int kTreeViewHeight = 150;
     45 
     46 // The page numbers of the pages in the notebook.
     47 const gint kAllowedPageNumber = 0;
     48 const gint kBlockedPageNumber = 1;
     49 
     50 // Padding within the banner box.
     51 const int kBannerPadding = 3;
     52 
     53 // Returns the text to display in the info bar when content settings were
     54 // created.
     55 const std::string GetInfobarLabel(ContentSetting setting,
     56                                   bool multiple_domains_added,
     57                                   const string16& domain_name) {
     58   if (multiple_domains_added) {
     59     switch (setting) {
     60       case CONTENT_SETTING_BLOCK:
     61         return l10n_util::GetStringUTF8(
     62             IDS_COLLECTED_COOKIES_MULTIPLE_BLOCK_RULES_CREATED);
     63 
     64       case CONTENT_SETTING_ALLOW:
     65         return l10n_util::GetStringUTF8(
     66             IDS_COLLECTED_COOKIES_MULTIPLE_ALLOW_RULES_CREATED);
     67 
     68       case CONTENT_SETTING_SESSION_ONLY:
     69         return l10n_util::GetStringUTF8(
     70             IDS_COLLECTED_COOKIES_MULTIPLE_SESSION_RULES_CREATED);
     71 
     72       default:
     73         NOTREACHED();
     74         return std::string();
     75     }
     76   }
     77 
     78   switch (setting) {
     79     case CONTENT_SETTING_BLOCK:
     80       return l10n_util::GetStringFUTF8(
     81           IDS_COLLECTED_COOKIES_BLOCK_RULE_CREATED, domain_name);
     82 
     83     case CONTENT_SETTING_ALLOW:
     84       return l10n_util::GetStringFUTF8(
     85           IDS_COLLECTED_COOKIES_ALLOW_RULE_CREATED, domain_name);
     86 
     87     case CONTENT_SETTING_SESSION_ONLY:
     88       return l10n_util::GetStringFUTF8(
     89           IDS_COLLECTED_COOKIES_SESSION_RULE_CREATED, domain_name);
     90 
     91     default:
     92       NOTREACHED();
     93       return std::string();
     94   }
     95 }
     96 
     97 }  // namespace
     98 
     99 namespace chrome {
    100 
    101 // Declared in browser_dialogs.h so others don't have to depend on our header.
    102 void ShowCollectedCookiesDialog(content::WebContents* web_contents) {
    103   // Deletes itself on close.
    104   new CollectedCookiesGtk(
    105       web_contents->GetView()->GetTopLevelNativeWindow(),
    106       web_contents);
    107 }
    108 
    109 }  // namespace chrome
    110 
    111 CollectedCookiesGtk::CollectedCookiesGtk(GtkWindow* parent,
    112                                          content::WebContents* web_contents)
    113     : web_contents_(web_contents),
    114       status_changed_(false) {
    115   TabSpecificContentSettings* content_settings =
    116       TabSpecificContentSettings::FromWebContents(web_contents);
    117   registrar_.Add(this, chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN,
    118                  content::Source<TabSpecificContentSettings>(content_settings));
    119 
    120   Init();
    121 }
    122 
    123 void CollectedCookiesGtk::Init() {
    124   dialog_ = gtk_vbox_new(FALSE, ui::kContentAreaSpacing);
    125   gtk_box_set_spacing(GTK_BOX(dialog_), ui::kContentAreaSpacing);
    126   g_signal_connect(dialog_, "destroy", G_CALLBACK(OnDestroyThunk), this);
    127 
    128   GtkWidget* label = gtk_label_new(
    129       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_DIALOG_TITLE).c_str());
    130   gtk_box_pack_start(GTK_BOX(dialog_), label, TRUE, TRUE, 0);
    131 
    132   notebook_ = gtk_notebook_new();
    133   gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook_), GTK_POS_TOP);
    134   gtk_box_pack_start(GTK_BOX(dialog_), notebook_, TRUE, TRUE, 0);
    135 
    136   GtkWidget* allowed_pane = CreateAllowedPane();
    137   label = gtk_label_new(
    138       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_ALLOWED_COOKIES_TAB_LABEL)
    139           .c_str());
    140   gtk_widget_show(label);
    141   gtk_notebook_insert_page(GTK_NOTEBOOK(notebook_), allowed_pane, label,
    142                            kAllowedPageNumber);
    143 
    144   GtkWidget* blocked_pane = CreateBlockedPane();
    145   label = gtk_label_new(
    146       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_BLOCKED_COOKIES_TAB_LABEL)
    147           .c_str());
    148   gtk_widget_show(label);
    149   gtk_notebook_insert_page(GTK_NOTEBOOK(notebook_), blocked_pane, label,
    150                            kBlockedPageNumber);
    151   // Hook up the signal only after all pages are inserted, otherwise not
    152   // all member variables used in OnSwitchPage() will be properly initialized.
    153   g_signal_connect(notebook_, "switch-page", G_CALLBACK(OnSwitchPageThunk),
    154                    this);
    155 
    156   // Cookie info view.
    157   cookie_info_view_ = gtk_chrome_cookie_view_new(false);
    158   gtk_box_pack_start(GTK_BOX(dialog_), cookie_info_view_, TRUE, TRUE, 0);
    159   gtk_chrome_cookie_view_clear(GTK_CHROME_COOKIE_VIEW(cookie_info_view_));
    160   gtk_widget_show_all(cookie_info_view_);
    161 
    162   // Infobar.
    163   infobar_ = gtk_frame_new(NULL);
    164   GtkWidget* infobar_contents = gtk_hbox_new(FALSE, kBannerPadding);
    165   gtk_container_set_border_width(GTK_CONTAINER(infobar_contents),
    166                                  kBannerPadding);
    167   gtk_container_add(GTK_CONTAINER(infobar_), infobar_contents);
    168   GtkWidget* info_image =
    169       gtk_image_new_from_stock(GTK_STOCK_DIALOG_INFO,
    170                                GTK_ICON_SIZE_SMALL_TOOLBAR);
    171   gtk_box_pack_start(GTK_BOX(infobar_contents), info_image, FALSE, FALSE, 0);
    172   infobar_label_ = gtk_label_new(NULL);
    173   gtk_box_pack_start(
    174       GTK_BOX(infobar_contents), infobar_label_, FALSE, FALSE, 0);
    175   gtk_widget_show_all(infobar_);
    176   gtk_widget_set_no_show_all(infobar_, TRUE);
    177   gtk_widget_hide(infobar_);
    178   gtk_box_pack_start(GTK_BOX(dialog_), infobar_, TRUE, TRUE, 0);
    179 
    180   // Close button.
    181   GtkWidget* button_box = gtk_hbutton_box_new();
    182   gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_END);
    183   gtk_box_set_spacing(GTK_BOX(button_box), ui::kControlSpacing);
    184   gtk_box_pack_end(GTK_BOX(dialog_), button_box, FALSE, TRUE, 0);
    185   close_button_ = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
    186   gtk_button_set_label(GTK_BUTTON(close_button_),
    187                        l10n_util::GetStringUTF8(IDS_CLOSE).c_str());
    188   g_signal_connect(close_button_, "clicked", G_CALLBACK(OnCloseThunk), this);
    189   gtk_box_pack_end(GTK_BOX(button_box), close_button_, FALSE, TRUE, 0);
    190 
    191   // Show the dialog.
    192   allowed_cookies_tree_adapter_->Init();
    193   blocked_cookies_tree_adapter_->Init();
    194   EnableControls();
    195   ShowCookieInfo(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook_)));
    196   window_ = CreateWebContentsModalDialogGtk(dialog_, close_button_);
    197 
    198   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
    199       WebContentsModalDialogManager::FromWebContents(web_contents_);
    200   web_contents_modal_dialog_manager->ShowDialog(window_);
    201 }
    202 
    203 GtkWidget* CollectedCookiesGtk::CreateAllowedPane() {
    204   GtkWidget* cookie_list_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
    205 
    206   GtkWidget* label = gtk_label_new(
    207       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_ALLOWED_COOKIES_LABEL).
    208           c_str());
    209   gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    210   gtk_box_pack_start(GTK_BOX(cookie_list_vbox), label, FALSE, FALSE,
    211                      ui::kControlSpacing);
    212 
    213   GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL);
    214   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window),
    215                                  GTK_POLICY_AUTOMATIC,
    216                                  GTK_POLICY_AUTOMATIC);
    217   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window),
    218                                       GTK_SHADOW_ETCHED_IN);
    219   gtk_box_pack_start(GTK_BOX(cookie_list_vbox), scroll_window, TRUE, TRUE, 0);
    220 
    221   TabSpecificContentSettings* content_settings =
    222       TabSpecificContentSettings::FromWebContents(web_contents_);
    223 
    224   const LocalSharedObjectsContainer& allowed_data =
    225       content_settings->allowed_local_shared_objects();
    226   allowed_cookies_tree_model_ = allowed_data.CreateCookiesTreeModel();
    227 
    228   allowed_cookies_tree_adapter_.reset(
    229       new gtk_tree::TreeAdapter(this, allowed_cookies_tree_model_.get()));
    230   allowed_tree_ = gtk_tree_view_new_with_model(
    231       GTK_TREE_MODEL(allowed_cookies_tree_adapter_->tree_store()));
    232   gtk_widget_set_size_request(allowed_tree_, kTreeViewWidth, kTreeViewHeight);
    233   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(allowed_tree_), FALSE);
    234   gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(allowed_tree_), TRUE);
    235   gtk_container_add(GTK_CONTAINER(scroll_window), allowed_tree_);
    236 
    237   GtkTreeViewColumn* title_column = gtk_tree_view_column_new();
    238   GtkCellRenderer* pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
    239   gtk_tree_view_column_pack_start(title_column, pixbuf_renderer, FALSE);
    240   gtk_tree_view_column_add_attribute(title_column, pixbuf_renderer, "pixbuf",
    241                                      gtk_tree::TreeAdapter::COL_ICON);
    242   GtkCellRenderer* title_renderer = gtk_cell_renderer_text_new();
    243   gtk_tree_view_column_pack_start(title_column, title_renderer, TRUE);
    244   gtk_tree_view_column_add_attribute(title_column, title_renderer, "text",
    245                                      gtk_tree::TreeAdapter::COL_TITLE);
    246   gtk_tree_view_column_set_title(
    247       title_column, l10n_util::GetStringUTF8(
    248           IDS_COOKIES_DOMAIN_COLUMN_HEADER).c_str());
    249   gtk_tree_view_append_column(GTK_TREE_VIEW(allowed_tree_), title_column);
    250   g_signal_connect(allowed_tree_, "row-expanded",
    251                    G_CALLBACK(OnTreeViewRowExpandedThunk), this);
    252   allowed_selection_ =
    253       gtk_tree_view_get_selection(GTK_TREE_VIEW(allowed_tree_));
    254   gtk_tree_selection_set_mode(allowed_selection_, GTK_SELECTION_MULTIPLE);
    255   g_signal_connect(allowed_selection_, "changed",
    256                    G_CALLBACK(OnTreeViewSelectionChangeThunk), this);
    257 
    258   GtkWidget* button_box = gtk_hbutton_box_new();
    259   gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_START);
    260   gtk_box_set_spacing(GTK_BOX(button_box), ui::kControlSpacing);
    261   gtk_box_pack_start(GTK_BOX(cookie_list_vbox), button_box, FALSE, FALSE,
    262                      ui::kControlSpacing);
    263   block_allowed_cookie_button_ = gtk_button_new_with_label(
    264       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_BLOCK_BUTTON).c_str());
    265   g_signal_connect(block_allowed_cookie_button_, "clicked",
    266                    G_CALLBACK(OnBlockAllowedButtonClickedThunk), this);
    267   gtk_container_add(GTK_CONTAINER(button_box), block_allowed_cookie_button_);
    268 
    269   // Wrap the vbox inside an hbox so that we can specify padding along the
    270   // horizontal axis.
    271   GtkWidget* box = gtk_hbox_new(FALSE, 0);
    272   gtk_box_pack_start(GTK_BOX(box), cookie_list_vbox, TRUE, TRUE,
    273                      ui::kControlSpacing);
    274   return box;
    275 }
    276 
    277 GtkWidget* CollectedCookiesGtk::CreateBlockedPane() {
    278   PrefService* prefs = Profile::FromBrowserContext(
    279       web_contents_->GetBrowserContext())->GetPrefs();
    280 
    281   GtkWidget* cookie_list_vbox = gtk_vbox_new(FALSE, ui::kControlSpacing);
    282 
    283   GtkWidget* label = gtk_label_new(
    284       l10n_util::GetStringUTF8(
    285           prefs->GetBoolean(prefs::kBlockThirdPartyCookies) ?
    286               IDS_COLLECTED_COOKIES_BLOCKED_THIRD_PARTY_BLOCKING_ENABLED :
    287               IDS_COLLECTED_COOKIES_BLOCKED_COOKIES_LABEL).c_str());
    288   gtk_widget_set_size_request(label, kTreeViewWidth, -1);
    289   gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
    290   gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
    291   gtk_box_pack_start(GTK_BOX(cookie_list_vbox), label, TRUE, TRUE,
    292                      ui::kControlSpacing);
    293 
    294   GtkWidget* scroll_window = gtk_scrolled_window_new(NULL, NULL);
    295   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_window),
    296                                  GTK_POLICY_AUTOMATIC,
    297                                  GTK_POLICY_AUTOMATIC);
    298   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll_window),
    299                                       GTK_SHADOW_ETCHED_IN);
    300   gtk_box_pack_start(GTK_BOX(cookie_list_vbox), scroll_window, TRUE, TRUE, 0);
    301 
    302   TabSpecificContentSettings* content_settings =
    303       TabSpecificContentSettings::FromWebContents(web_contents_);
    304 
    305   const LocalSharedObjectsContainer& blocked_data =
    306       content_settings->blocked_local_shared_objects();
    307   blocked_cookies_tree_model_ = blocked_data.CreateCookiesTreeModel();
    308 
    309   blocked_cookies_tree_adapter_.reset(
    310       new gtk_tree::TreeAdapter(this, blocked_cookies_tree_model_.get()));
    311   blocked_tree_ = gtk_tree_view_new_with_model(
    312       GTK_TREE_MODEL(blocked_cookies_tree_adapter_->tree_store()));
    313   gtk_widget_set_size_request(blocked_tree_, kTreeViewWidth, kTreeViewHeight);
    314   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(blocked_tree_), FALSE);
    315   gtk_tree_view_set_enable_tree_lines(GTK_TREE_VIEW(blocked_tree_), TRUE);
    316   gtk_container_add(GTK_CONTAINER(scroll_window), blocked_tree_);
    317 
    318   GtkTreeViewColumn* title_column = gtk_tree_view_column_new();
    319   GtkCellRenderer* pixbuf_renderer = gtk_cell_renderer_pixbuf_new();
    320   gtk_tree_view_column_pack_start(title_column, pixbuf_renderer, FALSE);
    321   gtk_tree_view_column_add_attribute(title_column, pixbuf_renderer, "pixbuf",
    322                                      gtk_tree::TreeAdapter::COL_ICON);
    323   GtkCellRenderer* title_renderer = gtk_cell_renderer_text_new();
    324   gtk_tree_view_column_pack_start(title_column, title_renderer, TRUE);
    325   gtk_tree_view_column_add_attribute(title_column, title_renderer, "text",
    326                                      gtk_tree::TreeAdapter::COL_TITLE);
    327   gtk_tree_view_column_set_title(
    328       title_column, l10n_util::GetStringUTF8(
    329           IDS_COOKIES_DOMAIN_COLUMN_HEADER).c_str());
    330   gtk_tree_view_append_column(GTK_TREE_VIEW(blocked_tree_), title_column);
    331   g_signal_connect(blocked_tree_, "row-expanded",
    332                    G_CALLBACK(OnTreeViewRowExpandedThunk), this);
    333   blocked_selection_ =
    334       gtk_tree_view_get_selection(GTK_TREE_VIEW(blocked_tree_));
    335   gtk_tree_selection_set_mode(blocked_selection_, GTK_SELECTION_MULTIPLE);
    336   g_signal_connect(blocked_selection_, "changed",
    337                    G_CALLBACK(OnTreeViewSelectionChangeThunk), this);
    338 
    339   GtkWidget* button_box = gtk_hbutton_box_new();
    340   gtk_button_box_set_layout(GTK_BUTTON_BOX(button_box), GTK_BUTTONBOX_START);
    341   gtk_box_set_spacing(GTK_BOX(button_box), ui::kControlSpacing);
    342   gtk_box_pack_start(GTK_BOX(cookie_list_vbox), button_box, FALSE, FALSE,
    343                      ui::kControlSpacing);
    344   allow_blocked_cookie_button_ = gtk_button_new_with_label(
    345       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_ALLOW_BUTTON).c_str());
    346   g_signal_connect(allow_blocked_cookie_button_, "clicked",
    347                    G_CALLBACK(OnAllowBlockedButtonClickedThunk), this);
    348   gtk_container_add(GTK_CONTAINER(button_box), allow_blocked_cookie_button_);
    349   for_session_blocked_cookie_button_ = gtk_button_new_with_label(
    350       l10n_util::GetStringUTF8(IDS_COLLECTED_COOKIES_SESSION_ONLY_BUTTON).
    351           c_str());
    352   g_signal_connect(for_session_blocked_cookie_button_, "clicked",
    353                    G_CALLBACK(OnForSessionBlockedButtonClickedThunk), this);
    354   gtk_container_add(GTK_CONTAINER(button_box),
    355                     for_session_blocked_cookie_button_);
    356 
    357   // Wrap the vbox inside an hbox so that we can specify padding along the
    358   // horizontal axis.
    359   GtkWidget* box = gtk_hbox_new(FALSE, 0);
    360   gtk_box_pack_start(GTK_BOX(box), cookie_list_vbox, TRUE, TRUE,
    361                      ui::kControlSpacing);
    362   return box;
    363 }
    364 
    365 void CollectedCookiesGtk::ShowCookieInfo(gint current_page) {
    366   if (current_page == kAllowedPageNumber) {
    367     ShowSelectionInfo(allowed_selection_, allowed_cookies_tree_adapter_.get());
    368   } else if (current_page == kBlockedPageNumber) {
    369     ShowSelectionInfo(blocked_selection_, blocked_cookies_tree_adapter_.get());
    370   }
    371 }
    372 
    373 void CollectedCookiesGtk::ShowSelectionInfo(GtkTreeSelection* selection,
    374                                             gtk_tree::TreeAdapter* adapter) {
    375   // Check if one "cookie" node is selected.  Don't allow more than one.
    376   GtkTreeModel* model;
    377   GList* paths = gtk_tree_selection_get_selected_rows(selection, &model);
    378   if (g_list_length(paths) == 1) {
    379     GList* item = g_list_first(paths);
    380     GtkTreeIter iter;
    381     gtk_tree_model_get_iter(model, &iter,
    382                             reinterpret_cast<GtkTreePath*>(item->data));
    383     CookieTreeNode* node =
    384         static_cast<CookieTreeNode*>(adapter->GetNode(&iter));
    385     const CookieTreeNode::DetailedInfo detailed_info = node->GetDetailedInfo();
    386     if (detailed_info.node_type == CookieTreeNode::DetailedInfo::TYPE_COOKIE) {
    387       gtk_chrome_cookie_view_display_cookie(
    388           GTK_CHROME_COOKIE_VIEW(cookie_info_view_),
    389           detailed_info.cookie->Domain(),
    390           *detailed_info.cookie);
    391     } else {
    392       gtk_chrome_cookie_view_clear(GTK_CHROME_COOKIE_VIEW(cookie_info_view_));
    393     }
    394   } else {
    395     gtk_chrome_cookie_view_clear(GTK_CHROME_COOKIE_VIEW(cookie_info_view_));
    396   }
    397 
    398   g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL);
    399   g_list_free(paths);
    400 }
    401 
    402 CollectedCookiesGtk::~CollectedCookiesGtk() {
    403   gtk_widget_destroy(dialog_);
    404 }
    405 
    406 bool CollectedCookiesGtk::SelectionContainsHostNode(
    407     GtkTreeSelection* selection, gtk_tree::TreeAdapter* adapter) {
    408   // Check whether at least one "origin" node is selected.
    409   GtkTreeModel* model;
    410   GList* paths =
    411       gtk_tree_selection_get_selected_rows(selection, &model);
    412   bool contains_host_node = false;
    413   for (GList* item = paths; item; item = item->next) {
    414     GtkTreeIter iter;
    415     gtk_tree_model_get_iter(
    416         model, &iter, reinterpret_cast<GtkTreePath*>(item->data));
    417     CookieTreeNode* node =
    418         static_cast<CookieTreeNode*>(adapter->GetNode(&iter));
    419     if (node->GetDetailedInfo().node_type !=
    420         CookieTreeNode::DetailedInfo::TYPE_HOST)
    421       continue;
    422     CookieTreeHostNode* host_node = static_cast<CookieTreeHostNode*>(
    423         node);
    424     if (!host_node->CanCreateContentException())
    425       continue;
    426     contains_host_node = true;
    427   }
    428   g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL);
    429   g_list_free(paths);
    430   return contains_host_node;
    431 }
    432 
    433 void CollectedCookiesGtk::EnableControls() {
    434   // Update button states.
    435   bool enable_for_allowed_cookies =
    436       SelectionContainsHostNode(allowed_selection_,
    437                                   allowed_cookies_tree_adapter_.get());
    438   gtk_widget_set_sensitive(block_allowed_cookie_button_,
    439                            enable_for_allowed_cookies);
    440 
    441   bool enable_for_blocked_cookies =
    442       SelectionContainsHostNode(blocked_selection_,
    443                                   blocked_cookies_tree_adapter_.get());
    444   gtk_widget_set_sensitive(allow_blocked_cookie_button_,
    445                            enable_for_blocked_cookies);
    446   gtk_widget_set_sensitive(for_session_blocked_cookie_button_,
    447                            enable_for_blocked_cookies);
    448 }
    449 
    450 void CollectedCookiesGtk::Observe(int type,
    451                                   const content::NotificationSource& source,
    452                                   const content::NotificationDetails& details) {
    453   DCHECK(type == chrome::NOTIFICATION_COLLECTED_COOKIES_SHOWN);
    454   gtk_widget_destroy(window_);
    455 }
    456 
    457 void CollectedCookiesGtk::OnClose(GtkWidget* close_button) {
    458   if (status_changed_) {
    459     CollectedCookiesInfoBarDelegate::Create(
    460         InfoBarService::FromWebContents(web_contents_));
    461   }
    462   gtk_widget_destroy(window_);
    463 }
    464 
    465 void CollectedCookiesGtk::AddExceptions(GtkTreeSelection* selection,
    466                                         gtk_tree::TreeAdapter* adapter,
    467                                         ContentSetting setting) {
    468   GtkTreeModel* model;
    469   GList* paths =
    470       gtk_tree_selection_get_selected_rows(selection, &model);
    471   string16 last_domain_name;
    472   bool multiple_domains_added = false;
    473   for (GList* item = paths; item; item = item->next) {
    474     GtkTreeIter iter;
    475     gtk_tree_model_get_iter(
    476         model, &iter, reinterpret_cast<GtkTreePath*>(item->data));
    477     CookieTreeNode* node =
    478         static_cast<CookieTreeNode*>(adapter->GetNode(&iter));
    479     if (node->GetDetailedInfo().node_type !=
    480         CookieTreeNode::DetailedInfo::TYPE_HOST)
    481       continue;
    482     CookieTreeHostNode* host_node = static_cast<CookieTreeHostNode*>(
    483         node);
    484     if (host_node->CanCreateContentException()) {
    485       if (!last_domain_name.empty())
    486         multiple_domains_added = true;
    487       last_domain_name = host_node->GetTitle();
    488       Profile* profile =
    489           Profile::FromBrowserContext(web_contents_->GetBrowserContext());
    490       host_node->CreateContentException(
    491           CookieSettings::Factory::GetForProfile(profile).get(), setting);
    492     }
    493   }
    494   g_list_foreach(paths, reinterpret_cast<GFunc>(gtk_tree_path_free), NULL);
    495   g_list_free(paths);
    496   if (last_domain_name.empty()) {
    497     gtk_widget_hide(infobar_);
    498   } else {
    499     gtk_label_set_text(
    500         GTK_LABEL(infobar_label_), GetInfobarLabel(
    501             setting, multiple_domains_added, last_domain_name).c_str());
    502     gtk_widget_show(infobar_);
    503   }
    504   status_changed_ = true;
    505 }
    506 
    507 void CollectedCookiesGtk::OnBlockAllowedButtonClicked(GtkWidget* button) {
    508   AddExceptions(allowed_selection_, allowed_cookies_tree_adapter_.get(),
    509                 CONTENT_SETTING_BLOCK);
    510 }
    511 
    512 void CollectedCookiesGtk::OnAllowBlockedButtonClicked(GtkWidget* button) {
    513   AddExceptions(blocked_selection_, blocked_cookies_tree_adapter_.get(),
    514                 CONTENT_SETTING_ALLOW);
    515 }
    516 
    517 void CollectedCookiesGtk::OnForSessionBlockedButtonClicked(GtkWidget* button) {
    518   AddExceptions(blocked_selection_, blocked_cookies_tree_adapter_.get(),
    519                 CONTENT_SETTING_SESSION_ONLY);
    520 }
    521 
    522 void CollectedCookiesGtk::OnSwitchPage(GtkWidget* notebook,
    523                                        gpointer page,
    524                                        guint page_num) {
    525   EnableControls();
    526   ShowCookieInfo(page_num);
    527 }
    528 
    529 void CollectedCookiesGtk::OnTreeViewRowExpanded(GtkWidget* tree_view,
    530                                                 GtkTreeIter* iter,
    531                                                 GtkTreePath* path) {
    532   // When a row in the tree is expanded, expand all the children too.
    533   g_signal_handlers_block_by_func(
    534       tree_view, reinterpret_cast<gpointer>(OnTreeViewRowExpandedThunk), this);
    535   gtk_tree_view_expand_row(GTK_TREE_VIEW(tree_view), path, TRUE);
    536   g_signal_handlers_unblock_by_func(
    537       tree_view, reinterpret_cast<gpointer>(OnTreeViewRowExpandedThunk), this);
    538 }
    539 
    540 void CollectedCookiesGtk::OnTreeViewSelectionChange(GtkWidget* selection) {
    541   EnableControls();
    542   ShowCookieInfo(gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook_)));
    543 }
    544 
    545 void CollectedCookiesGtk::OnDestroy(GtkWidget* widget) {
    546   delete this;
    547 }
    548