aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/Makefile.am7
-rw-r--r--src/adblock-ui.c390
-rw-r--r--src/adblock-ui.h62
-rw-r--r--src/ephy-adblock-extension.c297
-rw-r--r--src/ephy-adblock-extension.h55
-rw-r--r--src/ephy-shell.c9
-rw-r--r--src/epiphany.gresource.xml1
-rw-r--r--src/resources/adblock.ui198
-rw-r--r--src/uri-tester.c895
-rw-r--r--src/uri-tester.h72
10 files changed, 1986 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index ada8e4716..2f3599538 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -17,7 +17,9 @@ header_DATA = \
$(INST_H_FILES)
NOINST_H_FILES = \
+ adblock-ui.h \
ephy-action-helper.h \
+ ephy-adblock-extension.h \
ephy-combined-stop-reload-action.h \
ephy-encoding-dialog.h \
ephy-encoding-menu.h \
@@ -37,6 +39,7 @@ NOINST_H_FILES = \
pdm-dialog.h \
popup-commands.h \
prefs-dialog.h \
+ uri-tester.h \
window-commands.h
INST_H_FILES = \
@@ -51,7 +54,9 @@ INST_H_FILES = \
$(NULL)
libephymain_la_SOURCES = \
+ adblock-ui.c \
ephy-action-helper.c \
+ ephy-adblock-extension.c \
ephy-completion-model.c \
ephy-completion-model.h \
ephy-combined-stop-reload-action.c \
@@ -79,6 +84,7 @@ libephymain_la_SOURCES = \
popup-commands.c \
prefs-dialog.c \
window-commands.c \
+ uri-tester.c \
$(INST_H_FILES) \
$(NOINST_H_FILES) \
$(NULL)
@@ -112,6 +118,7 @@ libephymain_la_CFLAGS = \
$(AM_CFLAGS)
UI_FILES = \
+ resources/adblock.ui \
resources/epiphany-bookmark-editor-ui.xml \
resources/epiphany-history-window-ui.xml \
resources/epiphany-ui.xml \
diff --git a/src/adblock-ui.c b/src/adblock-ui.c
new file mode 100644
index 000000000..d41ed53c1
--- /dev/null
+++ b/src/adblock-ui.c
@@ -0,0 +1,390 @@
+/*
+ * Copyright © 2011 Igalia S.L.
+ *
+ * This program 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 2, or (at your option)
+ * any later version.
+ *
+ * Some parts of this file based on the Midori's 'adblock' extension,
+ * licensed with the GNU Lesser General Public License 2.1, Copyright
+ * (C) 2009-2010 Christian Dywan <christian@twotoasts.de> and 2009
+ * Alexander Butenko <a.butenka@gmail.com>. Check Midori's web site
+ * at http://www.twotoasts.de
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "adblock-ui.h"
+
+#include "ephy-adblock.h"
+#include "ephy-adblock-manager.h"
+#include "ephy-debug.h"
+#include "ephy-embed-shell.h"
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#define ADBLOCK_UI_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE((object), TYPE_ADBLOCK_UI, AdblockUIPrivate))
+
+#define ADBLOCK_FILTER_VALID(__filter) \
+ (__filter && (g_str_has_prefix (__filter, "http") \
+ || g_str_has_prefix (__filter, "file")))
+
+struct _AdblockUIPrivate
+{
+ GtkWidget *dialog;
+
+ /* The dialog buttons. */
+ GtkEntry *new_filter;
+ GtkButton *add, *edit, *remove;
+
+ /* Data. */
+ GtkTreeView *treeview;
+ GtkTreeSelection *selection;
+ GtkListStore *store;
+
+ /* The uri tester. */
+ UriTester *tester;
+
+ /* Whether something has actually changed. */
+ gboolean dirty;
+};
+
+enum
+{
+ PROP_0,
+ PROP_TESTER,
+};
+
+enum
+{
+ COL_FILTER_URI,
+ N_COLUMNS
+};
+
+G_DEFINE_DYNAMIC_TYPE (AdblockUI, adblock_ui, EPHY_TYPE_DIALOG);
+
+/* Private functions. */
+
+static gboolean
+adblock_ui_foreach_save (GtkTreeModel *model,
+ GtkTreePath *path,
+ GtkTreeIter *iter,
+ GSList **filters)
+{
+ char *filter = NULL;
+
+ gtk_tree_model_get (model, iter, COL_FILTER_URI, &filter, -1);
+ *filters = g_slist_prepend (*filters, filter);
+
+ return FALSE;
+}
+
+static void
+adblock_ui_save (AdblockUI *dialog)
+{
+ GSList *filters = NULL;
+ gtk_tree_model_foreach (GTK_TREE_MODEL (dialog->priv->store),
+ (GtkTreeModelForeachFunc)adblock_ui_foreach_save,
+ &filters);
+
+ uri_tester_set_filters (dialog->priv->tester, filters);
+}
+
+static void
+adblock_ui_response_cb (GtkWidget *widget,
+ int response,
+ AdblockUI *dialog)
+{
+ if (response == GTK_RESPONSE_CLOSE && dialog->priv->dirty)
+ {
+ EphyAdBlockManager *manager;
+
+ adblock_ui_save (dialog);
+
+ /* Ask uri tester to reload all its patterns. */
+ uri_tester_reload (dialog->priv->tester);
+
+ /* Ask manager to emit a signal that rules have changed. */
+ manager = EPHY_ADBLOCK_MANAGER (ephy_embed_shell_get_adblock_manager (embed_shell));
+
+ g_signal_emit_by_name (manager, "rules_changed", NULL);
+ }
+
+ g_object_unref (dialog);
+}
+
+static void
+adblock_ui_add_filter (AdblockUI *dialog)
+{
+ GtkTreeIter iter;
+
+ const char *new_filter = gtk_entry_get_text (dialog->priv->new_filter);
+
+ if (ADBLOCK_FILTER_VALID (new_filter))
+ {
+ GtkListStore *store = dialog->priv->store;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, COL_FILTER_URI, new_filter, -1);
+
+ /* Makes the pattern field blank. */
+ gtk_entry_set_text (dialog->priv->new_filter, "");
+
+ dialog->priv->dirty = TRUE;
+ }
+ else
+ {
+ GtkWidget *error_dialog = NULL;
+ error_dialog = gtk_message_dialog_new (GTK_WINDOW (dialog->priv->dialog),
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ "%s",
+ _("Invalid filter"));
+ gtk_dialog_run (GTK_DIALOG (error_dialog));
+ gtk_widget_destroy (error_dialog);
+
+ gtk_entry_set_text (dialog->priv->new_filter, "");
+ }
+}
+
+static void
+adblock_ui_add_cb (GtkButton *button,
+ AdblockUI *dialog)
+{
+ adblock_ui_add_filter (dialog);
+}
+
+static void
+adblock_ui_edit_cb (GtkButton *button,
+ AdblockUI *dialog)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ selection = dialog->priv->selection;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ char* path = gtk_tree_model_get_string_from_iter (model, &iter);
+ GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
+ GtkTreeView *treeview = dialog->priv->treeview;
+ GtkTreeViewColumn *column = gtk_tree_view_get_column (treeview,
+ COL_FILTER_URI);
+
+ gtk_tree_view_set_cursor (treeview, tree_path, column, TRUE);
+ gtk_tree_path_free (tree_path);
+ g_free (path);
+ }
+}
+
+static void
+adblock_ui_remove_cb (GtkButton *button,
+ AdblockUI *dialog)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GtkTreeSelection *selection;
+
+ selection = dialog->priv->selection;
+
+ if (gtk_tree_selection_get_selected (selection, &model, &iter))
+ {
+ gtk_list_store_remove (GTK_LIST_STORE(model), &iter);
+ gtk_entry_set_text (dialog->priv->new_filter, "");
+
+ dialog->priv->dirty = TRUE;
+ }
+}
+
+static void
+adblock_ui_cell_edited_cb (GtkCellRendererText *cell,
+ char *path_string,
+ char *new_filter,
+ AdblockUI *dialog)
+{
+ GtkTreeModel *model = GTK_TREE_MODEL (dialog->priv->store);
+ GtkTreePath *path = gtk_tree_path_new_from_string (path_string);
+ GtkTreeIter iter;
+
+ gtk_tree_model_get_iter (model, &iter, path);
+ gtk_list_store_set (dialog->priv->store, &iter, COL_FILTER_URI, new_filter, -1);
+ gtk_tree_path_free (path);
+
+ dialog->priv->dirty = TRUE;
+}
+
+static void
+adblock_ui_build_treeview (AdblockUI *dialog)
+{
+ GtkCellRenderer *renderer;
+
+ dialog->priv->store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING);
+
+ renderer = gtk_cell_renderer_text_new ();
+ g_object_set(renderer, "editable", TRUE, NULL);
+ g_signal_connect(renderer,
+ "edited",
+ (GCallback) adblock_ui_cell_edited_cb,
+ (gpointer)dialog);
+
+ gtk_tree_view_insert_column_with_attributes (dialog->priv->treeview,
+ COL_FILTER_URI, _("Filter URI"),
+ renderer,
+ "text", COL_FILTER_URI,
+ NULL);
+
+ gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (dialog->priv->store),
+ COL_FILTER_URI,
+ GTK_SORT_ASCENDING);
+
+ gtk_tree_view_set_model (dialog->priv->treeview, GTK_TREE_MODEL (dialog->priv->store));
+ gtk_tree_view_set_search_column (dialog->priv->treeview, COL_FILTER_URI);
+
+ g_object_unref (dialog->priv->store);
+
+ dialog->priv->selection = gtk_tree_view_get_selection (dialog->priv->treeview);
+ gtk_tree_selection_set_mode (dialog->priv->selection, GTK_SELECTION_SINGLE);
+
+ dialog->priv->dirty = FALSE;
+}
+
+static void
+adblock_ui_populate_store (AdblockUI *dialog)
+{
+ GSList *filters = NULL;
+ GSList *item = NULL;
+ const char *filter_uri = NULL;
+ GtkTreeIter iter;
+
+ filters = uri_tester_get_filters (dialog->priv->tester);
+ for (item = filters; item; item = g_slist_next (item))
+ {
+ filter_uri = (const char *) item->data;
+
+ gtk_list_store_append (dialog->priv->store, &iter);
+ gtk_list_store_set (dialog->priv->store, &iter, COL_FILTER_URI, filter_uri, -1);
+ }
+}
+
+static void
+adblock_ui_init (AdblockUI *dialog)
+{
+ LOG ("AdblockUI initialising");
+ dialog->priv = ADBLOCK_UI_GET_PRIVATE (dialog);
+}
+
+static void
+adblock_ui_constructed (GObject *object)
+{
+ AdblockUI *dialog;
+ AdblockUIPrivate *priv;
+ EphyDialog *edialog;
+
+ dialog = ADBLOCK_UI (object);
+ edialog = EPHY_DIALOG (object);
+
+ priv = dialog->priv;
+
+ ephy_dialog_construct (EPHY_DIALOG (edialog),
+ "/org/gnome/epiphany/adblock.ui",
+ "adblock-ui",
+ GETTEXT_PACKAGE);
+
+ ephy_dialog_get_controls (edialog,
+ "adblock-ui", &priv->dialog,
+ "new_filter_entry", &priv->new_filter,
+ "treeview", &priv->treeview,
+ "add_button", &priv->add,
+ "edit_button", &priv->edit,
+ "remove_button", &priv->remove,
+ NULL);
+
+ g_signal_connect (priv->dialog, "response",
+ G_CALLBACK (adblock_ui_response_cb), dialog);
+
+ g_signal_connect (priv->add, "clicked",
+ G_CALLBACK (adblock_ui_add_cb), dialog);
+ g_signal_connect (priv->edit, "clicked",
+ G_CALLBACK (adblock_ui_edit_cb), dialog);
+ g_signal_connect (priv->remove, "clicked",
+ G_CALLBACK (adblock_ui_remove_cb), dialog);
+ g_signal_connect (priv->new_filter, "activate",
+ G_CALLBACK (adblock_ui_add_cb), dialog);
+
+ /* Build and fill. */
+ adblock_ui_build_treeview (dialog);
+ adblock_ui_populate_store (dialog);
+
+ /* Chain up. */
+ G_OBJECT_CLASS (adblock_ui_parent_class)->constructed (object);
+}
+
+static void
+adblock_ui_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ AdblockUI *dialog = ADBLOCK_UI (object);
+
+ switch (prop_id)
+ {
+ case PROP_TESTER:
+ dialog->priv->tester = g_value_get_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+adblock_ui_class_init (AdblockUIClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->constructed = adblock_ui_constructed;
+ object_class->set_property = adblock_ui_set_property;
+
+ g_object_class_install_property
+ (object_class,
+ PROP_TESTER,
+ g_param_spec_object ("tester",
+ "UriTester",
+ "UriTester",
+ TYPE_URI_TESTER,
+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_type_class_add_private (object_class, sizeof (AdblockUIPrivate));
+}
+
+static void adblock_ui_class_finalize (AdblockUIClass *klass)
+{
+}
+
+/* Public functions. */
+
+void adblock_ui_register (GTypeModule *module)
+{
+ adblock_ui_register_type (module);
+}
+
+AdblockUI *
+adblock_ui_new (UriTester *tester)
+{
+ return g_object_new (TYPE_ADBLOCK_UI,
+ "tester", tester,
+ NULL);
+}
diff --git a/src/adblock-ui.h b/src/adblock-ui.h
new file mode 100644
index 000000000..b308e849f
--- /dev/null
+++ b/src/adblock-ui.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2011 Igalia S.L.
+ *
+ * This program 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 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef ADBLOCK_UI_H
+#define ADBLOCK_UI_H
+
+#include "ephy-dialog.h"
+#include "uri-tester.h"
+
+#include <glib-object.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_ADBLOCK_UI (adblock_ui_get_type ())
+#define ADBLOCK_UI(o) (G_TYPE_CHECK_INSTANCE_CAST((o), TYPE_ADBLOCK_UI, AdblockUI))
+#define ADBLOCK_UI_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TYPE_ADBLOCK_UI, AdblockUIClass))
+#define IS_ADBLOCK_UI(o) (G_TYPE_CHECK_INSTANCE_TYPE((o), TYPE_ADBLOCK_UI))
+#define IS_ADBLOCK_UI_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE((k), TYPE_ADBLOCK_UI))
+#define ADBLOCK_UI_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), TYPE_ADBLOCK_UI, AdblockUIClass))
+
+typedef struct _AdblockUI AdblockUI;
+typedef struct _AdblockUIClass AdblockUIClass;
+typedef struct _AdblockUIPrivate AdblockUIPrivate;
+
+struct _AdblockUI
+{
+ EphyDialog parent;
+
+ /*< private >*/
+ AdblockUIPrivate *priv;
+};
+
+struct _AdblockUIClass
+{
+ EphyDialogClass parent_class;
+};
+
+GType adblock_ui_get_type (void);
+
+void adblock_ui_register (GTypeModule *module);
+
+AdblockUI *adblock_ui_new (UriTester *tester);
+
+G_END_DECLS
+
+#endif
diff --git a/src/ephy-adblock-extension.c b/src/ephy-adblock-extension.c
new file mode 100644
index 000000000..93068f2c5
--- /dev/null
+++ b/src/ephy-adblock-extension.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright © 2011 Igalia S.L.
+ *
+ * This program 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 2, or (at your option)
+ * any later version.
+ *
+ * Some parts of this file based on the previous 'adblock' extension,
+ * licensed with the GNU General Public License 2 and later versions,
+ * Copyright (C) 2003 Marco Pesenti Gritti, Christian Persch.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "ephy-adblock-extension.h"
+
+#include "adblock-ui.h"
+#include "ephy-adblock.h"
+#include "ephy-adblock-manager.h"
+#include "ephy-debug.h"
+#include "ephy-embed-shell.h"
+#include "ephy-extension.h"
+#include "ephy-file-helpers.h"
+#include "ephy-window.h"
+#include "uri-tester.h"
+
+#include <glib/gi18n-lib.h>
+#include <gtk/gtk.h>
+
+#define EPHY_ADBLOCK_EXTENSION_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_ADBLOCK_EXTENSION, EphyAdblockExtensionPrivate))
+
+#define WINDOW_DATA_KEY "EphyAdblockExtensionWindowData"
+#define STATUSBAR_EVBOX_KEY "EphyAdblockExtensionStatusbarEvbox"
+#define EXTENSION_KEY "EphyAdblockExtension"
+#define AD_BLOCK_ICON_NAME "ad-blocked"
+
+typedef struct
+{
+ EphyAdblockExtension *extension;
+ EphyWindow *window;
+
+ GtkActionGroup *action_group;
+ guint ui_id;
+} WindowData;
+
+struct EphyAdblockExtensionPrivate
+{
+ UriTester *tester;
+ AdblockUI *ui;
+};
+
+static void ephy_adblock_extension_iface_init (EphyExtensionIface *iface);
+static void ephy_adblock_adblock_iface_init (EphyAdBlockIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (EphyAdblockExtension,
+ ephy_adblock_extension,
+ G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (EPHY_TYPE_ADBLOCK,
+ ephy_adblock_adblock_iface_init)
+ G_IMPLEMENT_INTERFACE (EPHY_TYPE_EXTENSION,
+ ephy_adblock_extension_iface_init))
+
+/* Private functions. */
+
+static void
+ephy_adblock_extension_init (EphyAdblockExtension *extension)
+{
+ LOG ("EphyAdblockExtension initialising");
+
+ extension->priv = EPHY_ADBLOCK_EXTENSION_GET_PRIVATE (extension);
+ extension->priv->tester = uri_tester_new ();
+}
+
+static void
+ephy_adblock_extension_dispose (GObject *object)
+{
+ EphyAdblockExtension *extension = NULL;
+
+ LOG ("EphyAdblockExtension disposing");
+
+ extension = EPHY_ADBLOCK_EXTENSION (object);
+ g_clear_object (&extension->priv->ui);
+ g_clear_object (&extension->priv->tester);
+
+ G_OBJECT_CLASS (ephy_adblock_extension_parent_class)->dispose (object);
+}
+
+static void
+ephy_adblock_extension_finalize (GObject *object)
+{
+ LOG ("EphyAdblockExtension finalising");
+
+ G_OBJECT_CLASS (ephy_adblock_extension_parent_class)->finalize (object);
+}
+
+static void
+ephy_adblock_extension_class_init (EphyAdblockExtensionClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ephy_adblock_extension_dispose;
+ object_class->finalize = ephy_adblock_extension_finalize;
+
+ g_type_class_add_private (object_class, sizeof (EphyAdblockExtensionPrivate));
+}
+
+static gboolean
+ephy_adblock_impl_should_load (EphyAdBlock *blocker,
+ EphyEmbed *embed,
+ const char *url,
+ AdUriCheckType type)
+{
+ EphyAdblockExtension *self = NULL;
+ EphyWebView* web_view = NULL;
+ const char *address = NULL;
+
+ LOG ("ephy_adblock_impl_should_load checking %s", url);
+
+ self = EPHY_ADBLOCK_EXTENSION (blocker);
+ g_return_val_if_fail (self != NULL, TRUE);
+
+ web_view = ephy_embed_get_web_view (embed);
+ address = ephy_web_view_get_address (web_view);
+
+ return !uri_tester_test_uri (self->priv->tester, url, address, type);
+}
+
+static void
+ephy_adblock_impl_edit_rule (EphyAdBlock *blocker,
+ const char *url,
+ gboolean allowed)
+{
+ EphyAdblockExtension *self = NULL;
+ EphyAdblockExtensionPrivate *priv = NULL;
+
+ LOG ("ephy_adblock_impl_edit_rule %s with state %d", url, allowed);
+
+ self = EPHY_ADBLOCK_EXTENSION (blocker);
+ priv = self->priv;
+
+ if (priv->ui == NULL)
+ {
+ AdblockUI **ui;
+
+ /*
+ * TODO: url and allowed should be passed to the UI,
+ * so the user can actually do something with it.
+ */
+ priv->ui = adblock_ui_new (priv->tester);
+ ui = &priv->ui;
+
+ g_object_add_weak_pointer ((gpointer)priv->ui,
+ (gpointer *) ui);
+
+ ephy_dialog_set_parent (EPHY_DIALOG (priv->ui), NULL);
+ }
+
+ ephy_dialog_show (EPHY_DIALOG (priv->ui));
+}
+
+static void
+ephy_adblock_adblock_iface_init (EphyAdBlockIface *iface)
+{
+ iface->should_load = ephy_adblock_impl_should_load;
+ iface->edit_rule = ephy_adblock_impl_edit_rule;
+}
+
+static void
+ephy_adblock_extension_edit_cb (GtkAction *action, EphyWindow *window)
+{
+ WindowData *data = NULL;
+ EphyAdblockExtensionPrivate *priv = NULL;
+
+ data = g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+ g_return_if_fail (data != NULL);
+
+ priv = data->extension->priv;
+
+ if (priv->ui == NULL)
+ {
+ AdblockUI **ui;
+
+ priv->ui = adblock_ui_new (priv->tester);
+ ui = &priv->ui;
+
+ g_object_add_weak_pointer ((gpointer)priv->ui,
+ (gpointer *) ui);
+ }
+
+ ephy_dialog_set_parent (EPHY_DIALOG (priv->ui), GTK_WIDGET (window));
+ ephy_dialog_show (EPHY_DIALOG (priv->ui));
+}
+
+static const GtkActionEntry edit_entries[] = {
+ { "EphyAdblockExtensionEdit", NULL,
+ N_("Ad Blocker"), NULL,
+ N_("Configure Ad Blocker filters"),
+ G_CALLBACK (ephy_adblock_extension_edit_cb) }
+};
+
+static void
+impl_attach_window (EphyExtension *ext,
+ EphyWindow *window)
+{
+ WindowData *data = NULL;
+ GtkUIManager *manager = NULL;
+
+ /* Add adblock editor's menu entry. */
+ data = g_new (WindowData, 1);
+ g_object_set_data_full (G_OBJECT (window),
+ WINDOW_DATA_KEY,
+ data,
+ g_free);
+
+ data->extension = EPHY_ADBLOCK_EXTENSION (ext);
+ data->window = window;
+
+ data->action_group = gtk_action_group_new ("EphyAdblockExtension");
+ gtk_action_group_set_translation_domain (data->action_group,
+ GETTEXT_PACKAGE);
+ gtk_action_group_add_actions (data->action_group, edit_entries,
+ G_N_ELEMENTS(edit_entries), window);
+
+ manager = GTK_UI_MANAGER (ephy_window_get_ui_manager (window));
+
+ gtk_ui_manager_insert_action_group (manager, data->action_group, -1);
+
+ /* UI manager references the new action group. */
+ g_object_unref (data->action_group);
+
+ data->ui_id = gtk_ui_manager_new_merge_id (manager);
+
+ gtk_ui_manager_add_ui (manager,
+ data->ui_id,
+ "/ui/PagePopup/ExtensionsMenu",
+ "EphyAdblockExtensionEdit",
+ "EphyAdblockExtensionEdit",
+ GTK_UI_MANAGER_MENUITEM,
+ FALSE);
+
+ /* Remember the xtension attached to that window. */
+ g_object_set_data (G_OBJECT (window), EXTENSION_KEY, ext);
+}
+
+static void
+impl_detach_window (EphyExtension *ext,
+ EphyWindow *window)
+{
+ WindowData *data = NULL;
+ GtkUIManager *manager = NULL;
+
+ /* Remove editor UI. */
+ data = g_object_get_data (G_OBJECT (window), WINDOW_DATA_KEY);
+ g_assert (data != NULL);
+
+ manager = GTK_UI_MANAGER (ephy_window_get_ui_manager (window));
+
+ gtk_ui_manager_remove_ui (manager, data->ui_id);
+ gtk_ui_manager_remove_action_group (manager, data->action_group);
+
+ g_object_set_data (G_OBJECT (window), WINDOW_DATA_KEY, NULL);
+}
+
+static void
+impl_attach_tab (EphyExtension *ext,
+ EphyWindow *window,
+ EphyEmbed *embed)
+{
+
+}
+
+static void
+impl_detach_tab (EphyExtension *ext,
+ EphyWindow *window,
+ EphyEmbed *embed)
+{
+
+}
+
+static void
+ephy_adblock_extension_iface_init (EphyExtensionIface *iface)
+{
+ iface->attach_window = impl_attach_window;
+ iface->detach_window = impl_detach_window;
+ iface->attach_tab = impl_attach_tab;
+ iface->detach_tab = impl_detach_tab;
+}
+
diff --git a/src/ephy-adblock-extension.h b/src/ephy-adblock-extension.h
new file mode 100644
index 000000000..46bea1bca
--- /dev/null
+++ b/src/ephy-adblock-extension.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2011 Igalia S.L.
+ *
+ * This program 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 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef EPHY_ADBLOCK_EXTENSION_H
+#define EPHY_ADBLOCK_EXTENSION_H
+
+#include <glib-object.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define EPHY_TYPE_ADBLOCK_EXTENSION (ephy_adblock_extension_get_type ())
+#define EPHY_ADBLOCK_EXTENSION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), EPHY_TYPE_ADBLOCK_EXTENSION, EphyAdblockExtension))
+#define EPHY_ADBLOCK_EXTENSION_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), EPHY_TYPE_ADBLOCK_EXTENSION, EphyAdblockExtensionClass))
+#define EPHY_IS_ADBLOCK_EXTENSION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), EPHY_TYPE_ADBLOCK_EXTENSION))
+#define EPHY_IS_ADBLOCK_EXTENSION_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), EPHY_TYPE_ADBLOCK_EXTENSION))
+#define EPHY_ADBLOCK_EXTENSION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), EPHY_TYPE_ADBLOCK_EXTENSION, EphyAdblockExtensionClass))
+
+typedef struct EphyAdblockExtension EphyAdblockExtension;
+typedef struct EphyAdblockExtensionClass EphyAdblockExtensionClass;
+typedef struct EphyAdblockExtensionPrivate EphyAdblockExtensionPrivate;
+
+struct EphyAdblockExtensionClass
+{
+ GObjectClass parent_class;
+};
+
+struct EphyAdblockExtension
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ EphyAdblockExtensionPrivate *priv;
+};
+
+GType ephy_adblock_extension_get_type (void);
+
+G_END_DECLS
+
+#endif
diff --git a/src/ephy-shell.c b/src/ephy-shell.c
index 88f843e40..e88e9a086 100644
--- a/src/ephy-shell.c
+++ b/src/ephy-shell.c
@@ -23,6 +23,8 @@
#include "config.h"
#include "ephy-shell.h"
+#include "ephy-adblock-extension.h"
+#include "ephy-adblock-manager.h"
#include "ephy-bookmarks-editor.h"
#include "ephy-bookmarks-import.h"
#include "ephy-debug.h"
@@ -1055,6 +1057,8 @@ ephy_shell_get_prefs_dialog (EphyShell *shell)
void
_ephy_shell_create_instance (EphyEmbedShellMode mode)
{
+ EphyAdBlockManager *adblock_manager;
+
g_assert (ephy_shell == NULL);
ephy_shell = EPHY_SHELL (g_object_new (EPHY_TYPE_SHELL,
@@ -1063,6 +1067,11 @@ _ephy_shell_create_instance (EphyEmbedShellMode mode)
NULL));
/* FIXME weak ref */
g_assert (ephy_shell != NULL);
+
+ /* FIXME not the best place to have this */
+ adblock_manager = EPHY_ADBLOCK_MANAGER (ephy_embed_shell_get_adblock_manager (embed_shell));
+ ephy_adblock_manager_set_blocker (adblock_manager,
+ g_object_new (EPHY_TYPE_ADBLOCK_EXTENSION, NULL));
}
/**
diff --git a/src/epiphany.gresource.xml b/src/epiphany.gresource.xml
index b75646667..c04d5370b 100644
--- a/src/epiphany.gresource.xml
+++ b/src/epiphany.gresource.xml
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/org/gnome/epiphany">
+ <file preprocess="xml-stripblanks" compressed="true">adblock.ui</file>
<file preprocess="xml-stripblanks" compressed="true">epiphany.ui</file>
<file preprocess="xml-stripblanks" compressed="true">prefs-dialog.ui</file>
<file preprocess="xml-stripblanks">epiphany-application-menu.ui</file>
diff --git a/src/resources/adblock.ui b/src/resources/adblock.ui
new file mode 100644
index 000000000..883106926
--- /dev/null
+++ b/src/resources/adblock.ui
@@ -0,0 +1,198 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <!-- interface-requires gtk+ 2.6 -->
+ <object class="GtkDialog" id="adblock-ui">
+ <property name="can_focus">False</property>
+ <property name="border_width">5</property>
+ <property name="title" translatable="yes">Adblock Editor</property>
+ <property name="window_position">center-on-parent</property>
+ <property name="destroy_with_parent">True</property>
+ <property name="type_hint">dialog</property>
+ <property name="skip_taskbar_hint">True</property>
+ <property name="skip_pager_hint">True</property>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkLabel" id="description_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Type the address of a preconfigured filter list in the text entry to be added to the list. Find more lists at easylist.adblockplus.org</property>
+ <property name="use_markup">True</property>
+ <property name="wrap">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">6</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="close">
+ <property name="label">gtk-close</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="hbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkBox" id="left_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkEntry" id="new_filter_entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="invisible_char">●</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="treeview_scroller">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="treeview">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection1"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">4</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="right_vbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="filler_label">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="add_button">
+ <property name="label">gtk-add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="edit_button">
+ <property name="label">gtk-edit</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="remove_button">
+ <property name="label">gtk-remove</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_action_appearance">False</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="padding">4</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="padding">12</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-7">close</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/src/uri-tester.c b/src/uri-tester.c
new file mode 100644
index 000000000..bae25e43a
--- /dev/null
+++ b/src/uri-tester.c
@@ -0,0 +1,895 @@
+/*
+ * Copyright © 2011 Igalia S.L.
+ *
+ * This program 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 2, or (at your option)
+ * any later version.
+ *
+ * Some parts of this file based on the Midori's 'adblock' extension,
+ * licensed with the GNU Lesser General Public License 2.1, Copyright
+ * (C) 2009-2010 Christian Dywan <christian@twotoasts.de> and 2009
+ * Alexander Butenko <a.butenka@gmail.com>. Check Midori's web site
+ * at http://www.twotoasts.de
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include "uri-tester.h"
+
+#include "ephy-debug.h"
+#include "ephy-file-helpers.h"
+
+#include <gio/gio.h>
+#include <glib/gstdio.h>
+#include <string.h>
+#include <webkit/webkit.h>
+
+#define DEFAULT_FILTER_URL "http://adblockplus.mozdev.org/easylist/easylist.txt"
+#define FILTERS_LIST_FILENAME "filters.list"
+#define SIGNATURE_SIZE 8
+#define UPDATE_FREQUENCY 24 * 60 * 60 /* In seconds */
+
+#define URI_TESTER_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), TYPE_URI_TESTER, UriTesterPrivate))
+
+struct _UriTesterPrivate
+{
+ GSList *filters;
+
+ GHashTable *pattern;
+ GHashTable *keys;
+ GHashTable *optslist;
+ GHashTable *urlcache;
+
+ GString *blockcss;
+ GString *blockcssprivate;
+};
+
+enum
+{
+ PROP_0,
+ PROP_FILTERS,
+};
+
+G_DEFINE_TYPE (UriTester, uri_tester, G_TYPE_OBJECT);
+
+/* Private functions. */
+
+static void uri_tester_class_init (UriTesterClass *klass);
+static void uri_tester_init (UriTester *dialog);
+
+static GString *
+uri_tester_fixup_regexp (const char *prefix, char *src);
+
+static gboolean
+uri_tester_parse_file_at_uri (UriTester *tester, const char *fileuri);
+
+static char *
+uri_tester_ensure_data_dir ()
+{
+ char *folder = NULL;
+
+ /* Ensure adblock's dir is there. */
+ folder = g_build_filename (ephy_dot_dir (), "extensions", "data", "adblock", NULL);
+ g_mkdir_with_parents (folder, 0700);
+
+ return folder;
+}
+
+static char*
+uri_tester_get_fileuri_for_url (const char *url)
+{
+ char *filename = NULL;
+ char *folder = NULL;
+ char *path = NULL;
+ char *uri = NULL;
+
+ if (!strncmp (url, "file", 4))
+ return g_strndup (url + 7, strlen (url) - 7);
+
+ folder = uri_tester_ensure_data_dir ();
+ filename = g_compute_checksum_for_string (G_CHECKSUM_MD5, url, -1);
+
+ path = g_build_filename (folder, filename, NULL);
+ uri = g_filename_to_uri (path, NULL, NULL);
+
+ g_free (filename);
+ g_free (path);
+ g_free (folder);
+
+ return uri;
+}
+
+static void
+uri_tester_download_notify_status_cb (WebKitDownload *download,
+ GParamSpec *pspec,
+ UriTester *tester)
+{
+ const char *dest = NULL;
+
+ if (webkit_download_get_status (download) != WEBKIT_DOWNLOAD_STATUS_FINISHED)
+ return;
+
+ LOG ("Download from %s to %s completed",
+ webkit_download_get_uri (download),
+ webkit_download_get_destination_uri (download));
+
+ /* Parse the file from disk. */
+ dest = webkit_download_get_destination_uri (download);
+ uri_tester_parse_file_at_uri (tester, dest);
+}
+
+static void
+uri_tester_retrieve_filter (UriTester *tester, const char *url, const char *fileuri)
+{
+ WebKitNetworkRequest *request = NULL;
+ WebKitDownload *download = NULL;
+
+ g_return_if_fail (IS_URI_TESTER (tester));
+ g_return_if_fail (url != NULL);
+ g_return_if_fail (fileuri != NULL);
+
+ request = webkit_network_request_new (url);
+ download = webkit_download_new (request);
+ g_object_unref (request);
+
+ webkit_download_set_destination_uri (download, fileuri);
+
+ g_signal_connect (download, "notify::status",
+ G_CALLBACK (uri_tester_download_notify_status_cb), tester);
+
+ webkit_download_start (download);
+}
+
+static gboolean
+uri_tester_filter_is_valid (const char *fileuri)
+{
+ GFile *file = NULL;
+ GFileInfo *file_info = NULL;
+ gboolean result;
+
+ /* Now check if the local file is too old. */
+ file = g_file_new_for_uri (fileuri);
+ file_info = g_file_query_info (file,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_QUERY_INFO_NONE,
+ NULL,
+ NULL);
+ result = FALSE;
+ if (file_info)
+ {
+ GTimeVal current_time;
+ GTimeVal mod_time;
+
+ g_get_current_time (&current_time);
+ g_file_info_get_modification_time (file_info, &mod_time);
+
+ if (current_time.tv_sec > mod_time.tv_sec)
+ {
+ gint64 expire_time = mod_time.tv_sec + UPDATE_FREQUENCY;
+ result = current_time.tv_sec < expire_time;
+ }
+ g_object_unref (file_info);
+ }
+
+ g_object_unref (file);
+
+ return result;
+}
+
+static void
+uri_tester_load_patterns (UriTester *tester)
+{
+ GSList *filter = NULL;
+ char *url = NULL;
+ char *fileuri = NULL;
+
+ /* Load patterns from the list of filters. */
+ for (filter = tester->priv->filters; filter; filter = g_slist_next(filter))
+ {
+ url = (char*)filter->data;
+ fileuri = uri_tester_get_fileuri_for_url (url);
+
+ if (!uri_tester_filter_is_valid (fileuri))
+ uri_tester_retrieve_filter (tester, url, fileuri);
+ else
+ uri_tester_parse_file_at_uri (tester, fileuri);
+
+ g_free (fileuri);
+ }
+}
+
+static void
+uri_tester_load_filters (UriTester *tester)
+{
+ GSList *list = NULL;
+ char *data_dir = NULL;
+ char *filepath = NULL;
+
+ data_dir = uri_tester_ensure_data_dir ();
+ filepath = g_build_filename (data_dir, FILTERS_LIST_FILENAME, NULL);
+
+ if (g_file_test (filepath, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
+ {
+ GFile *file = NULL;
+ char *contents = NULL;
+ gsize length = 0;
+ GError *error = NULL;
+
+ file = g_file_new_for_path (filepath);
+ if (g_file_load_contents (file, NULL, &contents, &length, NULL, &error))
+ {
+ char **urls_array = NULL;
+ char *url = NULL;
+ int i = 0;
+
+ urls_array = g_strsplit (contents, ";", -1);
+ for (i = 0; urls_array [i]; i++)
+ {
+ url = g_strstrip (g_strdup (urls_array[i]));
+ if (!g_str_equal (url, ""))
+ list = g_slist_prepend (list, url);
+ }
+ g_strfreev (urls_array);
+
+ g_free (contents);
+ }
+
+ if (error)
+ {
+ LOG ("Error loading filters from %s: %s", filepath, error->message);
+ g_error_free (error);
+ }
+
+ g_object_unref (file);
+ }
+ else
+ {
+ /* No file exists yet, so use the default filter and save it. */
+ list = g_slist_prepend (list, g_strdup (DEFAULT_FILTER_URL));
+ }
+
+ g_free (filepath);
+
+ uri_tester_set_filters (tester, g_slist_reverse(list));
+}
+
+static void
+uri_tester_save_filters (UriTester *tester)
+{
+ FILE *file = NULL;
+ char *data_dir = NULL;
+ char *filepath = NULL;
+
+ data_dir = uri_tester_ensure_data_dir ();
+ filepath = g_build_filename (data_dir, FILTERS_LIST_FILENAME, NULL);
+
+ if ((file = g_fopen (filepath, "w")))
+ {
+ GSList *item = NULL;
+ char *filter = NULL;
+
+ for (item = tester->priv->filters; item; item = g_slist_next (item))
+ {
+ filter = g_strdup_printf ("%s;", (char*)item->data);
+ fputs (filter, file);
+ g_free (filter);
+ }
+ fclose (file);
+ }
+ g_free (filepath);
+}
+
+static inline int
+uri_tester_check_rule (UriTester *tester,
+ GRegex *regex,
+ const char *patt,
+ const char *req_uri,
+ const char *page_uri)
+{
+ char *opts;
+
+ if (!g_regex_match_full (regex, req_uri, -1, 0, 0, NULL, NULL))
+ return FALSE;
+
+ opts = g_hash_table_lookup (tester->priv->optslist, patt);
+ if (opts && g_regex_match_simple (",third-party", opts,
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
+ {
+ if (page_uri && g_regex_match_full (regex, page_uri, -1, 0, 0, NULL, NULL))
+ return FALSE;
+ }
+ /* TODO: Domain opt check */
+ LOG ("blocked by pattern regexp=%s -- %s", g_regex_get_pattern (regex), req_uri);
+ return TRUE;
+}
+
+static inline gboolean
+uri_tester_is_matched_by_pattern (UriTester *tester,
+ const char *req_uri,
+ const char *page_uri)
+{
+ GHashTableIter iter;
+ gpointer patt, regex;
+
+ g_hash_table_iter_init (&iter, tester->priv->pattern);
+ while (g_hash_table_iter_next (&iter, &patt, &regex))
+ {
+ if (uri_tester_check_rule(tester, regex, patt, req_uri, page_uri))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static inline gboolean
+uri_tester_is_matched_by_key (UriTester *tester,
+ const char *opts,
+ const char *req_uri,
+ const char *page_uri)
+{
+ UriTesterPrivate *priv = NULL;
+ char *uri;
+ int len;
+ int pos = 0;
+ GList *regex_bl = NULL;
+ GString *guri;
+ gboolean ret = FALSE;
+ char sig[SIGNATURE_SIZE + 1];
+
+ priv = tester->priv;
+
+ memset (&sig[0], 0, sizeof (sig));
+ /* Signatures are made on pattern, so we need to convert url to a pattern as well */
+ guri = uri_tester_fixup_regexp ("", (char*)req_uri);
+ uri = guri->str;
+ len = guri->len;
+
+ for (pos = len - SIGNATURE_SIZE; pos >= 0; pos--)
+ {
+ GRegex *regex;
+ strncpy (sig, uri + pos, SIGNATURE_SIZE);
+ regex = g_hash_table_lookup (priv->keys, sig);
+
+ /* Dont check if regex is already blacklisted */
+ if (!regex || g_list_find (regex_bl, regex))
+ continue;
+ ret = uri_tester_check_rule (tester, regex, sig, req_uri, page_uri);
+ if (ret)
+ break;
+ regex_bl = g_list_prepend (regex_bl, regex);
+ }
+ g_string_free (guri, TRUE);
+ g_list_free (regex_bl);
+ return ret;
+}
+
+static gboolean
+uri_tester_is_matched (UriTester *tester,
+ const char *opts,
+ const char *req_uri,
+ const char *page_uri)
+{
+ UriTesterPrivate *priv = NULL;
+ char *value;
+
+ priv = tester->priv;
+
+ /* Check cached URLs first. */
+ if ((value = g_hash_table_lookup (priv->urlcache, req_uri)))
+ return (value[0] != '0') ? TRUE : FALSE;
+
+ /* Look for a match either by key or by pattern. */
+ if (uri_tester_is_matched_by_key (tester, opts, req_uri, page_uri))
+ {
+ g_hash_table_insert (priv->urlcache, g_strdup (req_uri), g_strdup("1"));
+ return TRUE;
+ }
+
+ /* Matching by pattern is pretty expensive, so do it if needed only. */
+ if (uri_tester_is_matched_by_pattern (tester, req_uri, page_uri))
+ {
+ g_hash_table_insert (priv->urlcache, g_strdup (req_uri), g_strdup("1"));
+ return TRUE;
+ }
+
+ g_hash_table_insert (priv->urlcache, g_strdup (req_uri), g_strdup("0"));
+ return FALSE;
+}
+
+static GString *
+uri_tester_fixup_regexp (const char *prefix, char *src)
+{
+ GString *str;
+ int len = 0;
+
+ if (!src)
+ return NULL;
+
+ str = g_string_new (prefix);
+
+ /* lets strip first .* */
+ if (src[0] == '*')
+ {
+ (void)*src++;
+ }
+
+ do
+ {
+ switch (*src)
+ {
+ case '*':
+ g_string_append (str, ".*");
+ break;
+ /*case '.':
+ g_string_append (str, "\\.");
+ break;*/
+ case '?':
+ g_string_append (str, "\\?");
+ break;
+ case '|':
+ /* FIXME: We actually need to match :[0-9]+ or '/'. Sign means
+ "here could be port number or nothing". So bla.com^ will match
+ bla.com/ or bla.com:8080/ but not bla.com.au/ */
+ case '^':
+ case '+':
+ break;
+ default:
+ g_string_append_printf (str,"%c", *src);
+ break;
+ }
+ src++;
+ }
+ while (*src);
+
+ len = str->len;
+ /* We dont need .* in the end of url. Thats stupid */
+ if (str->str && str->str[len-1] == '*' && str->str[len-2] == '.')
+ g_string_erase (str, len-2, 2);
+
+ return str;
+}
+
+static gboolean
+uri_tester_compile_regexp (UriTester *tester,
+ GString *gpatt,
+ char *opts)
+{
+ GRegex *regex;
+ GError *error = NULL;
+ char *patt;
+ int len;
+
+ if (!gpatt)
+ return FALSE;
+
+ patt = gpatt->str;
+ len = gpatt->len;
+
+ /* TODO: Play with optimization flags */
+ regex = g_regex_new (patt, G_REGEX_OPTIMIZE,
+ G_REGEX_MATCH_NOTEMPTY, &error);
+ if (error)
+ {
+ g_warning ("%s: %s", G_STRFUNC, error->message);
+ g_error_free (error);
+ g_regex_unref (regex);
+ return TRUE;
+ }
+
+ if (!g_regex_match_simple ("^/.*[\\^\\$\\*].*/$", patt, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY))
+ {
+ int signature_count = 0;
+ int pos = 0;
+ char *sig;
+
+ for (pos = len - SIGNATURE_SIZE; pos >= 0; pos--) {
+ sig = g_strndup (patt + pos, SIGNATURE_SIZE);
+ if (!g_regex_match_simple ("[\\*]", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) &&
+ !g_hash_table_lookup (tester->priv->keys, sig))
+ {
+ LOG ("sig: %s %s", sig, patt);
+ g_hash_table_insert (tester->priv->keys, g_strdup (sig), g_regex_ref (regex));
+ g_hash_table_insert (tester->priv->optslist, g_strdup (sig), g_strdup (opts));
+ signature_count++;
+ }
+ else
+ {
+ if (g_regex_match_simple ("^\\*", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) &&
+ !g_hash_table_lookup (tester->priv->pattern, patt))
+ {
+ LOG ("patt2: %s %s", sig, patt);
+ g_hash_table_insert (tester->priv->pattern, g_strdup (patt), g_regex_ref (regex));
+ g_hash_table_insert (tester->priv->optslist, g_strdup (patt), g_strdup (opts));
+ }
+ }
+ g_free (sig);
+ }
+ g_regex_unref (regex);
+
+ if (signature_count > 1 && g_hash_table_lookup (tester->priv->pattern, patt))
+ {
+ g_hash_table_steal (tester->priv->pattern, patt);
+ return TRUE;
+ }
+
+ return FALSE;
+ }
+ else
+ {
+ LOG ("patt: %s%s", patt, "");
+ /* Pattern is a regexp chars */
+ g_hash_table_insert (tester->priv->pattern, g_strdup (patt), regex);
+ g_hash_table_insert (tester->priv->optslist, g_strdup (patt), g_strdup (opts));
+ return FALSE;
+ }
+}
+
+static char*
+uri_tester_add_url_pattern (UriTester *tester,
+ char *prefix,
+ char *type,
+ char *line)
+{
+ char **data;
+ char *patt;
+ GString *format_patt;
+ char *opts;
+ gboolean should_free;
+
+ data = g_strsplit (line, "$", -1);
+ if (!data || !data[0])
+ {
+ g_strfreev (data);
+ return NULL;
+ }
+
+ if (data[1] && data[2])
+ {
+ patt = g_strconcat (data[0], data[1], NULL);
+ opts = g_strconcat (type, ",", data[2], NULL);
+ }
+ else if (data[1])
+ {
+ patt = data[0];
+ opts = g_strconcat (type, ",", data[1], NULL);
+ }
+ else
+ {
+ patt = data[0];
+ opts = type;
+ }
+
+ if (g_regex_match_simple ("subdocument", opts,
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
+ {
+ if (data[1] && data[2])
+ g_free (patt);
+ if (data[1])
+ g_free (opts);
+ g_strfreev (data);
+ return NULL;
+ }
+
+ format_patt = uri_tester_fixup_regexp (prefix, patt);
+
+ LOG ("got: %s opts %s", format_patt->str, opts);
+ should_free = uri_tester_compile_regexp (tester, format_patt, opts);
+
+ if (data[1] && data[2])
+ g_free (patt);
+ if (data[1])
+ g_free (opts);
+ g_strfreev (data);
+
+ return g_string_free (format_patt, should_free);
+}
+
+static inline void
+uri_tester_frame_add (UriTester *tester, char *line)
+{
+ const char *separator = " , ";
+
+ (void)*line++;
+ (void)*line++;
+ if (strchr (line, '\'')
+ || (strchr (line, ':')
+ && !g_regex_match_simple (".*\\[.*:.*\\].*", line,
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY)))
+ {
+ return;
+ }
+ g_string_append (tester->priv->blockcss, separator);
+ g_string_append (tester->priv->blockcss, line);
+}
+
+static inline void
+uri_tester_frame_add_private (UriTester *tester,
+ const char *line,
+ const char *sep)
+{
+ char **data;
+ data = g_strsplit (line, sep, 2);
+
+ if (!(data[1] && *data[1])
+ || strchr (data[1], '\'')
+ || (strchr (data[1], ':')
+ && !g_regex_match_simple (".*\\[.*:.*\\].*", data[1],
+ G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY)))
+ {
+ g_strfreev (data);
+ return;
+ }
+
+ if (strchr (data[0], ','))
+ {
+ char **domains;
+ int i;
+
+ domains = g_strsplit (data[0], ",", -1);
+ for (i = 0; domains[i]; i++)
+ {
+ g_string_append_printf (tester->priv->blockcssprivate, ";sites['%s']+=',%s'",
+ g_strstrip (domains[i]), data[1]);
+ }
+ g_strfreev (domains);
+ }
+ else
+ {
+ g_string_append_printf (tester->priv->blockcssprivate, ";sites['%s']+=',%s'",
+ data[0], data[1]);
+ }
+ g_strfreev (data);
+}
+
+static char*
+uri_tester_parse_line (UriTester *tester, char *line)
+{
+ if (!line)
+ return NULL;
+ g_strchomp (line);
+ /* Ignore comments and new lines */
+ if (line[0] == '!')
+ return NULL;
+ /* FIXME: No support for whitelisting */
+ if (line[0] == '@' && line[1] == '@')
+ return NULL;
+ /* FIXME: No support for [include] and [exclude] tags */
+ if (line[0] == '[')
+ return NULL;
+
+ /* Skip garbage */
+ if (line[0] == ' ' || !line[0])
+ return NULL;
+
+ /* Got CSS block hider */
+ if (line[0] == '#' && line[1] == '#' )
+ {
+ uri_tester_frame_add (tester, line);
+ return NULL;
+ }
+ /* Got CSS block hider. Workaround */
+ if (line[0] == '#')
+ return NULL;
+
+ /* Got per domain CSS hider rule */
+ if (strstr (line, "##"))
+ {
+ uri_tester_frame_add_private (tester, line, "##");
+ return NULL;
+ }
+
+ /* Got per domain CSS hider rule. Workaround */
+ if (strchr (line, '#'))
+ {
+ uri_tester_frame_add_private (tester, line, "#");
+ return NULL;
+ }
+ /* Got URL blocker rule */
+ if (line[0] == '|' && line[1] == '|' )
+ {
+ (void)*line++;
+ (void)*line++;
+ return uri_tester_add_url_pattern (tester, "", "fulluri", line);
+ }
+ if (line[0] == '|')
+ {
+ (void)*line++;
+ return uri_tester_add_url_pattern (tester, "^", "fulluri", line);
+ }
+ return uri_tester_add_url_pattern (tester, "", "uri", line);
+}
+
+static gboolean
+uri_tester_parse_file_at_uri (UriTester *tester, const char *fileuri)
+{
+ FILE *file;
+ char line[2000];
+ char *path = NULL;
+ gboolean result = FALSE;
+
+ path = g_filename_from_uri (fileuri, NULL, NULL);
+ if ((file = g_fopen (path, "r")))
+ {
+ while (fgets (line, 2000, file))
+ g_free (uri_tester_parse_line (tester, line));
+ fclose (file);
+
+ result = TRUE;
+ }
+ g_free (path);
+
+ return result;
+}
+
+static void
+uri_tester_init (UriTester *tester)
+{
+ UriTesterPrivate *priv = NULL;
+
+ LOG ("UriTester initializing %p", tester);
+
+ priv = URI_TESTER_GET_PRIVATE (tester);
+ tester->priv = priv;
+
+ priv->filters = NULL;
+ priv->pattern = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_regex_unref);
+ priv->keys = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_regex_unref);
+ priv->optslist = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL,
+ (GDestroyNotify)g_free);
+ priv->urlcache = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify)g_free,
+ (GDestroyNotify)g_free);
+
+ priv->blockcss = g_string_new ("z-non-exist");
+ priv->blockcssprivate = g_string_new ("");
+
+ uri_tester_load_filters (tester);
+ uri_tester_load_patterns (tester);
+}
+
+static void
+uri_tester_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ UriTester *tester = URI_TESTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_FILTERS:
+ uri_tester_set_filters (tester, (GSList*) g_value_get_pointer (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+uri_tester_finalize (GObject *object)
+{
+ UriTesterPrivate *priv = URI_TESTER_GET_PRIVATE (URI_TESTER (object));
+
+ LOG ("UriTester finalizing %p", object);
+
+ g_slist_foreach (priv->filters, (GFunc) g_free, NULL);
+ g_slist_free (priv->filters);
+
+ g_hash_table_destroy (priv->pattern);
+ g_hash_table_destroy (priv->keys);
+ g_hash_table_destroy (priv->optslist);
+ g_hash_table_destroy (priv->urlcache);
+
+ g_string_free (priv->blockcss, TRUE);
+ g_string_free (priv->blockcssprivate, TRUE);
+
+ G_OBJECT_CLASS (uri_tester_parent_class)->finalize (object);
+}
+
+static void
+uri_tester_class_init (UriTesterClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->set_property = uri_tester_set_property;
+ object_class->finalize = uri_tester_finalize;
+
+ g_object_class_install_property
+ (object_class,
+ PROP_FILTERS,
+ g_param_spec_pointer ("filters",
+ "filters",
+ "filters",
+ G_PARAM_WRITABLE));
+
+ g_type_class_add_private (object_class, sizeof (UriTesterPrivate));
+}
+
+UriTester *
+uri_tester_new (void)
+{
+ return g_object_new (TYPE_URI_TESTER, NULL);
+}
+
+gboolean
+uri_tester_test_uri (UriTester *tester,
+ const char *req_uri,
+ const char *page_uri,
+ AdUriCheckType type)
+{
+ /* Don't block top level documents. */
+ if (type == AD_URI_CHECK_TYPE_DOCUMENT)
+ return FALSE;
+
+ return uri_tester_is_matched (tester, NULL, req_uri, page_uri);
+}
+
+void
+uri_tester_set_filters (UriTester *tester, GSList *filters)
+{
+ UriTesterPrivate *priv = tester->priv;
+
+ if (priv->filters)
+ {
+ g_slist_foreach (priv->filters, (GFunc) g_free, NULL);
+ g_slist_free (priv->filters);
+ }
+
+ /* Update private variable and save to disk. */
+ priv->filters = filters;
+ uri_tester_save_filters (tester);
+}
+
+GSList *
+uri_tester_get_filters (UriTester *tester)
+{
+ return tester->priv->filters;
+}
+
+void
+uri_tester_reload (UriTester *tester)
+{
+ GDir *g_data_dir = NULL;
+ const char *data_dir = NULL;
+
+ /* Remove data files in the data dir first. */
+ data_dir = uri_tester_ensure_data_dir ();
+
+ g_data_dir = g_dir_open (data_dir, 0, NULL);
+ if (g_data_dir)
+ {
+ const char *filename = NULL;
+ char *filepath = NULL;
+
+ while ((filename = g_dir_read_name (g_data_dir)))
+ {
+ /* Omit the list of filters. */
+ if (!g_strcmp0 (filename, FILTERS_LIST_FILENAME))
+ continue;
+
+ filepath = g_build_filename (data_dir, filename, NULL);
+ g_unlink (filepath);
+
+ g_free (filepath);
+ }
+
+ g_dir_close (g_data_dir);
+ }
+
+ /* Load patterns from current filters. */
+ uri_tester_load_patterns (tester);
+}
diff --git a/src/uri-tester.h b/src/uri-tester.h
new file mode 100644
index 000000000..d8fb85aec
--- /dev/null
+++ b/src/uri-tester.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright © 2011 Igalia S.L.
+ *
+ * This program 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 2, or (at your option)
+ * any later version.
+ *
+ * This program 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 this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef URI_TESTER_H
+#define URI_TESTER_H
+
+#include "ephy-adblock.h"
+
+#include <glib-object.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_URI_TESTER (uri_tester_get_type ())
+#define URI_TESTER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), TYPE_URI_TESTER, UriTester))
+#define URI_TESTER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), TYPE_URI_TESTER, UriTesterClass))
+#define IS_URI_TESTER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), TYPE_URI_TESTER))
+#define IS_URI_TESTER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), TYPE_URI_TESTER))
+#define URI_TESTER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), TYPE_URI_TESTER, UriTesterClass))
+
+typedef struct _UriTester UriTester;
+typedef struct _UriTesterClass UriTesterClass;
+typedef struct _UriTesterPrivate UriTesterPrivate;
+
+struct _UriTester
+{
+ GObject parent_instance;
+
+ /*< private >*/
+ UriTesterPrivate *priv;
+};
+
+struct _UriTesterClass
+{
+ GObjectClass parent_class;
+};
+
+GType uri_tester_get_type (void);
+
+void uri_tester_register (GTypeModule *module);
+
+UriTester *uri_tester_new (void);
+
+gboolean uri_tester_test_uri (UriTester *tester,
+ const char *req_uri,
+ const char *page_uri,
+ AdUriCheckType type);
+
+void uri_tester_set_filters (UriTester *tester, GSList *filters);
+
+GSList *uri_tester_get_filters (UriTester *tester);
+
+void uri_tester_reload (UriTester *tester);
+
+G_END_DECLS
+
+#endif /* URI_TESTER_H */