/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* * Copyright © 2013 Red Hat, Inc. * * This file is part of Epiphany. * * Epiphany is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Epiphany is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Epiphany. If not, see . */ #include "config.h" #include "clear-data-view.h" #include #include #include #include #include "ephy-history-service.h" #include "ephy-embed-shell.h" #include "ephy-settings.h" struct _ClearDataView { EphyDataView parent_instance; GtkWidget *treeview; GtkTreeModel *treestore; GtkTreeModelFilter *treemodelfilter; GCancellable *cancellable; }; enum { TYPE_COLUMN, ACTIVE_COLUMN, NAME_COLUMN, DATA_COLUMN, SENSITIVE_COLUMN }; G_DEFINE_TYPE (ClearDataView, clear_data_view, EPHY_TYPE_DATA_VIEW) #define PERSISTENT_DATA_TYPES WEBKIT_WEBSITE_DATA_COOKIES | \ WEBKIT_WEBSITE_DATA_DISK_CACHE | \ WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE | \ WEBKIT_WEBSITE_DATA_LOCAL_STORAGE | \ WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES | \ WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES | \ WEBKIT_WEBSITE_DATA_PLUGIN_DATA | \ WEBKIT_WEBSITE_DATA_HSTS_CACHE | \ WEBKIT_WEBSITE_DATA_ITP typedef struct { guint id; WebKitWebsiteDataTypes type; const char *name; } DataEntry; static const DataEntry data_entries[] = { { 0x001, WEBKIT_WEBSITE_DATA_COOKIES, N_("Cookies") }, { 0x002, WEBKIT_WEBSITE_DATA_DISK_CACHE, N_("HTTP disk cache") }, { 0x004, WEBKIT_WEBSITE_DATA_LOCAL_STORAGE, N_("Local storage data") }, { 0x008, WEBKIT_WEBSITE_DATA_OFFLINE_APPLICATION_CACHE, N_("Offline web application cache") }, { 0x010, WEBKIT_WEBSITE_DATA_INDEXEDDB_DATABASES, N_("IndexedDB databases") }, { 0x020, WEBKIT_WEBSITE_DATA_WEBSQL_DATABASES, N_("WebSQL databases") }, { 0x040, WEBKIT_WEBSITE_DATA_PLUGIN_DATA, N_("Plugins data") }, { 0x080, WEBKIT_WEBSITE_DATA_HSTS_CACHE, N_("HSTS policies cache") }, { 0x100, WEBKIT_WEBSITE_DATA_ITP, N_("Intelligent Tracking Prevention data") } }; static WebKitWebsiteDataManager * get_website_data_manger (void) { WebKitWebContext *web_context; web_context = ephy_embed_shell_get_web_context (ephy_embed_shell_get_default ()); return webkit_web_context_get_website_data_manager (web_context); } static void website_data_fetched_cb (WebKitWebsiteDataManager *manager, GAsyncResult *result, ClearDataView *clear_data_view) { GList *data_list; GtkTreeStore *treestore; GError *error = NULL; int active_items; data_list = webkit_website_data_manager_fetch_finish (manager, result, &error); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free (error); return; } ephy_data_view_set_is_loading (EPHY_DATA_VIEW (clear_data_view), FALSE); if (!data_list) { ephy_data_view_set_has_data (EPHY_DATA_VIEW (clear_data_view), FALSE); if (error) g_error_free (error); return; } ephy_data_view_set_has_data (EPHY_DATA_VIEW (clear_data_view), TRUE); active_items = g_settings_get_int (EPHY_SETTINGS_MAIN, EPHY_PREFS_ACTIVE_CLEAR_DATA_ITEMS); treestore = GTK_TREE_STORE (clear_data_view->treestore); for (guint i = 0; i < G_N_ELEMENTS (data_entries); i++) { GtkTreeIter parent_iter; gboolean empty = TRUE; gtk_tree_store_insert_with_values (treestore, &parent_iter, NULL, -1, TYPE_COLUMN, data_entries[i].type, ACTIVE_COLUMN, active_items & data_entries[i].id, NAME_COLUMN, _(data_entries[i].name), DATA_COLUMN, NULL, SENSITIVE_COLUMN, TRUE, -1); for (GList *l = data_list; l && l->data; l = g_list_next (l)) { WebKitWebsiteData *data = (WebKitWebsiteData *)l->data; if (!(webkit_website_data_get_types (data) & data_entries[i].type)) continue; gtk_tree_store_insert_with_values (treestore, NULL, &parent_iter, -1, TYPE_COLUMN, data_entries[i].type, ACTIVE_COLUMN, active_items & data_entries[i].id, NAME_COLUMN, webkit_website_data_get_name (data), DATA_COLUMN, webkit_website_data_ref (data), SENSITIVE_COLUMN, TRUE, -1); empty = FALSE; } if (empty) gtk_tree_store_remove (treestore, &parent_iter); } g_list_free_full (data_list, (GDestroyNotify)webkit_website_data_unref); } static gboolean all_children_visible (GtkTreeModel *model, GtkTreeIter *child_iter, GtkTreeModelFilter *filter) { GtkTreeIter filter_iter; gtk_tree_model_filter_convert_child_iter_to_iter (filter, &filter_iter, child_iter); return gtk_tree_model_iter_n_children (model, child_iter) == gtk_tree_model_iter_n_children (GTK_TREE_MODEL (filter), &filter_iter); } static void on_clear_button_clicked (ClearDataView *clear_data_view) { GtkTreeIter top_iter; WebKitWebsiteDataTypes types_to_clear = 0; GList *data_to_remove = NULL; WebKitWebsiteDataTypes types_to_remove = 0; if (!gtk_tree_model_get_iter_first (clear_data_view->treestore, &top_iter)) return; do { guint type; gboolean active; GtkTreeIter child_iter; gtk_tree_model_get (clear_data_view->treestore, &top_iter, TYPE_COLUMN, &type, ACTIVE_COLUMN, &active, -1); if (active && all_children_visible (clear_data_view->treestore, &top_iter, clear_data_view->treemodelfilter)) { types_to_clear |= type; } else if (gtk_tree_model_iter_children (clear_data_view->treestore, &child_iter, &top_iter)) { gboolean empty = TRUE; do { WebKitWebsiteData *data; GtkTreeIter filter_iter; if (gtk_tree_model_filter_convert_child_iter_to_iter (clear_data_view->treemodelfilter, &filter_iter, &child_iter)) { gtk_tree_model_get (clear_data_view->treestore, &child_iter, ACTIVE_COLUMN, &active, DATA_COLUMN, &data, -1); if (active) { data_to_remove = g_list_prepend (data_to_remove, data); empty = FALSE; } else webkit_website_data_unref (data); } } while (gtk_tree_model_iter_next (clear_data_view->treestore, &child_iter)); if (!empty) types_to_remove |= type; } } while (gtk_tree_model_iter_next (clear_data_view->treestore, &top_iter)); if (types_to_clear) { webkit_website_data_manager_clear (get_website_data_manger (), types_to_clear, 0, NULL, NULL, NULL); } if (types_to_remove) { webkit_website_data_manager_remove (get_website_data_manger (), types_to_remove, data_to_remove, NULL, NULL, NULL); } g_list_free_full (data_to_remove, (GDestroyNotify)webkit_website_data_unref); /* Reload tree */ ephy_data_view_set_is_loading (EPHY_DATA_VIEW (clear_data_view), TRUE); gtk_tree_store_clear (GTK_TREE_STORE (clear_data_view->treestore)); webkit_website_data_manager_fetch (get_website_data_manger (), PERSISTENT_DATA_TYPES, clear_data_view->cancellable, (GAsyncReadyCallback)website_data_fetched_cb, clear_data_view); } static gboolean any_item_checked (ClearDataView *self) { GtkTreeIter top_iter; if (!gtk_tree_model_get_iter_first (self->treestore, &top_iter)) return FALSE; do { gboolean active; GtkTreeIter child_iter; gtk_tree_model_get (self->treestore, &top_iter, ACTIVE_COLUMN, &active, -1); if (active) { return TRUE; } else if (gtk_tree_model_iter_children (self->treestore, &child_iter, &top_iter)) { do { GtkTreeIter filter_iter; if (gtk_tree_model_filter_convert_child_iter_to_iter (self->treemodelfilter, &filter_iter, &child_iter)) { gtk_tree_model_get (self->treestore, &child_iter, ACTIVE_COLUMN, &active, -1); if (active) return TRUE; } } while (gtk_tree_model_iter_next (self->treestore, &child_iter)); } } while (gtk_tree_model_iter_next (self->treestore, &top_iter)); return FALSE; } static void item_toggled_cb (GtkCellRendererToggle *renderer, const char *path_str, ClearDataView *clear_data_view) { GtkTreePath *path = gtk_tree_path_new_from_string (path_str); GtkTreeIter filter_iter, iter; gboolean active; gtk_tree_model_get_iter (GTK_TREE_MODEL (clear_data_view->treemodelfilter), &filter_iter, path); gtk_tree_model_filter_convert_iter_to_child_iter (clear_data_view->treemodelfilter, &iter, &filter_iter); gtk_tree_model_get (clear_data_view->treestore, &iter, ACTIVE_COLUMN, &active, -1); gtk_tree_store_set (GTK_TREE_STORE (clear_data_view->treestore), &iter, ACTIVE_COLUMN, !active, -1); if (gtk_tree_model_iter_has_child (clear_data_view->treestore, &iter)) { GtkTreeIter child_iter; g_autofree char *name = NULL; int active_items; active_items = g_settings_get_int (EPHY_SETTINGS_MAIN, EPHY_PREFS_ACTIVE_CLEAR_DATA_ITEMS); gtk_tree_model_get (clear_data_view->treestore, &iter, NAME_COLUMN, &name, -1); for (guint i = 0; i < G_N_ELEMENTS (data_entries); i++) { if (g_strcmp0 (gettext (data_entries[i].name), name) == 0) { if (active) active_items &= ~data_entries[i].id; else active_items |= data_entries[i].id; break; } } g_settings_set_int (EPHY_SETTINGS_MAIN, EPHY_PREFS_ACTIVE_CLEAR_DATA_ITEMS, active_items); gtk_tree_model_iter_children (clear_data_view->treestore, &child_iter, &iter); do { gtk_tree_store_set (GTK_TREE_STORE (clear_data_view->treestore), &child_iter, ACTIVE_COLUMN, !active, -1); } while (gtk_tree_model_iter_next (clear_data_view->treestore, &child_iter)); } else { GtkTreeIter parent_iter; /* Update the parent */ gtk_tree_model_iter_parent (clear_data_view->treestore, &parent_iter, &iter); if (active) { /* When unchecking a child we know the parent should be unchecked too */ gtk_tree_store_set (GTK_TREE_STORE (clear_data_view->treestore), &parent_iter, ACTIVE_COLUMN, FALSE, -1); } else { GtkTreeIter child_iter; gboolean all_active = TRUE; /* When checking a child, parent should be checked if all its children are */ gtk_tree_model_iter_children (clear_data_view->treestore, &child_iter, &parent_iter); do { gtk_tree_model_get (clear_data_view->treestore, &child_iter, ACTIVE_COLUMN, &all_active, -1); } while (all_active && gtk_tree_model_iter_next (clear_data_view->treestore, &child_iter)); if (all_active) { gtk_tree_store_set (GTK_TREE_STORE (clear_data_view->treestore), &parent_iter, ACTIVE_COLUMN, TRUE, -1); } } } gtk_tree_path_free (path); ephy_data_view_set_can_clear (EPHY_DATA_VIEW (clear_data_view), any_item_checked (clear_data_view)); } static void search_text_changed_cb (ClearDataView *clear_data_view) { gtk_tree_model_filter_refilter (clear_data_view->treemodelfilter); } static gboolean row_visible_func (GtkTreeModel *model, GtkTreeIter *iter, ClearDataView *clear_data_view) { const char *search_text; char *name; gboolean visible; if (gtk_tree_model_iter_has_child (model, iter)) return TRUE; search_text = ephy_data_view_get_search_text (EPHY_DATA_VIEW (clear_data_view)); if (!search_text || search_text[0] == '\0') return TRUE; gtk_tree_model_get (model, iter, NAME_COLUMN, &name, -1); visible = name && strstr (name, search_text); g_free (name); if (visible) { GtkTreeIter parent_iter; GtkTreePath *path; gtk_tree_model_iter_parent (model, &parent_iter, iter); path = gtk_tree_model_get_path (model, &parent_iter); gtk_tree_view_expand_row (GTK_TREE_VIEW (clear_data_view->treeview), path, FALSE); gtk_tree_path_free (path); } return visible; } static void clear_data_view_dispose (GObject *object) { ClearDataView *clear_data_view = (ClearDataView *)object; if (clear_data_view->cancellable) { g_cancellable_cancel (clear_data_view->cancellable); g_clear_object (&clear_data_view->cancellable); } G_OBJECT_CLASS (clear_data_view_parent_class)->dispose (object); } static void clear_data_view_class_init (ClearDataViewClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = clear_data_view_dispose; g_type_ensure (WEBKIT_TYPE_WEBSITE_DATA); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/epiphany/gtk/clear-data-view.ui"); gtk_widget_class_bind_template_child (widget_class, ClearDataView, treeview); gtk_widget_class_bind_template_child (widget_class, ClearDataView, treestore); gtk_widget_class_bind_template_child (widget_class, ClearDataView, treemodelfilter); gtk_widget_class_bind_template_callback (widget_class, item_toggled_cb); gtk_widget_class_bind_template_callback (widget_class, on_clear_button_clicked); gtk_widget_class_bind_template_callback (widget_class, search_text_changed_cb); } static void clear_data_view_init (ClearDataView *clear_data_view) { gtk_widget_init_template (GTK_WIDGET (clear_data_view)); gtk_tree_model_filter_set_visible_func (clear_data_view->treemodelfilter, (GtkTreeModelFilterVisibleFunc)row_visible_func, clear_data_view, NULL); ephy_data_view_set_is_loading (EPHY_DATA_VIEW (clear_data_view), TRUE); clear_data_view->cancellable = g_cancellable_new (); webkit_website_data_manager_fetch (get_website_data_manger (), PERSISTENT_DATA_TYPES, clear_data_view->cancellable, (GAsyncReadyCallback)website_data_fetched_cb, clear_data_view); }