Merge lp:~midori/midori/wk2ab into lp:midori

Proposed by Cris Dywan
Status: Merged
Approved by: Paweł Forysiuk
Approved revision: 6325
Merged at revision: 6558
Proposed branch: lp:~midori/midori/wk2ab
Merge into: lp:midori
Diff against target: 3520 lines (+1443/-1934)
18 files modified
data/adblock.list (+54/-0)
extensions/CMakeLists.txt (+18/-2)
extensions/adblock.c (+0/-1930)
extensions/adblock/config.vala (+107/-0)
extensions/adblock/element.vala (+36/-0)
extensions/adblock/extension.vala (+545/-0)
extensions/adblock/filter.vala (+52/-0)
extensions/adblock/keys.vala (+47/-0)
extensions/adblock/options.vala (+32/-0)
extensions/adblock/pattern.vala (+26/-0)
extensions/adblock/subscriptions.vala (+328/-0)
extensions/adblock/updater.vala (+139/-0)
extensions/adblock/whitelist.vala (+30/-0)
katze/midori-paths.vala (+1/-0)
midori/midori-app.c (+1/-1)
midori/midori.vapi (+1/-0)
midori/webkit2gtk-3.0.vapi (+16/-0)
po/POTFILES.in (+10/-1)
To merge this branch: bzr merge lp:~midori/midori/wk2ab
Reviewer Review Type Date Requested Status
Paweł Forysiuk Approve
Review via email: mp+178349@code.launchpad.net

Commit message

Rewrite Adblock more modularly, add Whitelist support

Description of the change

TODO:
- Expose filter metadata to user (name, last updated, time to next update)
- Check checksum hash of the subscription after downloading
- Remove subscriptions
- Open local files in editor
- Ability to disable adblock for site/for all sites via icon
- Save pre-parsed pre filtered ruleset into file as .preparsed
- Save matched/ cached uris from previous runs

[https://easylist-downloads.adblockplus.org/easylist.txt]
enabled=1
expires=48206903549424
retries=1
homepage=https://easylist-downloads.adblockplus.org
title=EasyList

Test cases https://hg.adblockplus.org/adblockplustests/file/3e166c282a1d/chrome/content/tests/elemhide.js

To post a comment you must log in.
lp:~midori/midori/wk2ab updated
6325. By Cris Dywan

Rename adblock2 to adblock

Revision history for this message
Paweł Forysiuk (tuxator) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'data/adblock.list'
2--- data/adblock.list 1970-01-01 00:00:00 +0000
3+++ data/adblock.list 2014-02-20 20:31:17 +0000
4@@ -0,0 +1,54 @@
5+[Adblock Plus 2.0]
6+! Version: 201402142200
7+! Title: Exercise
8+! Last modified: 11 Feb 2014 22:00 UTC
9+! Expires: 3 days (update frequency)
10+! Homepage: http://www.midori-browser.org
11+! Licence: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
12+! Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
13+!
14+! Yadayada http://example.com/ e-mail (somebody@example.com).
15+!
16+!-----Spam eggs--------!
17+! *** la:le/lu_foo_bar.txt ***
18+|/http://ads.blub.boing/*$domain=xxx.com
19+|/http://ads.blub.boing/*$domain=xxx.com,foo.fr,coco.at
20+/market.php?$domain=adf.ly|foo.com
21+/?placement=$script,domain=putlocker.com|sockshare.com
22+
23+! Some basic filters
24+*ads.foo.bar*
25+*ads.bogus.name*
26+||^http://ads.bla.blub/*
27+engine.adct.ru/*?
28+/addyn|*|adtech;
29+doubleclick.net/pfadx/*.mtvi
30+objects.tremormedia.com/embed/xml/*.xml?r=
31+videostrip.com^*/admatcherclient.
32+test.dom/test?var
33+/adpage.
34+br.gcl.ru/cgi-bin/br/
35+_300x600.
36+_rectangle_ads.
37++adverts/
38+-2/ads/
39+
40+! CSS elements
41+old.tv,delicio.us,longc.at###box
42+##.zRightAdNote
43+###advertisingModule160x600
44+##a[href$="/vghd.shtml"]
45+imagetwist.com###left[align="center"] > center > a[target="_blank"]
46+
47+! Options
48+||videobox.com/?tid=$popup
49+||sexsearchcom.com^$popup,third-party
50+||206.217.206.137^$third-party
51+/spopunder^$popup
52+||putlocker.com^*.php?*title$subdocument
53+://ads.$popup
54+
55+! Whitelist
56+@@||hortifor.com/images/*120x60$~third-party
57+@@||stickam.com/wb/www/category/300x250/$image
58+@@||adultadworld.com/adhandler/$subdocument
59
60=== modified file 'extensions/CMakeLists.txt'
61--- extensions/CMakeLists.txt 2013-09-08 19:56:46 +0000
62+++ extensions/CMakeLists.txt 2014-02-20 20:31:17 +0000
63@@ -15,7 +15,6 @@
64 file(GLOB EXTENSIONS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *)
65 if (HALF_BRO_INCOM_WEBKIT2)
66 list(REMOVE_ITEM EXTENSIONS
67- "adblock.c"
68 "cookie-permissions"
69 "addons.c"
70 "formhistory"
71@@ -46,13 +45,30 @@
72 string(FIND ${UNIT_SRC} "." UNIT_EXTENSION)
73 if (UNIT_EXTENSION EQUAL -1)
74 file(GLOB UNIT_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${UNIT_SRC}/*.c")
75+ file(GLOB UNIT_FILES_VALA RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${UNIT_SRC}/*.vala")
76+ if (UNIT_FILES_VALA)
77+ include(ValaPrecompile)
78+ vala_precompile(UNIT_SRC_C ${UNIT_SRC}
79+ ${UNIT_FILES_VALA}
80+ PACKAGES
81+ ${PKGS}
82+ OPTIONS
83+ ${VALAFLAGS}
84+ CUSTOM_VAPIS
85+ ${EXTRA_VAPIS}
86+ "${CMAKE_SOURCE_DIR}/midori/midori.vapi"
87+ "${CMAKE_SOURCE_DIR}/katze/katze.vapi"
88+ "${CMAKE_BINARY_DIR}/midori/${LIBMIDORI}.vapi"
89+ )
90+ set(UNIT_FILES ${UNIT_FILES} ${UNIT_SRC_C})
91+ endif ()
92 if (UNIT_FILES)
93 add_library(${UNIT_SRC} MODULE ${UNIT_FILES})
94 target_link_libraries(${UNIT_SRC}
95 ${LIBMIDORI}
96 )
97 set_target_properties(${UNIT_SRC} PROPERTIES
98- COMPILE_FLAGS ${CFLAGS}
99+ COMPILE_FLAGS ${VALA_CFLAGS}
100 )
101 install(TARGETS ${UNIT_SRC}
102 LIBRARY DESTINATION ${EXTENSIONDIR}
103
104=== added directory 'extensions/adblock'
105=== removed file 'extensions/adblock.c'
106--- extensions/adblock.c 2013-11-09 17:56:58 +0000
107+++ extensions/adblock.c 1970-01-01 00:00:00 +0000
108@@ -1,1930 +0,0 @@
109-/*
110- Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
111- Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
112-
113- This library is free software; you can redistribute it and/or
114- modify it under the terms of the GNU Lesser General Public
115- License as published by the Free Software Foundation; either
116- version 2.1 of the License, or (at your option) any later version.
117-
118- See the file COPYING for the full license text.
119-*/
120-#include <midori/midori.h>
121-#include "midori-core.h"
122-#include <glib/gstdio.h>
123-
124-#include "config.h"
125-#if HAVE_UNISTD_H
126- #include <unistd.h>
127-#endif
128-
129-#define SIGNATURE_SIZE 8
130-#define USE_PATTERN_MATCHING 1
131-#define CUSTOM_LIST_NAME "custom.list"
132-#define ADBLOCK_FILTER_VALID(__filter) \
133- (__filter && (g_str_has_prefix (__filter, "http") \
134- || g_str_has_prefix (__filter, "file")))
135-#define ADBLOCK_FILTER_SET(__filter,__active) \
136- __filter[4] = __active ? (__filter[5] == ':' ? 's' : ':') : '-'
137-#define ADBLOCK_FILTER_IS_SET(__filter) \
138- (__filter[4] != '-' && __filter[5] != '-')
139- #define adblock_debug(dmsg, darg1, darg2) \
140- do { if (midori_debug ("adblock:match")) g_debug (dmsg, darg1, darg2); } while (0)
141-
142-static GHashTable* pattern = NULL;
143-static GHashTable* keys = NULL;
144-static GHashTable* optslist = NULL;
145-static GHashTable* urlcache = NULL;
146-static GHashTable* blockcssprivate = NULL;
147-static GHashTable* navigationwhitelist = NULL;
148-static GString* blockcss = NULL;
149-static GList* update_list = NULL;
150-static gboolean update_done = FALSE;
151-
152-static void
153-adblock_parse_file (gchar* path);
154-
155-static gboolean
156-adblock_file_is_up_to_date (gchar* path);
157-
158-static void
159-adblock_reload_rules (MidoriExtension* extension,
160- gboolean custom_only);
161-
162-static gchar*
163-adblock_build_js (const gchar* uri)
164-{
165- gchar* domain;
166- const gchar* style;
167- GString* subdomain;
168- GString* code;
169- int cnt = 0, blockscnt = 0;
170- gchar** subdomains;
171-
172- domain = midori_uri_parse_hostname (uri, NULL);
173- subdomains = g_strsplit (domain, ".", -1);
174- g_free (domain);
175- if (!subdomains)
176- return NULL;
177-
178- code = g_string_new (
179- "window.addEventListener ('DOMContentLoaded',"
180- "function () {"
181- " if (document.getElementById('madblock'))"
182- " return;"
183- " public = '");
184-
185- cnt = g_strv_length (subdomains) - 1;
186- subdomain = g_string_new (subdomains [cnt]);
187- g_string_prepend_c (subdomain, '.');
188- cnt--;
189- while (cnt >= 0)
190- {
191- g_string_prepend (subdomain, subdomains[cnt]);
192- if ((style = g_hash_table_lookup (blockcssprivate, subdomain->str)))
193- {
194- g_string_append (code, style);
195- g_string_append_c (code, ',');
196- blockscnt++;
197- }
198- g_string_prepend_c (subdomain, '.');
199- cnt--;
200- }
201- g_string_free (subdomain, TRUE);
202- g_strfreev (subdomains);
203-
204- if (blockscnt == 0)
205- return g_string_free (code, TRUE);
206-
207- g_string_append (code,
208- " zz-non-existent {display: none !important}';"
209- " var mystyle = document.createElement('style');"
210- " mystyle.setAttribute('type', 'text/css');"
211- " mystyle.setAttribute('id', 'madblock');"
212- " mystyle.appendChild(document.createTextNode(public));"
213- " var head = document.getElementsByTagName('head')[0];"
214- " if (head) head.appendChild(mystyle);"
215- "}, true);");
216- return g_string_free (code, FALSE);
217-}
218-
219-static GString*
220-adblock_fixup_regexp (const gchar* prefix,
221- gchar* src);
222-
223-static void
224-adblock_destroy_db ()
225-{
226- g_string_free (blockcss, TRUE);
227- blockcss = NULL;
228-
229- g_hash_table_destroy (pattern);
230- pattern = NULL;
231- g_hash_table_destroy (optslist);
232- optslist = NULL;
233- g_hash_table_destroy (urlcache);
234- urlcache = NULL;
235- g_hash_table_destroy (blockcssprivate);
236- blockcssprivate = NULL;
237- g_hash_table_destroy (navigationwhitelist);
238- navigationwhitelist = NULL;
239-}
240-
241-static void
242-adblock_init_db ()
243-{
244- pattern = g_hash_table_new_full (g_str_hash, g_str_equal,
245- (GDestroyNotify)g_free,
246- (GDestroyNotify)g_regex_unref);
247- keys = g_hash_table_new_full (g_str_hash, g_str_equal,
248- (GDestroyNotify)g_free,
249- (GDestroyNotify)g_regex_unref);
250- optslist = g_hash_table_new_full (g_str_hash, g_str_equal,
251- NULL,
252- (GDestroyNotify)g_free);
253- urlcache = g_hash_table_new_full (g_str_hash, g_str_equal,
254- (GDestroyNotify)g_free,
255- (GDestroyNotify)g_free);
256- blockcssprivate = g_hash_table_new_full (g_str_hash, g_str_equal,
257- (GDestroyNotify)g_free,
258- (GDestroyNotify)g_free);
259- navigationwhitelist = g_hash_table_new_full (g_direct_hash, g_str_equal,
260- NULL,
261- (GDestroyNotify)g_free);
262-
263- blockcss = g_string_new ("z-non-exist");
264-}
265-
266-static void
267-adblock_download_notify_status_cb (WebKitDownload* download,
268- GParamSpec* pspec,
269- MidoriExtension* extension)
270-{
271- if (update_done)
272- return;
273-
274- if (webkit_download_get_status (download) == WEBKIT_DOWNLOAD_STATUS_FINISHED)
275- {
276- GList* li = NULL;
277- for (li = update_list; li != NULL; li = g_list_next (li))
278- {
279- gchar* uri = g_strdup (webkit_download_get_destination_uri (download) + 7);
280- if (g_strcmp0 (li->data, uri))
281- update_list = g_list_remove (update_list, li->data);
282- g_free (uri);
283- }
284- }
285-
286- if (g_list_length (update_list) == 0)
287- {
288- adblock_reload_rules (extension, FALSE);
289- update_done = TRUE;
290- }
291-}
292-
293-static gchar*
294-adblock_get_filename_for_uri (const gchar* uri)
295-{
296- gchar* filename;
297- gchar* folder;
298- gchar* path;
299-
300- if (!ADBLOCK_FILTER_IS_SET (uri))
301- return NULL;
302-
303- if (!strncmp (uri, "file", 4))
304- return g_strndup (uri + 7, strlen (uri) - 7);
305-
306- folder = g_build_filename (midori_paths_get_cache_dir (), "adblock", NULL);
307- katze_mkdir_with_parents (folder, 0700);
308-
309- filename = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
310- path = g_build_filename (folder, filename, NULL);
311-
312- g_free (filename);
313- g_free (folder);
314- return path;
315-}
316-
317-static void
318-adblock_reload_rules (MidoriExtension* extension,
319- gboolean custom_only)
320-{
321- gchar* path;
322- gchar* custom_list;
323- gchar** filters;
324- guint i = 0;
325- MidoriApp* app = midori_extension_get_app (extension);
326- MidoriWebSettings* settings = katze_object_get_object (app, "settings");
327-
328- if (pattern)
329- adblock_destroy_db ();
330- adblock_init_db ();
331-
332- custom_list = g_build_filename (midori_extension_get_config_dir (extension),
333- CUSTOM_LIST_NAME, NULL);
334- adblock_parse_file (custom_list);
335- g_free (custom_list);
336-
337- filters = midori_extension_get_string_list (extension, "filters", NULL);
338- if (!custom_only && filters && *filters)
339- {
340- while (filters[i] != NULL)
341- {
342- path = adblock_get_filename_for_uri (filters[i]);
343- if (!path)
344- {
345- i++;
346- continue;
347- }
348-
349- if (!adblock_file_is_up_to_date (path))
350- {
351- WebKitNetworkRequest* request;
352- WebKitDownload* download;
353- gchar* destination = g_filename_to_uri (path, NULL, NULL);
354-
355- request = webkit_network_request_new (filters[i]);
356- download = webkit_download_new (request);
357- g_object_unref (request);
358- webkit_download_set_destination_uri (download, destination);
359- update_list = g_list_prepend (update_list, path);
360- g_free (destination);
361- g_signal_connect (download, "notify::status",
362- G_CALLBACK (adblock_download_notify_status_cb), extension);
363- webkit_download_start (download);
364- }
365- else
366- adblock_parse_file (path);
367- g_free (path);
368- i++;
369- }
370- }
371- g_strfreev (filters);
372- g_string_append (blockcss, " {display: none !important}\n");
373-
374- midori_web_settings_add_style (settings, "adblock-blockcss", blockcss->str);
375- g_object_unref (settings);
376-}
377-
378-static void
379-adblock_preferences_render_tick_cb (GtkTreeViewColumn* column,
380- GtkCellRenderer* renderer,
381- GtkTreeModel* model,
382- GtkTreeIter* iter,
383- MidoriExtension* extension)
384-{
385- gchar* filter;
386-
387- gtk_tree_model_get (model, iter, 0, &filter, -1);
388-
389- g_object_set (renderer,
390- "activatable", ADBLOCK_FILTER_VALID (filter),
391- "active", ADBLOCK_FILTER_VALID (filter) && ADBLOCK_FILTER_IS_SET (filter),
392- NULL);
393-
394- g_free (filter);
395-}
396-
397-static void
398-adblock_preferences_renderer_text_edited_cb (GtkCellRenderer* renderer,
399- const gchar* tree_path,
400- const gchar* new_text,
401- GtkTreeModel* model)
402-{
403- GtkTreeIter iter;
404-
405- if (gtk_tree_model_get_iter_from_string (model, &iter, tree_path))
406- gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, new_text, -1);
407-}
408-
409-static void
410-adblock_preferences_renderer_toggle_toggled_cb (GtkCellRendererToggle* renderer,
411- const gchar* path,
412- GtkTreeModel* model)
413-{
414- GtkTreeIter iter;
415-
416- if (gtk_tree_model_get_iter_from_string (model, &iter, path))
417- {
418- gchar* filter;
419-
420- gtk_tree_model_get (model, &iter, 0, &filter, -1);
421-
422- if (ADBLOCK_FILTER_VALID (filter))
423- {
424- ADBLOCK_FILTER_SET (filter, TRUE);
425- if (gtk_cell_renderer_toggle_get_active (renderer))
426- {
427- if (midori_uri_is_http (filter))
428- {
429- gchar* filename = adblock_get_filename_for_uri (filter);
430- g_unlink (filename);
431- g_free (filename);
432- }
433- ADBLOCK_FILTER_SET (filter, FALSE);
434- }
435-
436- gtk_list_store_set (GTK_LIST_STORE (model), &iter, 0, filter, -1);
437-
438- g_free (filter);
439- }
440- }
441-}
442-
443-static void
444-adblock_preferences_render_text_cb (GtkTreeViewColumn* column,
445- GtkCellRenderer* renderer,
446- GtkTreeModel* model,
447- GtkTreeIter* iter,
448- MidoriExtension* extension)
449-{
450- gchar* filter;
451-
452- gtk_tree_model_get (model, iter, 0, &filter, -1);
453-
454- if (ADBLOCK_FILTER_VALID (filter))
455- ADBLOCK_FILTER_SET (filter, TRUE);
456-
457- g_object_set (renderer,
458- "text", filter,
459- NULL);
460-
461- g_free (filter);
462-}
463-
464-static void
465-adblock_preferences_model_row_changed_cb (GtkTreeModel* model,
466- GtkTreePath* path,
467- GtkTreeIter* iter,
468- MidoriExtension* extension)
469-{
470- gsize length = gtk_tree_model_iter_n_children (model, NULL);
471- gchar** filters = g_new (gchar*, length + 1);
472- guint i = 0;
473- gboolean need_reload = FALSE;
474-
475- if (gtk_tree_model_iter_children (model, iter, NULL))
476- do
477- {
478- gchar* filter;
479- gtk_tree_model_get (model, iter, 0, &filter, -1);
480- if (filter && *filter)
481- {
482- filters[i++] = filter;
483- need_reload = TRUE;
484- }
485- else
486- g_free (filter);
487- }
488- while (gtk_tree_model_iter_next (model, iter));
489- filters[i] = NULL;
490- midori_extension_set_string_list (extension, "filters", filters, i);
491- g_free (filters);
492- if (need_reload)
493- adblock_reload_rules (extension, FALSE);
494-}
495-
496-static void
497-adblock_preferences_model_row_deleted_cb (GtkTreeModel* model,
498- GtkTreePath* path,
499- MidoriExtension* extension)
500-{
501- GtkTreeIter iter;
502- adblock_preferences_model_row_changed_cb (model, path, &iter, extension);
503-}
504-
505-static void
506-adblock_preferences_add_clicked_cb (GtkWidget* button,
507- GtkTreeModel* model)
508-{
509- GtkEntry* entry = GTK_IS_ENTRY (button)
510- ? button : g_object_get_data (G_OBJECT (button), "entry");
511- gtk_list_store_insert_with_values (GTK_LIST_STORE (model),
512- NULL, 0, 0, gtk_entry_get_text (entry), -1);
513- gtk_entry_set_text (entry, "");
514-}
515-
516-static void
517-adblock_preferences_edit_clicked_cb (GtkWidget* button,
518- GtkTreeViewColumn* column)
519-{
520- GdkEvent* event = gtk_get_current_event ();
521- GtkTreeView* treeview = g_object_get_data (G_OBJECT (button), "treeview");
522- GtkTreeModel* model;
523- GtkTreeIter iter;
524- if (katze_tree_view_get_selected_iter (treeview, &model, &iter))
525- {
526- gchar* path = gtk_tree_model_get_string_from_iter (model, &iter);
527- GtkTreePath* tree_path = gtk_tree_path_new_from_string (path);
528- /* gtk_cell_renderer_start_editing */
529- gtk_tree_view_set_cursor (treeview, tree_path, column, TRUE);
530- gtk_tree_path_free (tree_path);
531- g_free (path);
532- }
533- gdk_event_free (event);
534-}
535-
536-static void
537-adblock_preferences_remove_clicked_cb (GtkWidget* button,
538- GtkTreeView* treeview)
539-{
540- GtkTreeModel* model;
541- GtkTreeIter iter;
542- if (katze_tree_view_get_selected_iter (treeview, &model, &iter))
543- gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
544-}
545-
546-static gboolean
547-adblock_activate_link_cb (GtkWidget* label,
548- const gchar* uri)
549-{
550- MidoriBrowser* browser = midori_browser_get_for_widget (label);
551- GtkWidget* view = midori_browser_add_uri (browser, uri);
552- midori_browser_set_current_tab (browser, view);
553- return TRUE;
554-}
555-
556-static GtkWidget*
557-adblock_get_preferences_dialog (MidoriExtension* extension)
558-{
559- MidoriApp* app;
560- GtkWidget* browser;
561- const gchar* dialog_title;
562- GtkWidget* dialog;
563- GtkWidget* content_area;
564- gint width, height;
565- GtkWidget* xfce_heading;
566- GtkWidget* hbox;
567- GtkListStore* liststore;
568- GtkWidget* treeview;
569- GtkTreeViewColumn* column;
570- GtkCellRenderer* renderer_text;
571- GtkCellRenderer* renderer_toggle;
572- GtkWidget* scrolled;
573- gchar** filters;
574- GtkWidget* vbox;
575- GtkWidget* button;
576- gchar* description;
577- GtkWidget* entry;
578- #if HAVE_OSX
579- GtkWidget* icon;
580- #endif
581-
582- app = midori_extension_get_app (extension);
583- browser = katze_object_get_object (app, "browser");
584-
585- dialog_title = _("Configure Advertisement filters");
586- dialog = gtk_dialog_new_with_buttons (dialog_title, GTK_WINDOW (browser),
587- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
588- #if !HAVE_OSX
589- GTK_STOCK_HELP, GTK_RESPONSE_HELP,
590- GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
591- #endif
592- NULL);
593- katze_widget_add_class (gtk_dialog_get_widget_for_response (
594- GTK_DIALOG (dialog), GTK_RESPONSE_HELP), "help_button");
595- content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
596- gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_PROPERTIES);
597- /* TODO: Implement some kind of help function */
598- gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
599- GTK_RESPONSE_HELP, FALSE);
600- sokoke_widget_get_text_size (dialog, "M", &width, &height);
601- gtk_window_set_default_size (GTK_WINDOW (dialog), width * 52, -1);
602- g_signal_connect_swapped (dialog, "response",
603- G_CALLBACK (gtk_widget_destroy), dialog);
604- /* TODO: We need mnemonics */
605- if ((xfce_heading = sokoke_xfce_header_new (
606- gtk_window_get_icon_name (GTK_WINDOW (dialog)), dialog_title)))
607- gtk_box_pack_start (GTK_BOX (content_area), xfce_heading, FALSE, FALSE, 0);
608- hbox = gtk_hbox_new (FALSE, 0);
609- gtk_box_pack_start (GTK_BOX (content_area), hbox, TRUE, TRUE, 12);
610- vbox = gtk_vbox_new (FALSE, 0);
611- gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 4);
612- button = gtk_label_new (NULL);
613- description = g_strdup_printf (_(
614- "Type the address of a preconfigured filter list in the text entry "
615- "and click \"Add\" to add it to the list. "
616- "You can find more lists at %s."),
617- "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a> "
618- "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>");
619- g_signal_connect (button, "activate-link",
620- G_CALLBACK (adblock_activate_link_cb), NULL);
621- gtk_label_set_markup (GTK_LABEL (button), description);
622- g_free (description);
623- gtk_label_set_line_wrap (GTK_LABEL (button), TRUE);
624- gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 4);
625- entry = katze_uri_entry_new (NULL);
626- gtk_box_pack_start (GTK_BOX (vbox), entry, FALSE, FALSE, 4);
627- liststore = gtk_list_store_new (1, G_TYPE_STRING);
628- treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (liststore));
629- gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
630- column = gtk_tree_view_column_new ();
631- renderer_toggle = gtk_cell_renderer_toggle_new ();
632- gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (column), renderer_toggle, FALSE);
633- gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer_toggle,
634- (GtkCellLayoutDataFunc)adblock_preferences_render_tick_cb,
635- extension, NULL);
636- g_signal_connect (renderer_toggle, "toggled",
637- G_CALLBACK (adblock_preferences_renderer_toggle_toggled_cb), liststore);
638- gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
639- column = gtk_tree_view_column_new ();
640- renderer_text = gtk_cell_renderer_text_new ();
641- gtk_tree_view_column_pack_start (column, renderer_text, TRUE);
642- g_object_set (renderer_text, "editable", TRUE, NULL);
643- g_signal_connect (renderer_text, "edited",
644- G_CALLBACK (adblock_preferences_renderer_text_edited_cb), liststore);
645- gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (column), renderer_text,
646- (GtkCellLayoutDataFunc)adblock_preferences_render_text_cb,
647- extension, NULL);
648- gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
649- scrolled = gtk_scrolled_window_new (NULL, NULL);
650- gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
651- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
652- gtk_container_add (GTK_CONTAINER (scrolled), treeview);
653- gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
654- GTK_SHADOW_IN);
655- gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 5);
656-
657- filters = midori_extension_get_string_list (extension, "filters", NULL);
658- if (filters != NULL)
659- {
660- gsize i = 0;
661- while (filters[i++] != NULL)
662- gtk_list_store_insert_with_values (GTK_LIST_STORE (liststore),
663- NULL, i - 1, 0, filters[i -1], -1);
664- }
665- g_strfreev (filters);
666- g_object_connect (liststore,
667- "signal::row-inserted",
668- adblock_preferences_model_row_changed_cb, extension,
669- "signal::row-changed",
670- adblock_preferences_model_row_changed_cb, extension,
671- "signal::row-deleted",
672- adblock_preferences_model_row_deleted_cb, extension,
673- NULL);
674-
675- g_object_unref (liststore);
676- vbox = gtk_vbox_new (FALSE, 4);
677- gtk_box_pack_start (GTK_BOX (hbox), vbox, FALSE, FALSE, 4);
678- button = gtk_button_new_from_stock (GTK_STOCK_ADD);
679- g_object_set_data (G_OBJECT (dialog), "entry", entry);
680- g_object_set_data (G_OBJECT (button), "entry", entry);
681- g_signal_connect (button, "clicked",
682- G_CALLBACK (adblock_preferences_add_clicked_cb), liststore);
683- g_signal_connect (entry, "activate",
684- G_CALLBACK (adblock_preferences_add_clicked_cb), liststore);
685- gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
686- button = gtk_button_new_from_stock (GTK_STOCK_EDIT);
687- g_object_set_data (G_OBJECT (button), "treeview", treeview);
688- g_signal_connect (button, "clicked",
689- G_CALLBACK (adblock_preferences_edit_clicked_cb), column);
690- gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
691- button = gtk_button_new_from_stock (GTK_STOCK_REMOVE);
692- g_signal_connect (button, "clicked",
693- G_CALLBACK (adblock_preferences_remove_clicked_cb), treeview);
694- gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0);
695- button = gtk_label_new (""); /* This is an invisible separator */
696- gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 8);
697- gtk_widget_set_sensitive (button, FALSE);
698- button = gtk_label_new (""); /* This is an invisible separator */
699- gtk_box_pack_start (GTK_BOX (vbox), button, TRUE, TRUE, 12);
700- button = gtk_button_new_from_stock (GTK_STOCK_GO_DOWN);
701- gtk_widget_set_sensitive (button, FALSE);
702- gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
703- button = gtk_button_new_from_stock (GTK_STOCK_GO_UP);
704- gtk_widget_set_sensitive (button, FALSE);
705- gtk_box_pack_end (GTK_BOX (vbox), button, FALSE, FALSE, 0);
706-
707- #if HAVE_OSX
708- hbox = gtk_hbox_new (FALSE, 0);
709- button = gtk_button_new ();
710- icon = gtk_image_new_from_stock (GTK_STOCK_HELP, GTK_ICON_SIZE_BUTTON);
711- gtk_button_set_image (GTK_BUTTON (button), icon);
712- /* TODO: Implement some kind of help function */
713- gtk_widget_set_sensitive (button, FALSE);
714- /* g_signal_connect (button, "clicked",
715- G_CALLBACK (adblock_preferences_help_clicked_cb), dialog); */
716- gtk_box_pack_end (GTK_BOX (hbox),
717- button, FALSE, FALSE, 4);
718- gtk_box_pack_end (GTK_BOX (content_area),
719- hbox, FALSE, FALSE, 0);
720- #endif
721- gtk_widget_show_all (content_area);
722-
723- g_object_unref (browser);
724-
725- return dialog;
726-}
727-
728-static GtkWidget*
729-adblock_show_preferences_dialog (MidoriExtension* extension,
730- const gchar* uri)
731-{
732- static GtkWidget* dialog = NULL;
733-
734- if (!dialog)
735- {
736- dialog = adblock_get_preferences_dialog (extension);
737- g_signal_connect (dialog, "destroy",
738- G_CALLBACK (gtk_widget_destroyed), &dialog);
739- gtk_widget_show (dialog);
740- }
741- else
742- gtk_window_present (GTK_WINDOW (dialog));
743-
744- if (uri != NULL)
745- {
746- GtkWidget* entry = g_object_get_data (G_OBJECT (dialog), "entry");
747- gtk_entry_set_text (GTK_ENTRY (entry), uri);
748- }
749- return dialog;
750-}
751-
752-static void
753-adblock_open_preferences_cb (MidoriExtension* extension)
754-{
755- adblock_show_preferences_dialog (extension, NULL);
756-}
757-
758-static gint
759-adblock_check_rule (GRegex* regex,
760- const gchar* patt,
761- const gchar* req_uri,
762- const gchar* page_uri)
763-{
764- gchar* opts;
765-
766- if (!g_regex_match_full (regex, req_uri, -1, 0, 0, NULL, NULL))
767- return FALSE;
768-
769- opts = g_hash_table_lookup (optslist, patt);
770- if (opts && g_regex_match_simple (",third-party", opts,
771- G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
772- {
773- if (page_uri && g_regex_match_full (regex, page_uri, -1, 0, 0, NULL, NULL))
774- return FALSE;
775- }
776- /* TODO: Domain opt check */
777- adblock_debug ("blocked by pattern regexp=%s -- %s", g_regex_get_pattern (regex), req_uri);
778- return TRUE;
779-}
780-
781-static gboolean
782-adblock_is_matched_by_pattern (const gchar* req_uri,
783- const gchar* page_uri)
784-{
785- GHashTableIter iter;
786- gpointer patt, regex;
787-
788- if (USE_PATTERN_MATCHING == 0)
789- return FALSE;
790-
791- g_hash_table_iter_init (&iter, pattern);
792- while (g_hash_table_iter_next (&iter, &patt, &regex))
793- {
794- if (adblock_check_rule (regex, patt, req_uri, page_uri))
795- return TRUE;
796- }
797- return FALSE;
798-}
799-
800-static gboolean
801-adblock_is_matched_by_key (const gchar* req_uri,
802- const gchar* page_uri)
803-{
804- gchar* uri;
805- gint len;
806- int pos = 0;
807- GList* regex_bl = NULL;
808- GString* guri;
809- gboolean ret = FALSE;
810- gchar sig[SIGNATURE_SIZE + 1];
811-
812- memset (&sig[0], 0, sizeof (sig));
813- /* Signatures are made on pattern, so we need to convert url to a pattern as well */
814- guri = adblock_fixup_regexp ("", (gchar*)req_uri);
815- uri = guri->str;
816- len = guri->len;
817-
818- for (pos = len - SIGNATURE_SIZE; pos >= 0; pos--)
819- {
820- GRegex* regex;
821- strncpy (sig, uri + pos, SIGNATURE_SIZE);
822- regex = g_hash_table_lookup (keys, sig);
823-
824- /* Dont check if regex is already blacklisted */
825- if (!regex || g_list_find (regex_bl, regex))
826- continue;
827- ret = adblock_check_rule (regex, sig, req_uri, page_uri);
828- if (ret)
829- break;
830- regex_bl = g_list_prepend (regex_bl, regex);
831- }
832- g_string_free (guri, TRUE);
833- g_list_free (regex_bl);
834- return ret;
835-}
836-
837-static gboolean
838-adblock_is_matched (const gchar* req_uri,
839- const gchar* page_uri)
840-{
841- gchar* value;
842-
843- if ((value = g_hash_table_lookup (urlcache, req_uri)))
844- {
845- if (value[0] == '0')
846- return FALSE;
847- else
848- return TRUE;
849- }
850-
851- if (adblock_is_matched_by_key (req_uri, page_uri))
852- {
853- g_hash_table_insert (urlcache, g_strdup (req_uri), g_strdup("1"));
854- return TRUE;
855- }
856-
857- if (adblock_is_matched_by_pattern (req_uri, page_uri))
858- {
859- g_hash_table_insert (urlcache, g_strdup (req_uri), g_strdup("1"));
860- return TRUE;
861- }
862- g_hash_table_insert (urlcache, g_strdup (req_uri), g_strdup("0"));
863- return FALSE;
864-}
865-
866-static gchar*
867-adblock_prepare_urihider_js (GList* uris)
868-{
869- GList* li = NULL;
870- GString* js = g_string_new (
871- "(function() {"
872- "function getElementsByAttribute (strTagName, strAttributeName, arrAttributeValue) {"
873- " var arrElements = document.getElementsByTagName (strTagName);"
874- " var arrReturnElements = new Array();"
875- " for (var j=0; j<arrAttributeValue.length; j++) {"
876- " var strAttributeValue = arrAttributeValue[j];"
877- " for (var i=0; i<arrElements.length; i++) {"
878- " var oCurrent = arrElements[i];"
879- " var oAttribute = oCurrent.getAttribute && oCurrent.getAttribute (strAttributeName);"
880- " if (oAttribute && oAttribute.length > 0 && strAttributeValue.indexOf (oAttribute) != -1)"
881- " arrReturnElements.push (oCurrent);"
882- " }"
883- " }"
884- " return arrReturnElements;"
885- "};"
886- "function hideElementBySrc (uris) {"
887- " var oElements = getElementsByAttribute('img', 'src', uris);"
888- " if (oElements.length == 0)"
889- " oElements = getElementsByAttribute ('iframe', 'src', uris);"
890- " for (var i=0; i<oElements.length; i++) {"
891- " oElements[i].style.visibility = 'hidden !important';"
892- " oElements[i].style.width = '0';"
893- " oElements[i].style.height = '0';"
894- " }"
895- "};"
896- "var uris=new Array ();");
897-
898- for (li = uris; li != NULL; li = g_list_next (li))
899- g_string_append_printf (js, "uris.push ('%s');", (gchar*)li->data);
900-
901- g_string_append (js, "hideElementBySrc (uris);})();");
902-
903- return g_string_free (js, FALSE);
904-}
905-
906-static gboolean
907-adblock_navigation_policy_decision_requested_cb (WebKitWebView* web_view,
908- WebKitWebFrame* web_frame,
909- WebKitNetworkRequest* request,
910- WebKitWebNavigationAction* action,
911- WebKitWebPolicyDecision* decision,
912- MidoriExtension* extension)
913-{
914- const gchar* uri = webkit_network_request_get_uri (request);
915- if (g_str_has_prefix (uri, "abp:"))
916- {
917- gchar** parts;
918- gchar* filter;
919- if (g_str_has_prefix (uri, "abp:subscribe?location="))
920- uri = &uri[23];
921- else if (g_str_has_prefix (uri, "abp://subscribe?location="))
922- uri = &uri[25];
923- else
924- return FALSE;
925-
926- parts = g_strsplit (uri, "&", 2);
927- filter = soup_uri_decode (parts[0]);
928- webkit_web_policy_decision_ignore (decision);
929- adblock_show_preferences_dialog (extension, filter);
930- g_free (filter);
931- g_strfreev (parts);
932- return TRUE;
933- }
934-
935- if (web_frame == webkit_web_view_get_main_frame (web_view))
936- {
937- const gchar* req_uri = webkit_network_request_get_uri (request);
938- g_hash_table_replace (navigationwhitelist, web_view, g_strdup (req_uri));
939- }
940- return false;
941-}
942-
943-
944-static void
945-adblock_resource_request_starting_cb (WebKitWebView* web_view,
946- WebKitWebFrame* web_frame,
947- WebKitWebResource* web_resource,
948- WebKitNetworkRequest* request,
949- WebKitNetworkResponse* response,
950- MidoriView* view)
951-{
952- SoupMessage* msg;
953- GList* blocked_uris;
954- const gchar* req_uri;
955- const char *page_uri;
956-
957- page_uri = webkit_web_view_get_uri (web_view);
958- /* Skip checks on about: pages */
959- if (midori_uri_is_blank (page_uri))
960- return;
961-
962- req_uri = webkit_network_request_get_uri (request);
963-
964- if (!g_strcmp0 (req_uri, g_hash_table_lookup (navigationwhitelist, web_view)))
965- return;
966-
967- if (!midori_uri_is_http (req_uri)
968- || g_str_has_suffix (req_uri, "favicon.ico"))
969- return;
970-
971- msg = webkit_network_request_get_message (request);
972- if (!(msg && !g_strcmp0 (msg->method, "GET")))
973- return;
974-
975- if (response != NULL) /* request is caused by redirect */
976- {
977- if (web_frame == webkit_web_view_get_main_frame (web_view))
978- {
979- g_hash_table_replace (navigationwhitelist, web_view, g_strdup (req_uri));
980- return;
981- }
982- }
983-
984- if (midori_debug ("adblock:time"))
985- g_test_timer_start ();
986- if (adblock_is_matched (req_uri, page_uri))
987- {
988- blocked_uris = g_object_get_data (G_OBJECT (web_view), "blocked-uris");
989- blocked_uris = g_list_prepend (blocked_uris, g_strdup (req_uri));
990- webkit_network_request_set_uri (request, "about:blank");
991- g_object_set_data (G_OBJECT (web_view), "blocked-uris", blocked_uris);
992- }
993- if (midori_debug ("adblock:time"))
994- g_debug ("match: %f%s", g_test_timer_elapsed (), "seconds");
995-}
996-
997-static void
998-adblock_custom_block_image_cb (GtkWidget* widget,
999- MidoriExtension* extension)
1000-{
1001- gchar* custom_list;
1002- FILE* list;
1003- MidoriApp* app;
1004- GtkWidget* browser;
1005- GtkWidget* dialog;
1006- GtkWidget* content_area;
1007- GtkSizeGroup* sizegroup;
1008- GtkWidget* hbox;
1009- GtkWidget* label;
1010- GtkWidget* entry;
1011- gchar* title;
1012-
1013- app = midori_extension_get_app (extension);
1014- browser = katze_object_get_object (app, "browser");
1015-
1016- title = _("Edit rule");
1017- dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW (browser),
1018- GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_NO_SEPARATOR,
1019- GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1020- GTK_STOCK_ADD, GTK_RESPONSE_ACCEPT,
1021- NULL);
1022- content_area = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
1023- gtk_window_set_icon_name (GTK_WINDOW (dialog), GTK_STOCK_ADD);
1024- gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
1025- gtk_container_set_border_width (GTK_CONTAINER (content_area), 5);
1026- sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
1027-
1028- hbox = gtk_hbox_new (FALSE, 8);
1029- gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1030- label = gtk_label_new_with_mnemonic (_("_Rule:"));
1031- gtk_size_group_add_widget (sizegroup, label);
1032- gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1033- entry = gtk_entry_new ();
1034- gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
1035- gtk_entry_set_text (GTK_ENTRY (entry),
1036- g_object_get_data (G_OBJECT (widget), "uri"));
1037- gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1038- gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, TRUE, 0);
1039- gtk_widget_show_all (hbox);
1040-
1041- gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
1042- if (gtk_dialog_run (GTK_DIALOG (dialog)) != GTK_RESPONSE_ACCEPT)
1043- {
1044- gtk_widget_destroy (dialog);
1045- return;
1046- }
1047-
1048- custom_list = g_build_filename (midori_extension_get_config_dir (extension),
1049- CUSTOM_LIST_NAME, NULL);
1050- katze_mkdir_with_parents (midori_extension_get_config_dir (extension), 0700);
1051- if ((list = g_fopen (custom_list, "a+")))
1052- {
1053- g_fprintf (list, "%s\n", gtk_entry_get_text (GTK_ENTRY (entry)));
1054- fclose (list);
1055- adblock_reload_rules (extension, TRUE);
1056- g_debug ("%s: Updated custom list\n", G_STRFUNC);
1057- }
1058- else
1059- g_debug ("%s: Failed to open custom list %s\n", G_STRFUNC, custom_list);
1060- g_free (custom_list);
1061- gtk_widget_destroy (dialog);
1062-}
1063-
1064-static void
1065-adblock_populate_popup_cb (WebKitWebView* web_view,
1066- GtkWidget* menu,
1067- MidoriExtension* extension)
1068-{
1069- GtkWidget* menuitem;
1070- gchar *uri;
1071- gint x, y;
1072- GdkEventButton event;
1073- WebKitHitTestResultContext context;
1074- WebKitHitTestResult* hit_test;
1075-
1076- gdk_window_get_pointer (gtk_widget_get_window(GTK_WIDGET (web_view)), &x, &y, NULL);
1077- event.x = x;
1078- event.y = y;
1079- hit_test = webkit_web_view_get_hit_test_result (web_view, &event);
1080- context = katze_object_get_int (hit_test, "context");
1081- if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_IMAGE)
1082- {
1083- uri = katze_object_get_string (hit_test, "image-uri");
1084- menuitem = gtk_menu_item_new_with_mnemonic (_("Bl_ock image"));
1085- }
1086- else if (context & WEBKIT_HIT_TEST_RESULT_CONTEXT_LINK)
1087- {
1088- uri = katze_object_get_string (hit_test, "link-uri");
1089- menuitem = gtk_menu_item_new_with_mnemonic (_("Bl_ock link"));
1090- }
1091- else
1092- return;
1093- gtk_widget_show (menuitem);
1094- gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
1095- g_object_set_data_full (G_OBJECT (menuitem), "uri", uri, (GDestroyNotify)g_free);
1096- g_signal_connect (menuitem, "activate",
1097- G_CALLBACK (adblock_custom_block_image_cb), extension);
1098-}
1099-
1100-static void
1101-adblock_load_finished_cb (WebKitWebView *web_view,
1102- WebKitWebFrame *web_frame,
1103- gpointer user_data)
1104-{
1105- GList* uris = g_object_get_data (G_OBJECT (web_view), "blocked-uris");
1106- gchar* script;
1107- GList* li;
1108-
1109- if (g_list_nth_data (uris, 0) == NULL)
1110- return;
1111-
1112- script = adblock_prepare_urihider_js (uris);
1113- webkit_web_view_execute_script (web_view, script);
1114- li = NULL;
1115- for (li = uris; li != NULL; li = g_list_next (li))
1116- uris = g_list_remove (uris, li->data);
1117- g_free (script);
1118- g_object_set_data (G_OBJECT (web_view), "blocked-uris", uris);
1119-}
1120-
1121-static void
1122-adblock_window_object_cleared_cb (WebKitWebView* web_view,
1123- WebKitWebFrame* web_frame,
1124- JSContextRef js_context,
1125- JSObjectRef js_window)
1126-{
1127- const char *page_uri;
1128- gchar* script;
1129-
1130- page_uri = webkit_web_frame_get_uri (web_frame);
1131- /* Don't add adblock css into speeddial and about: pages */
1132- if (!midori_uri_is_http (page_uri))
1133- return;
1134-
1135- script = adblock_build_js (page_uri);
1136- if (!script)
1137- return;
1138-
1139- g_free (sokoke_js_script_eval (js_context, script, NULL));
1140- g_free (script);
1141-}
1142-
1143-static void
1144-adblock_add_tab_cb (MidoriBrowser* browser,
1145- MidoriView* view,
1146- MidoriExtension* extension)
1147-{
1148- GtkWidget* web_view = midori_view_get_web_view (view);
1149-
1150- g_signal_connect (web_view, "window-object-cleared",
1151- G_CALLBACK (adblock_window_object_cleared_cb), 0);
1152-
1153- g_signal_connect_after (web_view, "populate-popup",
1154- G_CALLBACK (adblock_populate_popup_cb), extension);
1155- g_signal_connect (web_view, "navigation-policy-decision-requested",
1156- G_CALLBACK (adblock_navigation_policy_decision_requested_cb), extension);
1157- g_signal_connect (web_view, "resource-request-starting",
1158- G_CALLBACK (adblock_resource_request_starting_cb), view);
1159- g_signal_connect (web_view, "load-finished",
1160- G_CALLBACK (adblock_load_finished_cb), view);
1161-}
1162-
1163-static void
1164-adblock_remove_tab_cb (MidoriBrowser* browser,
1165- MidoriView* view,
1166- MidoriExtension* extension)
1167-{
1168- GtkWidget* web_view = midori_view_get_web_view (view);
1169- g_hash_table_remove (navigationwhitelist, web_view);
1170-}
1171-
1172-static void
1173-adblock_deactivate_cb (MidoriExtension* extension,
1174- MidoriBrowser* browser);
1175-
1176-static void
1177-adblock_app_add_browser_cb (MidoriApp* app,
1178- MidoriBrowser* browser,
1179- MidoriExtension* extension)
1180-{
1181- GtkWidget* statusbar;
1182- GtkWidget* image;
1183- GList* children;
1184-
1185- statusbar = katze_object_get_object (browser, "statusbar");
1186- image = NULL;
1187- /* image = gtk_image_new_from_stock (STOCK_IMAGE, GTK_ICON_SIZE_MENU);
1188- gtk_widget_show (image);
1189- gtk_box_pack_start (GTK_BOX (statusbar), image, FALSE, FALSE, 3); */
1190- g_object_set_data_full (G_OBJECT (browser), "status-image", image,
1191- (GDestroyNotify)gtk_widget_destroy);
1192-
1193- children = midori_browser_get_tabs (MIDORI_BROWSER (browser));
1194- for (; children; children = g_list_next (children))
1195- adblock_add_tab_cb (browser, children->data, extension);
1196- g_list_free (children);
1197-
1198- g_signal_connect (browser, "add-tab",
1199- G_CALLBACK (adblock_add_tab_cb), extension);
1200- g_signal_connect (browser, "remove-tab",
1201- G_CALLBACK (adblock_remove_tab_cb), extension);
1202- g_signal_connect (extension, "open-preferences",
1203- G_CALLBACK (adblock_open_preferences_cb), extension);
1204- g_signal_connect (extension, "deactivate",
1205- G_CALLBACK (adblock_deactivate_cb), browser);
1206- g_object_unref (statusbar);
1207-}
1208-
1209-static GString*
1210-adblock_fixup_regexp (const gchar* prefix,
1211- gchar* src)
1212-{
1213- GString* str;
1214- int len = 0;
1215-
1216- if (!src)
1217- return NULL;
1218-
1219- str = g_string_new (prefix);
1220-
1221- /* lets strip first .* */
1222- if (src[0] == '*')
1223- {
1224- (void)*src++;
1225- }
1226-
1227- do
1228- {
1229- switch (*src)
1230- {
1231- case '*':
1232- g_string_append (str, ".*");
1233- break;
1234- /*case '.':
1235- g_string_append (str, "\\.");
1236- break;*/
1237- case '?':
1238- g_string_append (str, "\\?");
1239- break;
1240- case '|':
1241- /* FIXME: We actually need to match :[0-9]+ or '/'. Sign means
1242- "here could be port number or nothing". So bla.com^ will match
1243- bla.com/ or bla.com:8080/ but not bla.com.au/ */
1244- case '^':
1245- case '+':
1246- break;
1247- case '[':
1248- g_string_append (str, "\\[");
1249- break;
1250- case ']':
1251- g_string_append (str, "\\]");
1252- break;
1253- case '(':
1254- g_string_append (str, "\\(");
1255- break;
1256- case ')':
1257- g_string_append (str, "\\)");
1258- break;
1259- default:
1260- g_string_append_printf (str,"%c", *src);
1261- break;
1262- }
1263- src++;
1264- }
1265- while (*src);
1266-
1267- len = str->len;
1268- /* We dont need .* in the end of url. Thats stupid */
1269- if (str->str && str->str[len-1] == '*' && str->str[len-2] == '.')
1270- g_string_erase (str, len-2, 2);
1271-
1272- return str;
1273-}
1274-
1275-static gboolean
1276-adblock_compile_regexp (GString* gpatt,
1277- gchar* opts)
1278-{
1279- GRegex* regex;
1280- GError* error = NULL;
1281- int pos = 0;
1282- gchar *sig;
1283- gchar *patt;
1284- int len;
1285-
1286- if (!gpatt)
1287- return FALSE;
1288-
1289- patt = gpatt->str;
1290- len = gpatt->len;
1291-
1292- /* TODO: Play with optimization flags */
1293- regex = g_regex_new (patt, G_REGEX_OPTIMIZE,
1294- G_REGEX_MATCH_NOTEMPTY, &error);
1295- if (error)
1296- {
1297- g_warning ("%s: %s", G_STRFUNC, error->message);
1298- g_error_free (error);
1299- return TRUE;
1300- }
1301-
1302- if (!g_regex_match_simple ("^/.*[\\^\\$\\*].*/$", patt, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY))
1303- {
1304- int signature_count = 0;
1305-
1306- for (pos = len - SIGNATURE_SIZE; pos >= 0; pos--) {
1307- sig = g_strndup (patt + pos, SIGNATURE_SIZE);
1308- if (!g_regex_match_simple ("[\\*]", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) &&
1309- !g_hash_table_lookup (keys, sig))
1310- {
1311- adblock_debug ("sig: %s %s", sig, patt);
1312- g_hash_table_insert (keys, sig, regex);
1313- g_hash_table_insert (optslist, sig, g_strdup (opts));
1314- signature_count++;
1315- }
1316- else
1317- {
1318- if (g_regex_match_simple ("^\\*", sig, G_REGEX_UNGREEDY, G_REGEX_MATCH_NOTEMPTY) &&
1319- !g_hash_table_lookup (pattern, patt))
1320- {
1321- adblock_debug ("patt2: %s %s", sig, patt);
1322- g_hash_table_insert (pattern, patt, regex);
1323- g_hash_table_insert (optslist, patt, g_strdup (opts));
1324- }
1325- g_free (sig);
1326- }
1327- }
1328- if (signature_count > 1 && g_hash_table_lookup (pattern, patt))
1329- {
1330- g_hash_table_steal (pattern, patt);
1331- return TRUE;
1332- }
1333- return FALSE;
1334- }
1335- else
1336- {
1337- adblock_debug ("patt: %s%s", patt, "");
1338- /* Pattern is a regexp chars */
1339- g_hash_table_insert (pattern, patt, regex);
1340- g_hash_table_insert (optslist, patt, g_strdup (opts));
1341- return FALSE;
1342- }
1343-}
1344-
1345-static gchar*
1346-adblock_add_url_pattern (gchar* prefix,
1347- gchar* type,
1348- gchar* line)
1349-{
1350- gchar** data;
1351- gchar* patt;
1352- GString* format_patt;
1353- gchar* opts;
1354- gboolean should_free;
1355-
1356- data = g_strsplit (line, "$", -1);
1357- if (!data || !data[0])
1358- {
1359- g_strfreev (data);
1360- return NULL;
1361- }
1362-
1363- if (data[1] && data[2])
1364- {
1365- patt = g_strconcat (data[0], data[1], NULL);
1366- opts = g_strconcat (type, ",", data[2], NULL);
1367- }
1368- else if (data[1])
1369- {
1370- patt = data[0];
1371- opts = g_strconcat (type, ",", data[1], NULL);
1372- }
1373- else
1374- {
1375- patt = data[0];
1376- opts = type;
1377- }
1378-
1379- if (g_regex_match_simple ("subdocument", opts,
1380- G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY))
1381- {
1382- if (data[1] && data[2])
1383- g_free (patt);
1384- if (data[1])
1385- g_free (opts);
1386- g_strfreev (data);
1387- return NULL;
1388- }
1389-
1390- format_patt = adblock_fixup_regexp (prefix, patt);
1391-
1392- adblock_debug ("got: %s opts %s", format_patt->str, opts);
1393- should_free = adblock_compile_regexp (format_patt, opts);
1394-
1395- if (data[1] && data[2])
1396- g_free (patt);
1397- if (data[1])
1398- g_free (opts);
1399- g_strfreev (data);
1400-
1401- return g_string_free (format_patt, should_free);
1402-}
1403-
1404-static void
1405-adblock_frame_add (gchar* line)
1406-{
1407- const gchar* separator = " , ";
1408-
1409- (void)*line++;
1410- (void)*line++;
1411- if (strchr (line, '\'')
1412- || (strchr (line, ':')
1413- && !g_regex_match_simple (".*\\[.*:.*\\].*", line,
1414- G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY)))
1415- {
1416- return;
1417- }
1418- g_string_append (blockcss, separator);
1419- g_string_append (blockcss, line);
1420-}
1421-
1422-static void
1423-adblock_update_css_hash (gchar* domain,
1424- gchar* value)
1425-{
1426- const gchar* olddata;
1427- gchar* newdata;
1428-
1429- if ((olddata = g_hash_table_lookup (blockcssprivate, domain)))
1430- {
1431- newdata = g_strconcat (olddata, " , ", value, NULL);
1432- g_hash_table_replace (blockcssprivate, g_strdup (domain), newdata);
1433- }
1434- else
1435- g_hash_table_insert (blockcssprivate, g_strdup (domain), g_strdup (value));
1436-}
1437-
1438-static void
1439-adblock_frame_add_private (const gchar* line,
1440- const gchar* sep)
1441-{
1442- gchar** data;
1443- data = g_strsplit (line, sep, 2);
1444-
1445- if (!(data[1] && *data[1])
1446- || strchr (data[1], '\'')
1447- || (strchr (data[1], ':')
1448- && !g_regex_match_simple (".*\\[.*:.*\\].*", data[1],
1449- G_REGEX_CASELESS, G_REGEX_MATCH_NOTEMPTY)))
1450- {
1451- g_strfreev (data);
1452- return;
1453- }
1454-
1455- if (strchr (data[0], ','))
1456- {
1457- gchar** domains;
1458- gint i;
1459-
1460- domains = g_strsplit (data[0], ",", -1);
1461- for (i = 0; domains[i]; i++)
1462- {
1463- gchar* domain;
1464-
1465- domain = domains[i];
1466- /* Ignore Firefox-specific option */
1467- if (!g_strcmp0 (domain, "~pregecko2"))
1468- continue;
1469- /* FIXME: ~ should negate match */
1470- if (domain[0] == '~')
1471- domain++;
1472- adblock_update_css_hash (g_strstrip (domain), data[1]);
1473- }
1474- g_strfreev (domains);
1475- }
1476- else
1477- {
1478- adblock_update_css_hash (data[0], data[1]);
1479- }
1480- g_strfreev (data);
1481-}
1482-
1483-static gchar*
1484-adblock_parse_line (gchar* line)
1485-{
1486- /*
1487- * AdblockPlus rule reference based on http://adblockplus.org/en/filters
1488- * Block URL:
1489- * http://example.com/ads/banner123.gif
1490- * http://example.com/ads/banner*.gif
1491- * Partial match for "ad":
1492- * *ad*
1493- * ad
1494- * Block example.com/annoyingflash.swf but not example.com/swf/:
1495- * swf|
1496- * Block bad.example/banner.gif but not good.example/analyze?http://bad.example:
1497- * |http://baddomain.example/
1498- * Block http(s) example.com but not badexample.com or good.example/analyze?http://bad.example:
1499- * ||example.com/banner.gif
1500- * Block example.com/ and example.com:8000/ but not example.com.ar/:
1501- * http://example.com^
1502- * A ^ matches anything that isn't A-Za-z0-0_-.%
1503- * Block example.com:8000/foo.bar?a=12&b=%D1%82%D0%B5:
1504- * ^example.com^
1505- * ^%D1%82%D0%B5^
1506- * ^foo.bar^
1507- * TODO: ^ is partially supported by Midori
1508- * Block banner123 and banner321 with a regex:
1509- * /banner\d+/
1510- * Never block URIs with "advice":
1511- * @@advice
1512- * No blocking at all:
1513- * @@http://example.com
1514- * @@|http://example.com
1515- * TODO: @@ is currently ignored by Midori.
1516- * Element hiding by class:
1517- * ##textad
1518- * ##div.textad
1519- * Element hiding by id:
1520- * ##div#sponsorad
1521- * ##*#sponsorad
1522- * Match example.com/ and something.example.com/ but not example.org/
1523- * example.com##*.sponsor
1524- * Match multiple domains:
1525- * domain1.example,domain2.example,domain3.example##*.sponsor
1526- * Match on any domain but "example.com":
1527- * ~example.com##*.sponsor
1528- * Match on "example.com" except "foo.example.com":
1529- * example.com,~foo.example.com##*.sponsor
1530- * By design rules only apply to full domain names:
1531- * "domain" is NOT equal to "domain.example,domain.test."
1532- * In Firefox rules can apply to browser UI:
1533- * browser##menuitem#javascriptConsole will hide the Console menuitem
1534- * Hide tables with width attribute 80%:
1535- * ##table[width="80%"]
1536- * Hide all div with title attribute containing "adv":
1537- * ##div[title*="adv"]
1538- * Hide div with title starting with "adv" and ending with "ert":
1539- * ##div[title^="adv"][title$="ert"]
1540- * Match tables with width attribute 80% and bgcolor attribute white:
1541- * table[width="80%"][bgcolor="white"]
1542- * TODO: [] is currently ignored by Midori
1543- * Hide anything following div with class "adheader":
1544- * ##div.adheader + *
1545- * Old CSS element hiding syntax, officially deprecated:
1546- * #div(id=foo)
1547- * Match anything but "example.com"
1548- * ~example.com##*.sponsor
1549- * TODO: ~ is currently ignored by Midori
1550- * Match "example.com" domain except "foo.example.com":
1551- * example.com,~foo.example.com##*.sponsor
1552- * ! Comment
1553- * Supported options after a trailing $:
1554- * domain,third-party,~pregecko2
1555- * Official options (not all supported by Midori):
1556- * script,image,stylesheet,object,xmlhttprequest,object-subrequest,
1557- * subdocument,document,elemhide,popup,third-party,sitekey,match-case
1558- * collapse,donottrack,pregecko2
1559- * Deprecated:
1560- * background,xbl,ping,dtd
1561- * Inverse options:
1562- * ~script,~image,~stylesheet,~object,~xmlhttprequest,~collapse,
1563- * ~object-subrequest,~subdocument,~document,~elemhide,~third-party,
1564- * ~pregecko2
1565- **/
1566-
1567- /* Skip invalid, empty and comment lines */
1568- if (!(line && line[0] != ' ' && line[0] != '!' && line[0]))
1569- return NULL;
1570-
1571- /* FIXME: No support for whitelisting */
1572- if (line[0] == '@' && line[1] == '@')
1573- return NULL;
1574- /* FIXME: No support for [include] and [exclude] tags */
1575- if (line[0] == '[')
1576- return NULL;
1577-
1578- g_strchomp (line);
1579-
1580- /* Got CSS block hider */
1581- if (line[0] == '#' && line[1] == '#' )
1582- {
1583- adblock_frame_add (line);
1584- return NULL;
1585- }
1586- /* Got CSS block hider. Workaround */
1587- if (line[0] == '#')
1588- return NULL;
1589-
1590- /* Got per domain CSS hider rule */
1591- if (strstr (line, "##"))
1592- {
1593- adblock_frame_add_private (line, "##");
1594- return NULL;
1595- }
1596- /* Got per domain CSS hider rule. Workaround */
1597- if (strchr (line, '#'))
1598- {
1599- adblock_frame_add_private (line, "#");
1600- return NULL;
1601- }
1602-
1603- /* Got URL blocker rule */
1604- if (line[0] == '|' && line[1] == '|' )
1605- {
1606- (void)*line++;
1607- (void)*line++;
1608- return adblock_add_url_pattern ("", "fulluri", line);
1609- }
1610- if (line[0] == '|')
1611- {
1612- (void)*line++;
1613- return adblock_add_url_pattern ("^", "fulluri", line);
1614- }
1615- return adblock_add_url_pattern ("", "uri", line);
1616-}
1617-
1618-static GDateMonth
1619-str_month_name_to_gdate (const gchar* month)
1620-{
1621- guint i;
1622- const gchar* months[] = {
1623- "", "January", "February", "March", "April", "May", "June",
1624- "July", "August", "September", "October", "November", "December"
1625- };
1626-
1627- for (i = 0; i < G_N_ELEMENTS (months); i++)
1628- {
1629- if (strncmp (month, months[i], 3) == 0)
1630- return i;
1631- }
1632- return 0;
1633-}
1634-
1635-static gboolean
1636-adblock_file_is_up_to_date (gchar* path)
1637-{
1638- FILE* file;
1639- gchar line[2000];
1640-
1641- /* Check a chunk of header for update info */
1642- if ((file = g_fopen (path, "r")))
1643- {
1644- gint days_to_expire = 0;
1645- gchar* timestamp = NULL;
1646- guint i;
1647- gboolean found_meta = FALSE;
1648- gint fs_days_elapsed, days_elapsed = 0, least_days;
1649-
1650- for (i = 0; i <= 15; i++)
1651- {
1652- if (!fgets (line, 2000, file))
1653- break;
1654-
1655- if (strncmp ("! Expires", line, 9) == 0)
1656- {
1657- gchar** parts = g_strsplit (line, " ", 4);
1658- days_to_expire = atoi (parts[2]);
1659- g_strfreev (parts);
1660- found_meta = TRUE;
1661- }
1662- if (strncmp ("! This list expires after", line, 25) == 0)
1663- {
1664- gchar** parts = g_strsplit (line, " ", 7);
1665-
1666- if (strncmp (parts[6], "days", 4) == 0)
1667- days_to_expire = atoi (parts[5]);
1668- if (strncmp (parts[6], "hours", 5) == 0)
1669- days_to_expire = (atoi (parts[5])) / 24;
1670-
1671- g_strfreev (parts);
1672- found_meta = TRUE;
1673- }
1674-
1675- if (strncmp ("! Last mod", line, 10) == 0
1676- || strncmp ("! Updated", line, 9) == 0)
1677- {
1678- gchar** parts = g_strsplit (line, ":", 2);
1679- timestamp = g_strdup (parts[1] + 1);
1680- g_strchomp (timestamp);
1681- g_strfreev (parts);
1682- found_meta = TRUE;
1683- }
1684- }
1685-
1686- if (!found_meta)
1687- {
1688- g_print ("Adblock: no metadata found in %s (broken download?)\n", path);
1689- return FALSE;
1690- }
1691-
1692- /* query filesystem about file change, maybe there is no update yet
1693- * or there is no "modified" metadata to check, otherwise we will repeatedly
1694- * download files that have no new updates */
1695- {
1696- GDate* current = g_date_new ();
1697- GDate* fs_mod_date = g_date_new ();
1698- GTimeVal mod_time;
1699- GFile* filter_file = g_file_new_for_path (path);
1700- GFileInfo* info = g_file_query_info (filter_file, "time:modified", 0, NULL, NULL);
1701-
1702- g_file_info_get_modification_time (info, &mod_time);
1703- g_date_set_time_t (current, time (NULL));
1704- g_date_set_time_val (fs_mod_date, &mod_time);
1705-
1706- fs_days_elapsed = g_date_days_between (fs_mod_date, current);
1707-
1708- g_date_free (current);
1709- g_date_free (fs_mod_date);
1710- }
1711-
1712- /* If there is no update metadata but file is valid, assume one week */
1713- if ((!days_to_expire && !timestamp) && fs_days_elapsed < 7)
1714- return TRUE;
1715-
1716- if (days_to_expire && timestamp != NULL)
1717- {
1718- GDate* current = g_date_new ();
1719- GDate* mod_date = g_date_new ();
1720- gchar** parts;
1721- gboolean use_dots = FALSE;
1722-
1723- /* Common dates are 20 Mar 2012, 20.08.2012 */
1724- if (strrchr (timestamp, '.'))
1725- {
1726- use_dots = TRUE;
1727- /* In case of date like '20.08.2012 12:34'
1728- * we should also nuke the time part */
1729- if (strrchr (timestamp, ' '))
1730- {
1731- gchar** part = g_strsplit (timestamp, " ", 2);
1732- parts = g_strsplit (part[0], ".", 4);
1733- g_strfreev (part);
1734- }
1735- else
1736- parts = g_strsplit (timestamp, ".", 4);
1737- }
1738- else
1739- parts = g_strsplit (timestamp, " ", 4);
1740-
1741- if (use_dots)
1742- g_date_set_month (mod_date, atoi (parts[1]));
1743- else
1744- g_date_set_month (mod_date, str_month_name_to_gdate (parts[1]));
1745-
1746- /* check if first part is year 201(2) or day */
1747- if (strncmp (parts[0], "201", 3) == 0)
1748- {
1749- g_date_set_day (mod_date, atoi (parts[2]));
1750- g_date_set_year (mod_date, atoi (parts[0]));
1751- }
1752- else
1753- {
1754- g_date_set_day (mod_date, atoi (parts[0]));
1755- g_date_set_year (mod_date, atoi (parts[2]));
1756- }
1757- g_strfreev (parts);
1758-
1759- g_date_set_time_t (current, time (NULL));
1760- days_elapsed = g_date_days_between (mod_date, current);
1761-
1762- g_date_free (current);
1763- g_date_free (mod_date);
1764- g_free (timestamp);
1765- }
1766-
1767- /* File from the future? Assume up to date */
1768- if (days_elapsed < 0)
1769- {
1770- g_print ("Adblock: file %s appears to be from the future,"
1771- "check your system clock!\n", path);
1772- return TRUE;
1773- }
1774-
1775- least_days = days_elapsed < fs_days_elapsed ? days_elapsed : fs_days_elapsed;
1776- if (least_days < days_to_expire)
1777- return TRUE;
1778- else
1779- return FALSE;
1780-
1781- return TRUE;
1782- }
1783- return FALSE;
1784-}
1785-
1786-static void
1787-adblock_parse_file (gchar* path)
1788-{
1789- FILE* file;
1790- gchar line[2000];
1791-
1792- if ((file = g_fopen (path, "r")))
1793- {
1794- while (fgets (line, 2000, file))
1795- adblock_parse_line (line);
1796- fclose (file);
1797- }
1798-}
1799-
1800-static void
1801-adblock_deactivate_tabs (MidoriView* view,
1802- MidoriBrowser* browser,
1803- MidoriExtension* extension)
1804-{
1805- GtkWidget* web_view = midori_view_get_web_view (view);
1806-
1807- g_signal_handlers_disconnect_by_func (
1808- web_view, adblock_window_object_cleared_cb, 0);
1809- g_signal_handlers_disconnect_by_func (
1810- web_view, adblock_populate_popup_cb, extension);
1811- g_signal_handlers_disconnect_by_func (
1812- web_view, adblock_resource_request_starting_cb, view);
1813- g_signal_handlers_disconnect_by_func (
1814- web_view, adblock_load_finished_cb, view);
1815- g_signal_handlers_disconnect_by_func (
1816- web_view, adblock_navigation_policy_decision_requested_cb, extension);
1817-}
1818-
1819-static void
1820-adblock_deactivate_cb (MidoriExtension* extension,
1821- MidoriBrowser* browser)
1822-{
1823- GList* children;
1824- MidoriApp* app = midori_extension_get_app (extension);
1825- MidoriWebSettings* settings = katze_object_get_object (app, "settings");
1826-
1827- g_signal_handlers_disconnect_by_func (
1828- browser, adblock_open_preferences_cb, extension);
1829- g_signal_handlers_disconnect_by_func (
1830- extension, adblock_deactivate_cb, browser);
1831- g_signal_handlers_disconnect_by_func (
1832- app, adblock_app_add_browser_cb, extension);
1833- g_signal_handlers_disconnect_by_func (
1834- browser, adblock_add_tab_cb, extension);
1835- g_signal_handlers_disconnect_by_func (
1836- browser, adblock_remove_tab_cb, extension);
1837-
1838- children = midori_browser_get_tabs (MIDORI_BROWSER (browser));
1839- for (; children; children = g_list_next (children))
1840- adblock_deactivate_tabs (children->data, browser, extension);
1841- g_list_free (children);
1842-
1843- adblock_destroy_db ();
1844- midori_web_settings_remove_style (settings, "adblock-blockcss");
1845- g_object_unref (settings);
1846-}
1847-
1848-static void
1849-adblock_activate_cb (MidoriExtension* extension,
1850- MidoriApp* app)
1851-{
1852- KatzeArray* browsers;
1853- MidoriBrowser* browser;
1854-
1855- adblock_reload_rules (extension, FALSE);
1856-
1857- browsers = katze_object_get_object (app, "browsers");
1858- KATZE_ARRAY_FOREACH_ITEM (browser, browsers)
1859- adblock_app_add_browser_cb (app, browser, extension);
1860- g_signal_connect (app, "add-browser",
1861- G_CALLBACK (adblock_app_add_browser_cb), extension);
1862-
1863- g_object_unref (browsers);
1864-}
1865-
1866-static void
1867-test_adblock_parse (void)
1868-{
1869- adblock_init_db ();
1870- g_assert (!adblock_parse_line (NULL));
1871- g_assert (!adblock_parse_line ("!"));
1872- g_assert (!adblock_parse_line ("@@"));
1873- g_assert (!adblock_parse_line ("##"));
1874- g_assert (!adblock_parse_line ("["));
1875-
1876- g_assert_cmpstr (adblock_parse_line ("+advert/"), ==, "advert/");
1877- g_assert_cmpstr (adblock_parse_line ("*foo"), ==, "foo");
1878- g_assert_cmpstr (adblock_parse_line ("f*oo"), ==, "f.*oo");
1879- g_assert_cmpstr (adblock_parse_line ("?foo"), ==, "\\?foo");
1880- g_assert_cmpstr (adblock_parse_line ("foo?"), ==, "foo\\?");
1881-
1882- g_assert_cmpstr (adblock_parse_line (".*foo/bar"), ==, "..*foo/bar");
1883- g_assert_cmpstr (adblock_parse_line ("http://bla.blub/*"), ==, "http://bla.blub/");
1884- g_assert_cmpstr (adblock_parse_line ("bag?r[]=*cpa"), ==, "bag\\?r\\[\\]=.*cpa");
1885- g_assert_cmpstr (adblock_parse_line ("(facebookLike,"), ==, "\\(facebookLike,");
1886- adblock_destroy_db ();
1887-}
1888-
1889-static void
1890-test_subscription_update (void)
1891-{
1892- gint temp;
1893- gchar* filename;
1894-
1895- adblock_init_db ();
1896- temp = g_file_open_tmp ("midori_adblock_update_test_XXXXXX", &filename, NULL);
1897- close (temp);
1898-
1899- g_file_set_contents (filename, "", -1, NULL);
1900- g_assert (!adblock_file_is_up_to_date (filename));
1901-
1902- g_file_set_contents (filename,
1903- "[Adblock Plus 1.1]\n"
1904- "! Checksum: 48f6Qdo4PsNogsurLvQ71w\n"
1905- "! Title: EasyList\n"
1906- "! Last modified: 05 Sep 2010 11:00 UTC\n"
1907- "! This list expires after 48 hours\n",
1908- -1, NULL);
1909- g_assert (!adblock_file_is_up_to_date (filename));
1910-
1911- g_file_set_contents (filename,
1912- "[Adblock Plus 1.1]\n"
1913- "! Checksum: 48f6Qdo4PsNogsurLvQ71w\n"
1914- "! Title: EasyList\n"
1915- "! Last modified: 05.09.2010 11:00 UTC\n"
1916- "! Expires: 2 days (update frequency)\n",
1917- -1, NULL);
1918- g_assert (!adblock_file_is_up_to_date (filename));
1919-
1920- g_file_set_contents (filename,
1921- "[Adblock Plus 1.1]\n"
1922- "! Checksum: 48f6Qdo4PsNogsurLvQ71w\n"
1923- "! Title: EasyList\n"
1924- "! Updated: 05 Nov 2014 11:00 UTC\n"
1925- "! Expires: 5 days (update frequency)\n",
1926- -1, NULL);
1927- g_assert (adblock_file_is_up_to_date (filename));
1928-
1929- g_file_set_contents (filename,
1930- "[Adblock]\n"
1931- "! dutchblock v3\n"
1932- "! This list expires after 14 days\n"
1933- "|http://b*.mookie1.com/\n",
1934- -1, NULL);
1935- g_assert (adblock_file_is_up_to_date (filename));
1936-
1937- g_file_set_contents (filename,
1938- "[Adblock Plus 2.0]\n"
1939- "! Checksum: S4reE8XaYTtAFxe2RjgBPg\n"
1940- "! Last modification time (GMT): 2012.11.05 13:33\n"
1941- "! Expires: 5 days (update frequency)\n",
1942- -1, NULL);
1943- g_assert (!adblock_file_is_up_to_date (filename));
1944-
1945- g_file_set_contents (filename,
1946- "[Adblock Plus 2.0]\n"
1947- "! Checksum: S4reE8XaYTtAFxe2RjgBPg\n"
1948- "! Last modification time (GMT): 2012.11.05 13:33\n",
1949- -1, NULL);
1950- g_assert (!adblock_file_is_up_to_date (filename));
1951-
1952- g_unlink (filename);
1953- g_free (filename);
1954- adblock_destroy_db ();
1955-}
1956-
1957-static void
1958-test_adblock_pattern (void)
1959-{
1960- gint temp;
1961- gchar* filename;
1962-
1963- adblock_init_db ();
1964- temp = g_file_open_tmp ("midori_adblock_match_test_XXXXXX", &filename, NULL);
1965-
1966- /* TODO: Update some tests and add new ones. */
1967- g_file_set_contents (filename,
1968- "*ads.foo.bar*\n"
1969- "*ads.bogus.name*\n"
1970- "||^http://ads.bla.blub/*\n"
1971- "|http://ads.blub.boing/*$domain=xxx.com\n"
1972- "engine.adct.ru/*?\n"
1973- "/addyn|*|adtech;\n"
1974- "doubleclick.net/pfadx/*.mtvi\n"
1975- "objects.tremormedia.com/embed/xml/*.xml?r=\n"
1976- "videostrip.com^*/admatcherclient.\n"
1977- "test.dom/test?var\n"
1978- "/adpage.\n"
1979- "br.gcl.ru/cgi-bin/br/",
1980- -1, NULL);
1981-
1982- adblock_parse_file (filename);
1983-
1984- g_test_timer_start ();
1985- g_assert (adblock_is_matched ("http://www.engadget.com/_uac/adpage.html", ""));
1986- g_assert (adblock_is_matched ("http://test.dom/test?var=1", ""));
1987- g_assert (adblock_is_matched ("http://ads.foo.bar/teddy", ""));
1988- g_assert (!adblock_is_matched ("http://ads.fuu.bar/teddy", ""));
1989- g_assert (adblock_is_matched ("https://ads.bogus.name/blub", ""));
1990- g_assert (adblock_is_matched ("http://ads.bla.blub/kitty", ""));
1991- g_assert (adblock_is_matched ("http://ads.blub.boing/soda", ""));
1992- g_assert (!adblock_is_matched ("http://ads.foo.boing/beer", ""));
1993- g_assert (adblock_is_matched ("https://testsub.engine.adct.ru/test?id=1", ""));
1994- if (USE_PATTERN_MATCHING)
1995- g_assert (adblock_is_matched ("http://test.ltd/addyn/test/test?var=adtech;&var2=1", ""));
1996- g_assert (adblock_is_matched ("http://add.doubleclick.net/pfadx/aaaa.mtvi", ""));
1997- g_assert (!adblock_is_matched ("http://add.doubleclick.net/pfadx/aaaa.mtv", ""));
1998- g_assert (adblock_is_matched ("http://objects.tremormedia.com/embed/xml/list.xml?r=", ""));
1999- g_assert (!adblock_is_matched ("http://qq.videostrip.c/sub/admatcherclient.php", ""));
2000- g_assert (adblock_is_matched ("http://qq.videostrip.com/sub/admatcherclient.php", ""));
2001- g_assert (adblock_is_matched ("http://qq.videostrip.com/sub/admatcherclient.php", ""));
2002- g_assert (adblock_is_matched ("http://br.gcl.ru/cgi-bin/br/test", ""));
2003- g_assert (!adblock_is_matched ("https://bugs.webkit.org/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&long_desc_type=substring&long_desc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&keywords_type=allwords&keywords=&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailassigned_to1=1&emailtype1=substring&email1=&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailtype2=substring&email2=&bugidtype=include&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&query_based_on=gtkport&field0-0-0=keywords&type0-0-0=anywordssubstr&value0-0-0=Gtk%20Cairo%20soup&field0-0-1=short_desc&type0-0-1=anywordssubstr&value0-0-1=Gtk%20Cairo%20soup%20autoconf%20automake%20autotool&field0-0-2=component&type0-0-2=equals&value0-0-2=WebKit%20Gtk", ""));
2004- g_assert (!adblock_is_matched ("http://www.engadget.com/2009/09/24/google-hits-android-rom-modder-with-a-cease-and-desist-letter/", ""));
2005- g_assert (!adblock_is_matched ("http://karibik-invest.com/es/bienes_raices/search.php?sqT=19&sqN=&sqMp=&sqL=0&qR=1&sqMb=&searchMode=1&action=B%FAsqueda", ""));
2006- g_assert (!adblock_is_matched ("http://google.com", ""));
2007-
2008- g_print ("Search took %f seconds\n", g_test_timer_elapsed ());
2009-
2010- close (temp);
2011- g_unlink (filename);
2012- adblock_destroy_db ();
2013-}
2014-
2015-void
2016-extension_test (void)
2017-{
2018- g_test_add_func ("/extensions/adblock/parse", test_adblock_parse);
2019- g_test_add_func ("/extensions/adblock/pattern", test_adblock_pattern);
2020- g_test_add_func ("/extensions/adblock/update", test_subscription_update);
2021-}
2022-
2023-MidoriExtension*
2024-extension_init (void)
2025-{
2026- MidoriExtension* extension = g_object_new (MIDORI_TYPE_EXTENSION,
2027- "name", _("Advertisement blocker"),
2028- "description", _("Block advertisements according to a filter list"),
2029- "version", "0.6" MIDORI_VERSION_SUFFIX,
2030- "authors", "Christian Dywan <christian@twotoasts.de>",
2031- NULL);
2032- midori_extension_install_string_list (extension, "filters", NULL, G_MAXSIZE);
2033-
2034- g_signal_connect (extension, "activate",
2035- G_CALLBACK (adblock_activate_cb), NULL);
2036-
2037- return extension;
2038-}
2039
2040=== added file 'extensions/adblock/config.vala'
2041--- extensions/adblock/config.vala 1970-01-01 00:00:00 +0000
2042+++ extensions/adblock/config.vala 2014-02-20 20:31:17 +0000
2043@@ -0,0 +1,107 @@
2044+/*
2045+ Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
2046+
2047+ This library is free software; you can redistribute it and/or
2048+ modify it under the terms of the GNU Lesser General Public
2049+ License as published by the Free Software Foundation; either
2050+ version 2.1 of the License, or (at your option) any later version.
2051+
2052+ See the file COPYING for the full license text.
2053+*/
2054+
2055+namespace Adblock {
2056+ public class Config : GLib.Object {
2057+ List<Subscription> subscriptions;
2058+ string? path;
2059+ KeyFile keyfile;
2060+ Subscription? custom;
2061+
2062+ public Config (string? path) {
2063+ subscriptions = new GLib.List<Subscription> ();
2064+
2065+ this.path = path;
2066+ if (path == null)
2067+ return;
2068+
2069+ string custom_list = GLib.Path.build_filename (path, "custom.list");
2070+ try {
2071+ custom = new Subscription (Filename.to_uri (custom_list, null));
2072+ subscriptions.append (custom);
2073+ } catch (Error error) {
2074+ custom = null;
2075+ warning ("Failed to add custom list %s: %s", custom_list, error.message);
2076+ }
2077+
2078+ string filename = GLib.Path.build_filename (path, "config");
2079+ keyfile = new GLib.KeyFile ();
2080+ try {
2081+ keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);
2082+ string[] filters = keyfile.get_string_list ("settings", "filters");
2083+ foreach (string filter in filters) {
2084+ bool active = false;
2085+ string uri = filter;
2086+ if (filter.has_prefix ("http-"))
2087+ uri = "http:" + filter.substring (6);
2088+ else if (filter.has_prefix ("file-"))
2089+ uri = "file:" + filter.substring (6);
2090+ else if (filter.has_prefix ("https-"))
2091+ uri = "https:" + filter.substring (7);
2092+ else
2093+ active = true;
2094+ Subscription sub = new Subscription (uri);
2095+ sub.active = active;
2096+ sub.add_feature (new Updater ());
2097+ sub.notify["active"].connect (active_changed);
2098+ subscriptions.append (sub);
2099+ }
2100+ } catch (FileError.NOENT exist_error) {
2101+ /* It's no error if no config file exists */
2102+ } catch (GLib.Error settings_error) {
2103+ warning ("Error reading settings from %s: %s\n", filename, settings_error.message);
2104+ }
2105+
2106+ size = subscriptions.length ();
2107+ }
2108+
2109+ void active_changed (Object subscription, ParamSpec pspec) {
2110+ var filters = new StringBuilder ();
2111+ foreach (var sub in subscriptions) {
2112+ if (sub == custom)
2113+ continue;
2114+ if (sub.uri.has_prefix ("http:") && !sub.active)
2115+ filters.append ("http-" + sub.uri.substring (4));
2116+ else if (sub.uri.has_prefix ("file:") && !sub.active)
2117+ filters.append ("file-" + sub.uri.substring (4));
2118+ else if (sub.uri.has_prefix ("https:") && !sub.active)
2119+ filters.append ("https-" + sub.uri.substring (5));
2120+ else
2121+ filters.append (sub.uri);
2122+ filters.append_c (';');
2123+ }
2124+
2125+ string[] list = (filters.str.slice (0, -1)).split (";");
2126+ keyfile.set_string_list ("settings", "filters", list);
2127+ try {
2128+ string filename = GLib.Path.build_filename (path, "config");
2129+ FileUtils.set_contents (filename, keyfile.to_data ());
2130+ } catch (Error error) {
2131+ warning ("Failed to save settings: %s", error.message);
2132+ }
2133+ }
2134+
2135+ public void add_custom_rule (string rule) {
2136+ try {
2137+ var file = File.new_for_uri (custom.uri);
2138+ file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data);
2139+ } catch (Error error) {
2140+ warning ("Failed to add custom rule: %s", error.message);
2141+ }
2142+ }
2143+
2144+ /* foreach support */
2145+ public new Subscription? get (uint index) {
2146+ return subscriptions.nth_data (index);
2147+ }
2148+ public uint size { get; private set; }
2149+ }
2150+}
2151
2152=== added file 'extensions/adblock/element.vala'
2153--- extensions/adblock/element.vala 1970-01-01 00:00:00 +0000
2154+++ extensions/adblock/element.vala 2014-02-20 20:31:17 +0000
2155@@ -0,0 +1,36 @@
2156+/*
2157+ Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
2158+
2159+ This library is free software; you can redistribute it and/or
2160+ modify it under the terms of the GNU Lesser General Public
2161+ License as published by the Free Software Foundation; either
2162+ version 2.1 of the License, or (at your option) any later version.
2163+
2164+ See the file COPYING for the full license text.
2165+*/
2166+
2167+namespace Adblock {
2168+ public class Element : Feature {
2169+ public HashTable<string, string> blockcssprivate;
2170+ bool debug_element;
2171+
2172+ public Element () {
2173+ base ();
2174+ debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
2175+ }
2176+
2177+ public override void clear () {
2178+ blockcssprivate = new HashTable<string, string> (str_hash, str_equal);
2179+ }
2180+
2181+ public string? lookup (string domain) {
2182+ return blockcssprivate.lookup (domain);
2183+ }
2184+
2185+ public void insert (string domain, string value) {
2186+ if (debug_element)
2187+ stdout.printf ("Element to be blocked %s => %s\n", domain, value);
2188+ blockcssprivate.insert (domain, value);
2189+ }
2190+ }
2191+}
2192
2193=== added file 'extensions/adblock/extension.vala'
2194--- extensions/adblock/extension.vala 1970-01-01 00:00:00 +0000
2195+++ extensions/adblock/extension.vala 2014-02-20 20:31:17 +0000
2196@@ -0,0 +1,545 @@
2197+/*
2198+ Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
2199+ Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
2200+
2201+ This library is free software; you can redistribute it and/or
2202+ modify it under the terms of the GNU Lesser General Public
2203+ License as published by the Free Software Foundation; either
2204+ version 2.1 of the License, or (at your option) any later version.
2205+
2206+ See the file COPYING for the full license text.
2207+*/
2208+
2209+namespace Adblock {
2210+ public enum Directive {
2211+ ALLOW,
2212+ BLOCK
2213+ }
2214+
2215+ public class Extension : Midori.Extension {
2216+ Config config;
2217+ HashTable<string, Directive?> cache;
2218+
2219+#if HAVE_WEBKIT2
2220+ public Extension (WebKit.WebExtension web_extension) {
2221+ init ();
2222+ web_extension.page_created.connect (page_created);
2223+ }
2224+
2225+ void page_created (WebKit.WebPage web_page) {
2226+ web_page.send_request.connect (send_request);
2227+ }
2228+
2229+ bool send_request (WebKit.WebPage web_page, WebKit.URIRequest request, WebKit.URIResponse? redirected_response) {
2230+ return request_handled (web_page.uri, request.uri);
2231+ }
2232+#else
2233+ public Extension () {
2234+ GLib.Object (name: _("Advertisement blocker"),
2235+ description: _("Block advertisements according to a filter list"),
2236+ version: "2.0",
2237+ authors: "Christian Dywan <christian@twotoasts.de>");
2238+ activate.connect (extension_activated);
2239+ open_preferences.connect (extension_preferences);
2240+ }
2241+
2242+ void extension_preferences () {
2243+ open_dialog (null);
2244+ }
2245+
2246+ void open_dialog (string? uri) {
2247+ var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"),
2248+ null,
2249+#if !HAVE_GTK3
2250+ Gtk.DialogFlags.NO_SEPARATOR |
2251+#endif
2252+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
2253+ Gtk.STOCK_HELP, Gtk.ResponseType.HELP,
2254+ Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
2255+#if HAVE_GTK3
2256+ dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button");
2257+#endif
2258+ dialog.set_icon_name (Gtk.STOCK_PROPERTIES);
2259+ dialog.set_response_sensitive (Gtk.ResponseType.HELP, false);
2260+
2261+ var hbox = new Gtk.HBox (false, 0);
2262+ (dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12);
2263+ var vbox = new Gtk.VBox (false, 0);
2264+ hbox.pack_start (vbox, true, true, 4);
2265+ var button = new Gtk.Label (null);
2266+ string description = """
2267+ Type the address of a preconfigured filter list in the text entry
2268+ and click "Add" to add it to the list.
2269+ You can find more lists at %s %s.
2270+ """.printf (
2271+ "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>",
2272+ "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>");
2273+ button.activate_link.connect ((uri)=>{
2274+ var browser = Midori.Browser.get_for_widget (button);
2275+ var view = browser.add_uri (uri);
2276+ browser.tab = view;
2277+ return true;
2278+ });
2279+ button.set_markup (description);
2280+ button.set_line_wrap (true);
2281+ vbox.pack_start (button, false, false, 4);
2282+
2283+ var entry = new Gtk.Entry ();
2284+ if (uri != null)
2285+ entry.set_text (uri);
2286+ vbox.pack_start (entry, false, false, 4);
2287+
2288+ var liststore = new Gtk.ListStore (1, typeof (Subscription));
2289+ var treeview = new Gtk.TreeView.with_model (liststore);
2290+ treeview.set_headers_visible (false);
2291+ var column = new Gtk.TreeViewColumn ();
2292+ var renderer_toggle = new Gtk.CellRendererToggle ();
2293+ column.pack_start (renderer_toggle, false);
2294+ column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => {
2295+ Subscription sub;
2296+ liststore.get (iter, 0, out sub);
2297+ renderer.set ("active", sub.active,
2298+ "sensitive", !sub.uri.has_suffix ("custom.list"));
2299+ });
2300+ renderer_toggle.toggled.connect ((path) => {
2301+ Gtk.TreeIter iter;
2302+ if (liststore.get_iter_from_string (out iter, path)) {
2303+ Subscription sub;
2304+ liststore.get (iter, 0, out sub);
2305+ sub.active = !sub.active;
2306+ }
2307+ });
2308+ treeview.append_column (column);
2309+
2310+ column = new Gtk.TreeViewColumn ();
2311+ var renderer_text = new Gtk.CellRendererText ();
2312+ column.pack_start (renderer_text, false);
2313+ renderer_text.set ("editable", true);
2314+ // TODO: renderer_text.edited.connect
2315+ column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => {
2316+ Subscription sub;
2317+ liststore.get (iter, 0, out sub);
2318+ renderer.set ("text", sub.uri);
2319+ });
2320+ treeview.append_column (column);
2321+
2322+ var scrolled = new Gtk.ScrolledWindow (null, null);
2323+ scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
2324+ scrolled.add (treeview);
2325+ vbox.pack_start (scrolled);
2326+
2327+ foreach (Subscription sub in config)
2328+ liststore.insert_with_values (null, 0, 0, sub);
2329+ // TODO: row-inserted row-changed row-deleted
2330+ // TODO vbox with add/ edit/ remove/ down/ up
2331+
2332+ dialog.get_content_area ().show_all ();
2333+
2334+ dialog.response.connect ((response)=>{ dialog.destroy (); });
2335+ dialog.show ();
2336+ }
2337+
2338+ void extension_activated (Midori.App app) {
2339+ init ();
2340+ foreach (var browser in app.get_browsers ())
2341+ browser_added (browser);
2342+ app.add_browser.connect (browser_added);
2343+ }
2344+
2345+ void browser_added (Midori.Browser browser) {
2346+ foreach (var tab in browser.get_tabs ())
2347+ tab_added (tab);
2348+ browser.add_tab.connect (tab_added);
2349+ }
2350+
2351+ void tab_added (Midori.View view) {
2352+ view.web_view.resource_request_starting.connect (resource_requested);
2353+ view.web_view.navigation_policy_decision_requested.connect (navigation_requested);
2354+ view.notify["load-status"].connect ((pspec) => {
2355+ if (view.load_status == Midori.LoadStatus.FINISHED)
2356+ inject_css (view, view.uri);
2357+ });
2358+ view.context_menu.connect (context_menu);
2359+ }
2360+
2361+ void context_menu (WebKit.HitTestResult hit_test_result, Midori.ContextAction menu) {
2362+ string label, uri;
2363+ if ((hit_test_result.context & WebKit.HitTestResultContext.IMAGE) != 0) {
2364+ label = _("Bl_ock image");
2365+ uri = hit_test_result.image_uri;
2366+ } else if ((hit_test_result.context & WebKit.HitTestResultContext.LINK) != 0) {
2367+ label = _("Bl_ock link");
2368+ uri = hit_test_result.link_uri;
2369+ } else
2370+ return;
2371+ var action = new Gtk.Action ("BlockElement", label, null, null);
2372+ action.activate.connect ((action) => {
2373+ edit_rule_dialog (uri);
2374+ });
2375+ menu.add (action);
2376+ }
2377+
2378+ void edit_rule_dialog (string uri) {
2379+ var dialog = new Gtk.Dialog.with_buttons (_("Edit rule"),
2380+ null,
2381+#if !HAVE_GTK3
2382+ Gtk.DialogFlags.NO_SEPARATOR |
2383+#endif
2384+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
2385+ Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
2386+ Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT);
2387+ dialog.set_icon_name (Gtk.STOCK_ADD);
2388+ dialog.resizable = false;
2389+
2390+ var hbox = new Gtk.HBox (false, 8);
2391+ var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
2392+ hbox.border_width = 5;
2393+ var label = new Gtk.Label.with_mnemonic (_("_Rule:"));
2394+ sizegroup.add_widget (label);
2395+ hbox.pack_start (label, false, false, 0);
2396+ (dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0);
2397+
2398+ var entry = new Gtk.Entry ();
2399+ sizegroup.add_widget (entry);
2400+ entry.activates_default = true;
2401+ entry.set_text (uri);
2402+ hbox.pack_start (entry, true, true, 0);
2403+
2404+ dialog.get_content_area ().show_all ();
2405+
2406+ dialog.set_default_response (Gtk.ResponseType.ACCEPT);
2407+ if (dialog.run () != Gtk.ResponseType.ACCEPT)
2408+ return;
2409+
2410+ string new_rule = entry.get_text ();
2411+ dialog.destroy ();
2412+ config.add_custom_rule (new_rule);
2413+ }
2414+
2415+
2416+ void resource_requested (WebKit.WebView web_view, WebKit.WebFrame frame,
2417+ WebKit.WebResource resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) {
2418+
2419+ if (request_handled (web_view.uri, request.uri))
2420+ request.set_uri ("about:blank");
2421+ }
2422+
2423+ bool navigation_requested (WebKit.WebFrame frame, WebKit.NetworkRequest request,
2424+ WebKit.WebNavigationAction action, WebKit.WebPolicyDecision decision) {
2425+
2426+ string uri = request.uri;
2427+ if (uri.has_prefix ("abp:")) {
2428+ uri = uri.replace ("abp://", "abp:");
2429+ if (uri.has_prefix ("abp:subscribe?location=")) {
2430+ /* abp://subscripe?location=http://example.com&title=foo */
2431+ string[] parts = uri.substring (23, -1).split ("&", 2);
2432+ decision.ignore ();
2433+ open_dialog (parts[0]);
2434+ return true;
2435+ }
2436+ }
2437+ return false;
2438+ }
2439+
2440+ void inject_css (Midori.View view, string page_uri) {
2441+ /* Don't block ads on internal pages */
2442+ if (!Midori.URI.is_http (page_uri))
2443+ return;
2444+ string domain = Midori.URI.parse_hostname (page_uri, null);
2445+ string[] subdomains = domain.split (".");
2446+ if (subdomains == null)
2447+ return;
2448+ int cnt = subdomains.length - 1;
2449+ var subdomain = new StringBuilder (subdomains[cnt]);
2450+ subdomain.prepend_c ('.');
2451+ cnt--;
2452+ var code = new StringBuilder ();
2453+ bool debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
2454+ string hider_css;
2455+
2456+ /* Hide elements that were blocked, otherwise we will get "broken image" icon */
2457+ cache.foreach ((key, val) => {
2458+ if (val == Adblock.Directive.BLOCK)
2459+ code.append ("img[src*=\"%s\"] , iframe[src*=\"%s\"] , ".printf (key, key));
2460+ });
2461+ if (debug_element)
2462+ hider_css = " { background-color: red; border: 4px solid green; }";
2463+ else
2464+ hider_css = " { visiblility: hidden; width: 0; height: 0; }";
2465+
2466+ code.truncate (code.len -3);
2467+ code.append (hider_css);
2468+ if (debug_element)
2469+ stdout.printf ("hider css: %s\n", code.str);
2470+ view.inject_stylesheet (code.str);
2471+
2472+ code.erase ();
2473+ int blockscnt = 0;
2474+ while (cnt >= 0) {
2475+ subdomain.prepend (subdomains[cnt]);
2476+ string? style = null;
2477+ foreach (Subscription sub in config) {
2478+ foreach (var feature in sub) {
2479+ if (feature is Adblock.Element) {
2480+ style = (feature as Adblock.Element).lookup (subdomain.str);
2481+ break;
2482+ }
2483+ }
2484+ }
2485+ if (style != null) {
2486+ code.append (style);
2487+ code.append_c (',');
2488+ blockscnt++;
2489+ }
2490+ subdomain.prepend_c ('.');
2491+ cnt--;
2492+ }
2493+ if (blockscnt == 0)
2494+ return;
2495+ code.truncate (code.len - 1);
2496+
2497+ if (debug_element)
2498+ hider_css = " { background-color: red !important; border: 4px solid green !important; }";
2499+ else
2500+ hider_css = " { display: none !important }";
2501+
2502+ code.append (hider_css);
2503+ view.inject_stylesheet (code.str);
2504+ if (debug_element)
2505+ stdout.printf ("css: %s\n", code.str);
2506+ }
2507+#endif
2508+
2509+ internal void init () {
2510+ debug ("Adblock2");
2511+
2512+ string config_dir = Midori.Paths.get_extension_config_dir ("libadblock.so");
2513+ config = new Config (config_dir);
2514+ reload_rules ();
2515+ }
2516+
2517+ void reload_rules () {
2518+ cache = new HashTable<string, Directive?> (str_hash, str_equal);
2519+ foreach (Subscription sub in config) {
2520+ try {
2521+ sub.parse ();
2522+ } catch (GLib.Error error) {
2523+ warning ("Error parsing %s: %s", sub.uri, error.message);
2524+ }
2525+ }
2526+ }
2527+
2528+ bool request_handled (string page_uri, string request_uri) {
2529+ /* Always allow the main page */
2530+ if (request_uri == page_uri)
2531+ return false;
2532+
2533+ Directive? directive = cache.lookup (request_uri);
2534+ if (directive == null) {
2535+ foreach (Subscription sub in config) {
2536+ directive = sub.get_directive (request_uri, page_uri);
2537+ if (directive != null)
2538+ break;
2539+ }
2540+ if (directive == null)
2541+ directive = Directive.ALLOW;
2542+ cache.insert (request_uri, directive);
2543+ }
2544+ return directive == Directive.BLOCK;
2545+ }
2546+ }
2547+
2548+ static void debug (string format, ...) {
2549+ bool debug_match = "adblock:match" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
2550+ if (!debug_match)
2551+ return;
2552+
2553+ var args = va_list ();
2554+ stdout.vprintf (format + "\n", args);
2555+ }
2556+
2557+ internal static string? fixup_regex (string prefix, string? src) {
2558+ if (src == null)
2559+ return null;
2560+
2561+ var fixed = new StringBuilder ();
2562+ fixed.append(prefix);
2563+
2564+ uint i = 0;
2565+ if (src[0] == '*')
2566+ i++;
2567+ uint l = src.length;
2568+ while (i < l) {
2569+ char c = src[i];
2570+ switch (c) {
2571+ case '*':
2572+ fixed.append (".*"); break;
2573+ case '|':
2574+ case '^':
2575+ case '+':
2576+ break;
2577+ case '?':
2578+ case '[':
2579+ case ']':
2580+ fixed.append_printf ("\\%c", c); break;
2581+ default:
2582+ fixed.append_c (c); break;
2583+ }
2584+ i++;
2585+ }
2586+ return fixed.str;
2587+ }
2588+}
2589+
2590+#if HAVE_WEBKIT2
2591+Adblock.Extension? filter;
2592+public static void webkit_web_extension_initialize (WebKit.WebExtension web_extension) {
2593+ filter = new Adblock.Extension (web_extension);
2594+}
2595+#else
2596+public Midori.Extension extension_init () {
2597+ return new Adblock.Extension ();
2598+}
2599+#endif
2600+
2601+#if !HAVE_WEBKIT2
2602+struct TestCaseLine {
2603+ public string line;
2604+ public string fixed;
2605+}
2606+
2607+const TestCaseLine[] lines = {
2608+ { null, null },
2609+ { "!", "!" },
2610+ { "@@", "@@" },
2611+ { "##", "##" },
2612+ { "[", "\\[" },
2613+ { "+advert/", "advert/" },
2614+ { "*foo", "foo" },
2615+ { "f*oo", "f.*oo" },
2616+ { "?foo", "\\?foo" },
2617+ { "foo?", "foo\\?" },
2618+ { ".*foo/bar", "..*foo/bar" },
2619+ { "http://bla.blub/*", "http://bla.blub/.*" },
2620+ { "bag?r[]=*cpa", "bag\\?r\\[\\]=.*cpa" },
2621+ { "(facebookLike,", "(facebookLike," },
2622+};
2623+
2624+void test_adblock_fixup_regexp () {
2625+ foreach (var line in lines) {
2626+ Katze.assert_str_equal (line.line, Adblock.fixup_regex ("", line.line), line.fixed);
2627+ }
2628+}
2629+
2630+struct TestCasePattern {
2631+ public string uri;
2632+ public Adblock.Directive directive;
2633+}
2634+
2635+const TestCasePattern[] patterns = {
2636+ { "http://www.engadget.com/_uac/adpage.html", Adblock.Directive.BLOCK },
2637+ { "http://test.dom/test?var=1", Adblock.Directive.BLOCK },
2638+ { "http://ads.foo.bar/teddy", Adblock.Directive.BLOCK },
2639+ { "http://ads.fuu.bar/teddy", Adblock.Directive.ALLOW },
2640+ { "https://ads.bogus.name/blub", Adblock.Directive.BLOCK },
2641+ // FIXME { "http://ads.bla.blub/kitty", Adblock.Directive.BLOCK },
2642+ // FIXME { "http://ads.blub.boing/soda", Adblock.Directive.BLOCK },
2643+ { "http://ads.foo.boing/beer", Adblock.Directive.ALLOW },
2644+ { "https://testsub.engine.adct.ru/test?id=1", Adblock.Directive.BLOCK },
2645+ { "http://test.ltd/addyn/test/test?var=adtech;&var2=1", Adblock.Directive.BLOCK },
2646+ { "http://add.doubleclick.net/pfadx/aaaa.mtvi", Adblock.Directive.BLOCK },
2647+ { "http://add.doubleclick.net/pfadx/aaaa.mtv", Adblock.Directive.ALLOW },
2648+ { "http://objects.tremormedia.com/embed/xml/list.xml?r=", Adblock.Directive.BLOCK },
2649+ { "http://qq.videostrip.c/sub/admatcherclient.php", Adblock.Directive.ALLOW },
2650+ { "http://qq.videostrip.com/sub/admatcherclient.php", Adblock.Directive.BLOCK },
2651+ { "http://qq.videostrip.com/sub/admatcherclient.php", Adblock.Directive.BLOCK },
2652+ { "http://br.gcl.ru/cgi-bin/br/test", Adblock.Directive.BLOCK },
2653+ { "https://bugs.webkit.org/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&long_desc_type=substring&long_desc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&keywords_type=allwords&keywords=&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailassigned_to1=1&emailtype1=substring&email1=&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailtype2=substring&email2=&bugidtype=include&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&query_based_on=gtkport&field0-0-0=keywords&type0-0-0=anywordssubstr&value0-0-0=Gtk%20Cairo%20soup&field0-0-1=short_desc&type0-0-1=anywordssubstr&value0-0-1=Gtk%20Cairo%20soup%20autoconf%20automake%20autotool&field0-0-2=component&type0-0-2=equals&value0-0-2=WebKit%20Gtk", Adblock.Directive.ALLOW },
2654+ { "http://www.engadget.com/2009/09/24/google-hits-android-rom-modder-with-a-cease-and-desist-letter/", Adblock.Directive.ALLOW },
2655+ { "http://karibik-invest.com/es/bienes_raices/search.php?sqT=19&sqN=&sqMp=&sqL=0&qR=1&sqMb=&searchMode=1&action=B%FAsqueda", Adblock.Directive.ALLOW },
2656+ { "http://google.com", Adblock.Directive.ALLOW },
2657+};
2658+
2659+string pretty_directive (Adblock.Directive? directive) {
2660+ if (directive == null)
2661+ return "none";
2662+ return directive.to_string ();
2663+}
2664+
2665+void test_adblock_pattern () {
2666+ string path = Midori.Paths.get_res_filename ("adblock.list");
2667+ string uri;
2668+ try {
2669+ uri = Filename.to_uri (path, null);
2670+ } catch (Error error) {
2671+ GLib.error (error.message);
2672+ }
2673+ var sub = new Adblock.Subscription (uri);
2674+ try {
2675+ sub.parse ();
2676+ } catch (Error error) {
2677+ GLib.error (error.message);
2678+ }
2679+ foreach (var pattern in patterns) {
2680+ Adblock.Directive? directive = sub.get_directive (pattern.uri, "");
2681+ if (directive == null)
2682+ directive = Adblock.Directive.ALLOW;
2683+ if (directive != pattern.directive) {
2684+ error ("%s expected for %s but got %s",
2685+ pretty_directive (pattern.directive), pattern.uri, pretty_directive (directive));
2686+ }
2687+ }
2688+}
2689+
2690+struct TestUpdateExample {
2691+ public string content;
2692+ public bool result;
2693+}
2694+
2695+ const TestUpdateExample[] examples = {
2696+ { "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true },
2697+ { "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true },
2698+ { "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false },
2699+ { "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false },
2700+ { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true },
2701+ { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true },
2702+ { "[Adblock]\n ! dummy, i dont have any dates\n", false },
2703+ };
2704+
2705+void test_subscription_update () {
2706+ string uri;
2707+ FileIOStream iostream;
2708+ File file;
2709+ try {
2710+ file = File.new_tmp ("midori_adblock_update_test_XXXXXX", out iostream);
2711+ uri = file.get_uri ();
2712+ } catch (Error error) {
2713+ GLib.error (error.message);
2714+ }
2715+ var sub = new Adblock.Subscription (uri);
2716+ var updater = new Adblock.Updater ();
2717+ sub.add_feature (updater);
2718+
2719+ foreach (var example in examples) {
2720+ try {
2721+ file.replace_contents (example.content.data, null, false, FileCreateFlags.NONE, null);
2722+ updater.last_mod_meta = null;
2723+ updater.expires_meta = null;
2724+ sub.parse ();
2725+ } catch (Error error) {
2726+ GLib.error (error.message);
2727+ }
2728+ if (example.result == true)
2729+ assert (updater.needs_updating());
2730+ else
2731+ assert (!updater.needs_updating());
2732+ }
2733+}
2734+
2735+public void extension_test () {
2736+ Test.add_func ("/extensions/adblock2/parse", test_adblock_fixup_regexp);
2737+ Test.add_func ("/extensions/adblock2/pattern", test_adblock_pattern);
2738+ Test.add_func ("/extensions/adblock2/update", test_subscription_update);
2739+}
2740+#endif
2741+
2742
2743=== added file 'extensions/adblock/filter.vala'
2744--- extensions/adblock/filter.vala 1970-01-01 00:00:00 +0000
2745+++ extensions/adblock/filter.vala 2014-02-20 20:31:17 +0000
2746@@ -0,0 +1,52 @@
2747+/*
2748+ Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
2749+ Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
2750+
2751+ This library is free software; you can redistribute it and/or
2752+ modify it under the terms of the GNU Lesser General Public
2753+ License as published by the Free Software Foundation; either
2754+ version 2.1 of the License, or (at your option) any later version.
2755+
2756+ See the file COPYING for the full license text.
2757+*/
2758+
2759+namespace Adblock {
2760+ public abstract class Filter : Feature {
2761+ Options optslist;
2762+ protected HashTable<string, Regex?> rules;
2763+
2764+ public virtual void insert (string sig, Regex regex) {
2765+ rules.insert (sig, regex);
2766+ }
2767+
2768+ public virtual Regex? lookup (string sig) {
2769+ return rules.lookup (sig);
2770+ }
2771+
2772+ public virtual uint size () {
2773+ return rules.size ();
2774+ }
2775+
2776+ protected Filter (Options options) {
2777+ optslist = options;
2778+ clear ();
2779+ }
2780+
2781+ public override void clear () {
2782+ rules = new HashTable<string, Regex> (str_hash, str_equal);
2783+ }
2784+
2785+ protected bool check_rule (Regex regex, string pattern, string request_uri, string page_uri) throws Error {
2786+ if (!regex.match_full (request_uri))
2787+ return false;
2788+
2789+ var opts = optslist.lookup (pattern);
2790+ if (opts != null && Regex.match_simple (",third-party", opts,
2791+ RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))
2792+ if (page_uri != null && regex.match_full (page_uri))
2793+ return false;
2794+ debug ("blocked by pattern regexp=%s -- %s", regex.get_pattern (), request_uri);
2795+ return true;
2796+ }
2797+ }
2798+}
2799
2800=== added file 'extensions/adblock/keys.vala'
2801--- extensions/adblock/keys.vala 1970-01-01 00:00:00 +0000
2802+++ extensions/adblock/keys.vala 2014-02-20 20:31:17 +0000
2803@@ -0,0 +1,47 @@
2804+/*
2805+ Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
2806+ Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
2807+
2808+ This library is free software; you can redistribute it and/or
2809+ modify it under the terms of the GNU Lesser General Public
2810+ License as published by the Free Software Foundation; either
2811+ version 2.1 of the License, or (at your option) any later version.
2812+
2813+ See the file COPYING for the full license text.
2814+*/
2815+
2816+namespace Adblock {
2817+ public class Keys : Filter {
2818+ List<Regex> blacklist;
2819+
2820+ public Keys (Options options) {
2821+ base (options);
2822+ }
2823+
2824+ public override void clear () {
2825+ base.clear ();
2826+ blacklist = new List<Regex> ();
2827+ }
2828+
2829+ public override Directive? match (string request_uri, string page_uri) throws Error {
2830+ string? uri = fixup_regex ("", request_uri);
2831+ if (uri == null)
2832+ return null;
2833+
2834+ int signature_size = 8;
2835+ int pos, l = uri.length;
2836+ for (pos = l - signature_size; pos >= 0; pos--) {
2837+ string signature = uri.offset (pos).ndup (signature_size);
2838+ var regex = rules.lookup (signature);
2839+ if (regex == null || blacklist.find (regex) != null)
2840+ continue;
2841+
2842+ if (check_rule (regex, uri, request_uri, page_uri))
2843+ return Directive.BLOCK;
2844+ blacklist.prepend (regex);
2845+ }
2846+
2847+ return null;
2848+ }
2849+ }
2850+}
2851
2852=== added file 'extensions/adblock/options.vala'
2853--- extensions/adblock/options.vala 1970-01-01 00:00:00 +0000
2854+++ extensions/adblock/options.vala 2014-02-20 20:31:17 +0000
2855@@ -0,0 +1,32 @@
2856+/*
2857+ Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
2858+
2859+ This library is free software; you can redistribute it and/or
2860+ modify it under the terms of the GNU Lesser General Public
2861+ License as published by the Free Software Foundation; either
2862+ version 2.1 of the License, or (at your option) any later version.
2863+
2864+ See the file COPYING for the full license text.
2865+*/
2866+
2867+namespace Adblock {
2868+ public class Options : GLib.Object {
2869+ HashTable<string, string?> optslist;
2870+
2871+ public Options () {
2872+ clear ();
2873+ }
2874+
2875+ public void insert (string sig, string? opts) {
2876+ optslist.insert (sig, opts);
2877+ }
2878+
2879+ public string? lookup (string sig) {
2880+ return optslist.lookup (sig);
2881+ }
2882+
2883+ public void clear () {
2884+ optslist = new HashTable<string, string?> (str_hash, str_equal);
2885+ }
2886+ }
2887+}
2888
2889=== added file 'extensions/adblock/pattern.vala'
2890--- extensions/adblock/pattern.vala 1970-01-01 00:00:00 +0000
2891+++ extensions/adblock/pattern.vala 2014-02-20 20:31:17 +0000
2892@@ -0,0 +1,26 @@
2893+/*
2894+ Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
2895+ Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
2896+
2897+ This library is free software; you can redistribute it and/or
2898+ modify it under the terms of the GNU Lesser General Public
2899+ License as published by the Free Software Foundation; either
2900+ version 2.1 of the License, or (at your option) any later version.
2901+
2902+ See the file COPYING for the full license text.
2903+*/
2904+
2905+namespace Adblock {
2906+ public class Pattern : Filter {
2907+ public Pattern (Options options) {
2908+ base (options);
2909+ }
2910+
2911+ public override Directive? match (string request_uri, string page_uri) throws Error {
2912+ foreach (var patt in rules.get_keys ())
2913+ if (check_rule (rules.lookup (patt), patt, request_uri, page_uri))
2914+ return Directive.BLOCK;
2915+ return null;
2916+ }
2917+ }
2918+}
2919
2920=== added file 'extensions/adblock/subscriptions.vala'
2921--- extensions/adblock/subscriptions.vala 1970-01-01 00:00:00 +0000
2922+++ extensions/adblock/subscriptions.vala 2014-02-20 20:31:17 +0000
2923@@ -0,0 +1,328 @@
2924+/*
2925+ Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
2926+ Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
2927+
2928+ This library is free software; you can redistribute it and/or
2929+ modify it under the terms of the GNU Lesser General Public
2930+ License as published by the Free Software Foundation; either
2931+ version 2.1 of the License, or (at your option) any later version.
2932+
2933+ See the file COPYING for the full license text.
2934+*/
2935+
2936+namespace Adblock {
2937+ public abstract class Feature : GLib.Object {
2938+ public virtual bool header (string key, string value) {
2939+ return false;
2940+ }
2941+ public virtual Directive? match (string request_uri, string page_uri) throws Error {
2942+ return null;
2943+ }
2944+ public virtual void clear () {
2945+ }
2946+ }
2947+
2948+ public class Subscription : GLib.Object {
2949+ public string? path;
2950+ public string uri { get; set; default = null; }
2951+ public bool active { get; set; default = true; }
2952+ List<Feature> features;
2953+ public Pattern pattern;
2954+ public Keys keys;
2955+ public Options optslist;
2956+ public Whitelist whitelist;
2957+ public Element element;
2958+ WebKit.Download? download;
2959+
2960+ public Subscription (string uri) {
2961+ this.uri = uri;
2962+
2963+ this.optslist = new Options ();
2964+ this.whitelist = new Whitelist (optslist);
2965+ add_feature (this.whitelist);
2966+ this.keys = new Keys (optslist);
2967+ add_feature (this.keys);
2968+ this.pattern = new Pattern (optslist);
2969+ add_feature (this.pattern);
2970+ this.element = new Element ();
2971+ add_feature (this.element);
2972+ clear ();
2973+ }
2974+
2975+ public void add_feature (Feature feature) {
2976+ features.append (feature);
2977+ size++;
2978+ }
2979+
2980+ /* foreach support */
2981+ public new Feature? get (uint index) {
2982+ return features.nth_data (index);
2983+ }
2984+ public uint size { get; private set; }
2985+
2986+ public void clear () {
2987+ foreach (var feature in features)
2988+ feature.clear ();
2989+ optslist.clear ();
2990+ }
2991+
2992+ internal void parse_line (string? line) throws Error {
2993+ if (line.has_prefix ("@@")) {
2994+ if (line.contains("$") && line.contains ("domain"))
2995+ return;
2996+ if (line.has_prefix ("@@||"))
2997+ add_url_pattern ("^", "whitelist", line.offset (4));
2998+ else if (line.has_prefix ("@@|"))
2999+ add_url_pattern ("^", "whitelist", line.offset (3));
3000+ else
3001+ add_url_pattern ("", "whitelist", line.offset (2));
3002+ return;
3003+ }
3004+ /* TODO: [include] [exclude] */
3005+ if (line[0] == '[')
3006+ return;
3007+
3008+ /* CSS block hider */
3009+ if (line.has_prefix ("##")) {
3010+ /* TODO */
3011+ return;
3012+ }
3013+ if (line[0] == '#')
3014+ return;
3015+
3016+ /* Per domain CSS hider rule */
3017+ if ("##" in line) {
3018+ frame_add_private (line, "##");
3019+ return;
3020+ }
3021+ if ("#" in line) {
3022+ frame_add_private (line, "#");
3023+ return;
3024+ }
3025+
3026+ /* URL blocker rule */
3027+ if (line.has_prefix ("|")) {
3028+ /* TODO: handle options and domains excludes */
3029+ if (line.contains("$"))
3030+ return;
3031+
3032+ if (line.has_prefix ("||"))
3033+ add_url_pattern ("", "fulluri", line.offset (2));
3034+ else
3035+ add_url_pattern ("^", "fulluri", line.offset (1));
3036+ return /* add_url_pattern */;
3037+ }
3038+
3039+ add_url_pattern ("", "uri", line);
3040+ return /* add_url_pattern */;
3041+ }
3042+
3043+ void frame_add_private (string line, string sep) {
3044+ string[] data = line.split (sep, 2);
3045+ if (!(data[1] != null && data[1] != "")
3046+ || data[1].chr (-1, '\'') != null
3047+ || (data[1].chr (-1, ':') != null
3048+ && !Regex.match_simple (".*\\[.*:.*\\].*", data[1],
3049+ RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))) {
3050+ return;
3051+ }
3052+
3053+ if (data[0].chr (-1, ',') != null) {
3054+ string[] domains = data[0].split (",", -1);
3055+
3056+ foreach (string domain in domains) {
3057+ /* Ignore Firefox-specific option */
3058+ if (domain == "~pregecko2")
3059+ continue;
3060+ /* FIXME: ~ should negate match */
3061+ if (domain[0] == '~')
3062+ domain = domain.substring (1, -1);
3063+ update_css_hash (domain.strip (), data[1]);
3064+ }
3065+ }
3066+ else {
3067+ update_css_hash (data[0], data[1]);
3068+ }
3069+ }
3070+
3071+ void update_css_hash (string domain, string value) {
3072+ string? olddata = element.lookup (domain);
3073+ if (olddata != null) {
3074+ string newdata = olddata + " , " + value;
3075+ element.insert (domain, newdata);
3076+ } else {
3077+ element.insert (domain, value);
3078+ }
3079+ }
3080+
3081+ void add_url_pattern (string prefix, string type, string line) throws Error {
3082+ string[]? data = line.split ("$", 2);
3083+ if (data == null || data[0] == null)
3084+ return;
3085+
3086+ string patt, opts;
3087+ patt = data[0];
3088+ opts = type;
3089+
3090+ if (data[1] != null)
3091+ opts = type + "," + data[1];
3092+
3093+ if (Regex.match_simple ("subdocument", opts,
3094+ RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))
3095+ return;
3096+
3097+ string format_patt = fixup_regex (prefix, patt);
3098+ debug ("got: %s opts %s", format_patt, opts);
3099+ compile_regexp (format_patt, opts);
3100+ /* return format_patt */
3101+ }
3102+
3103+ bool compile_regexp (string? patt, string opts) throws Error {
3104+ if (patt == null)
3105+ return false;
3106+ try {
3107+ var regex = new Regex (patt, RegexCompileFlags.OPTIMIZE, RegexMatchFlags.NOTEMPTY);
3108+ /* is pattern is already a regex? */
3109+ if (Regex.match_simple ("^/.*[\\^\\$\\*].*/$", patt,
3110+ RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY)
3111+ || opts != null && opts.contains ("whitelist")) {
3112+ debug ("patt: %s", patt);
3113+ if (opts.contains ("whitelist"))
3114+ this.whitelist.insert (patt, regex);
3115+ else
3116+ this.pattern.insert (patt, regex);
3117+ this.optslist.insert (patt, opts);
3118+ return false;
3119+ } else { /* nope, no regex */
3120+ int pos = 0, len;
3121+ int signature_size = 8;
3122+ string sig;
3123+ len = patt.length;
3124+
3125+ /* chop up pattern into substrings for faster matching */
3126+ for (pos = len - signature_size; pos>=0; pos--)
3127+ {
3128+ sig = patt.offset (pos).ndup (signature_size);
3129+ /* we don't have a * nor \\, does not look like regex, save chunk as "key" */
3130+ if (!Regex.match_simple ("[\\*]", sig, RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY) && keys.lookup (sig) == null) {
3131+ this.keys.insert (sig, regex);
3132+ this.optslist.insert (sig, opts);
3133+ } else {
3134+ /* starts with * or \\ - save as regex */
3135+ if ((sig.has_prefix ("*") || sig.has_prefix("\\")) && this.pattern.lookup (sig) == null) {
3136+ this.pattern.insert (sig, regex);
3137+ this.optslist.insert (sig, opts);
3138+ }
3139+ }
3140+ }
3141+ }
3142+ return false;
3143+ }
3144+ catch (Error error) {
3145+ warning ("Adblock compile regexp: %s", error.message);
3146+ return true;
3147+ }
3148+ }
3149+
3150+ public void parse_header (string header) throws Error {
3151+ /* Headers come in two forms
3152+ ! Foo: Bar
3153+ ! Some freeform text
3154+ */
3155+ string key = header;
3156+ string value = "";
3157+ if (header.contains (":")) {
3158+ string[] parts = header.split (":", 2);
3159+ if (parts[0] != null) {
3160+ key = parts[0].substring (2, -1);
3161+ value = parts[1].substring (1, -1);
3162+ }
3163+ }
3164+ debug ("Header '%s' says '%s'", key, value);
3165+ foreach (var feature in features) {
3166+ if (feature.header (key, value))
3167+ break;
3168+ }
3169+ }
3170+
3171+ void download_status (ParamSpec pspec) {
3172+ if (download.get_status () != WebKit.DownloadStatus.FINISHED)
3173+ return;
3174+
3175+ download = null;
3176+ try {
3177+ parse ();
3178+ } catch (Error error) {
3179+ warning ("Error parsing %s: %s", uri, error.message);
3180+ }
3181+ }
3182+
3183+ public void parse () throws Error
3184+ {
3185+ if (!active)
3186+ return;
3187+
3188+ debug ("Parsing %s (%s)", uri, path);
3189+
3190+ clear ();
3191+
3192+ if (uri.has_prefix ("file://"))
3193+ path = Filename.from_uri (uri);
3194+ else {
3195+ string cache_dir = GLib.Path.build_filename (GLib.Environment.get_home_dir (), ".cache", "midori", "adblock");
3196+ Midori.Paths.mkdir_with_parents (cache_dir);
3197+ string filename = Checksum.compute_for_string (ChecksumType.MD5, this.uri, -1);
3198+ path = GLib.Path.build_filename (cache_dir, filename);
3199+ }
3200+
3201+ File filter_file = File.new_for_path (path);
3202+ DataInputStream stream;
3203+ try {
3204+ stream = new DataInputStream (filter_file.read ());
3205+ } catch (IOError.NOT_FOUND exist_error) {
3206+#if HAVE_WEBKIT2
3207+ /* TODO */
3208+#else
3209+ if (download != null)
3210+ return;
3211+
3212+ download = new WebKit.Download (new WebKit.NetworkRequest (uri));
3213+ download.destination_uri = Filename.to_uri (path, null);
3214+ download.notify["status"].connect (download_status);
3215+ debug ("Fetching %s to %s now", uri, download.destination_uri);
3216+ download.start ();
3217+#endif
3218+ return;
3219+ }
3220+
3221+ string? line;
3222+ while ((line = stream.read_line (null)) != null) {
3223+ if (line == null)
3224+ continue;
3225+ string chomped = line.chomp ();
3226+ if (chomped == "")
3227+ continue;
3228+ if (line[0] == '!')
3229+ parse_header (chomped);
3230+ else
3231+ parse_line (chomped);
3232+ }
3233+ }
3234+
3235+ public Directive? get_directive (string request_uri, string page_uri) {
3236+ try {
3237+ foreach (var feature in features) {
3238+ Directive? directive = feature.match (request_uri, page_uri);
3239+ if (directive != null) {
3240+ debug ("%s gave %s for %s (%s)\n",
3241+ feature.get_type ().name (), directive.to_string (), request_uri, page_uri);
3242+ return directive;
3243+ }
3244+ }
3245+ } catch (Error error) {
3246+ warning ("Adblock match error: %s\n", error.message);
3247+ }
3248+ return null;
3249+ }
3250+ }
3251+}
3252
3253=== added file 'extensions/adblock/updater.vala'
3254--- extensions/adblock/updater.vala 1970-01-01 00:00:00 +0000
3255+++ extensions/adblock/updater.vala 2014-02-20 20:31:17 +0000
3256@@ -0,0 +1,139 @@
3257+/*
3258+ Copyright (C) 2014 Paweł Forysiuk <tuxator@o2.pl>
3259+ Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
3260+
3261+ This library is free software; you can redistribute it and/or
3262+ modify it under the terms of the GNU Lesser General Public
3263+ License as published by the Free Software Foundation; either
3264+ version 2.1 of the License, or (at your option) any later version.
3265+
3266+ See the file COPYING for the full license text.
3267+*/
3268+
3269+namespace Adblock {
3270+ public class Updater : Feature {
3271+ public string expires_meta { get; set; default = null; }
3272+ public string last_mod_meta { get; set; default = null; }
3273+ public int64 update_tstamp { get; set; default = 0; }
3274+ public int64 last_mod_tstamp { get; set; default = 0; }
3275+ public int64 last_check_tstamp { get; set; default = 0; }
3276+
3277+ public Updater () {
3278+ }
3279+
3280+ public override bool header (string key, string value) {
3281+ if (key.has_prefix ("Last mod") || key == "Updated") {
3282+ last_mod_meta = value;
3283+ return true;
3284+ } else if (key == "Expires") {
3285+ /* ! Expires: 5 days (update frequency) */
3286+ expires_meta = value;
3287+ return true;
3288+ } else if (key.has_prefix ("! This list expires after")) {
3289+ /* ! This list expires after 14 days */
3290+ expires_meta = key.substring (26, -1);
3291+ return true;
3292+ }
3293+ return false;
3294+ }
3295+
3296+ int get_month_from_string (string? month) {
3297+ if (month == null)
3298+ return 0;
3299+
3300+ string[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
3301+ for (int i = 0; i<= months.length; i++)
3302+ {
3303+ if (month.has_prefix (months[i]))
3304+ return i+1;
3305+ }
3306+ return 0;
3307+ }
3308+
3309+ public bool needs_updating () {
3310+ DateTime now = new DateTime.now_local ();
3311+ DateTime expire_date = null;
3312+ DateTime last_mod_date = null;
3313+ string? last_mod = last_mod_meta;
3314+ string? expires = expires_meta;
3315+
3316+ /* We have "last modification" metadata */
3317+ if (last_mod != null) {
3318+ int h = 0, min = 0, d, m, y;
3319+ /* Date in a form of: 20.08.2012 12:34 */
3320+ if (last_mod.contains (".") || last_mod.contains("-")) {
3321+ string[] parts = last_mod.split (" ", 2);
3322+ string[] date_parts;
3323+ string split_char = " ";
3324+
3325+ /* contains time part ? */
3326+ if (parts[1] != "" && parts[1].contains (":")) {
3327+ string[] time_parts = parts[1].split (":", 2);
3328+ h = int.parse(time_parts[0]);
3329+ min = int.parse(time_parts[1]);
3330+ }
3331+
3332+ /* check if dot or dash was used as a delimiter */
3333+ if (parts[0].contains ("."))
3334+ split_char = ".";
3335+ else if (parts[0].contains ("-"))
3336+ split_char = "-";
3337+
3338+ date_parts = parts[0].split (split_char, 3);
3339+ m = int.parse(date_parts[1]);
3340+ if (date_parts[2].length == 4) {
3341+ y = int.parse(date_parts[2]);
3342+ d = int.parse(date_parts[0]);
3343+ } else {
3344+ y = int.parse(date_parts[0]);
3345+ d = int.parse(date_parts[2]);
3346+ }
3347+ } else { /* Date in a form of: 20 Mar 2012 12:34 */
3348+ string[] parts = last_mod.split (" ", 4);
3349+ /* contains time part ? */
3350+ if (parts[3] != null && parts[3].contains (":")) {
3351+ string[] time_parts = parts[3].split (":", 2);
3352+ h = int.parse(time_parts[0]);
3353+ min = int.parse(time_parts[1]);
3354+ }
3355+
3356+ m = get_month_from_string (parts[1]);
3357+ if (parts[2].length == 4) {
3358+ y = int.parse(parts[2]);
3359+ d = int.parse(parts[0]);
3360+ } else {
3361+ y = int.parse(parts[0]);
3362+ d = int.parse(parts[2]);
3363+ }
3364+ }
3365+
3366+ last_mod_date = new DateTime.local (y, m, d, h, min, 0.0);
3367+ }
3368+
3369+ if (last_mod_date == null)
3370+ last_mod_date = now;
3371+
3372+ /* We have "expires" metadata */
3373+ if (expires != null) {
3374+ if (expires.contains ("days")) {
3375+ string[] parts = expires.split (" ");
3376+ expire_date = last_mod_date.add_days (int.parse (parts[0]));
3377+ } else if (expires.contains ("hours")) {
3378+ string[] parts = expires.split (" ");
3379+ expire_date = last_mod_date.add_hours (int.parse (parts[0]));
3380+ }
3381+ } else {
3382+ /* No expire metadata found, assume x days */
3383+ int days_to_expire = 7;
3384+ expire_date = last_mod_date.add_days (days_to_expire);
3385+ }
3386+
3387+ last_mod_tstamp = last_mod_date.to_unix ();
3388+ last_check_tstamp = now.to_unix ();
3389+ update_tstamp = expire_date.to_unix ();
3390+
3391+ /* Check if we are past expire date */
3392+ return now.compare (expire_date) == 1;
3393+ }
3394+ }
3395+}
3396
3397=== added file 'extensions/adblock/whitelist.vala'
3398--- extensions/adblock/whitelist.vala 1970-01-01 00:00:00 +0000
3399+++ extensions/adblock/whitelist.vala 2014-02-20 20:31:17 +0000
3400@@ -0,0 +1,30 @@
3401+/*
3402+ Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
3403+ Copyright (C) 2014 Paweł Forysiuk <tuxator@o2.pl>
3404+
3405+ This library is free software; you can redistribute it and/or
3406+ modify it under the terms of the GNU Lesser General Public
3407+ License as published by the Free Software Foundation; either
3408+ version 2.1 of the License, or (at your option) any later version.
3409+
3410+ See the file COPYING for the full license text.
3411+*/
3412+
3413+namespace Adblock {
3414+ public class Whitelist : Filter {
3415+ public Whitelist (Options options) {
3416+ base (options);
3417+ }
3418+
3419+ public override Directive? match (string request_uri, string page_uri) throws Error {
3420+ foreach (var white in rules.get_keys ()) {
3421+ var regex = rules.lookup (white);
3422+ if (!regex.match_full (request_uri))
3423+ return null;
3424+ if (Regex.match_simple (regex.get_pattern (), request_uri, RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY))
3425+ return Directive.ALLOW;
3426+ }
3427+ return null;
3428+ }
3429+ }
3430+}
3431
3432=== modified file 'katze/midori-paths.vala'
3433--- katze/midori-paths.vala 2013-11-22 18:14:18 +0000
3434+++ katze/midori-paths.vala 2014-02-20 20:31:17 +0000
3435@@ -143,6 +143,7 @@
3436 #if HAVE_WEBKIT2
3437 WebKit.WebContext.get_default ().set_disk_cache_directory (
3438 Path.build_path (Path.DIR_SEPARATOR_S, cache_dir, "web"));
3439+
3440 var cookie_manager = WebKit.WebContext.get_default ().get_cookie_manager ();
3441 cookie_manager.set_persistent_storage (Path.build_filename (config, "cookies.db"),
3442 WebKit.CookiePersistentStorage.SQLITE);
3443
3444=== modified file 'midori/midori-app.c'
3445--- midori/midori-app.c 2014-01-12 18:14:15 +0000
3446+++ midori/midori-app.c 2014-02-20 20:31:17 +0000
3447@@ -1256,7 +1256,7 @@
3448 midori_debug (const gchar* token)
3449 {
3450 static const gchar* debug_token = NULL;
3451- const gchar* debug_tokens = "adblock:match adblock:time startup headers body referer cookies paths hsts unarmed bookmarks mouse app ";
3452+ const gchar* debug_tokens = "adblock:match adblock:time adblock:element startup headers body referer cookies paths hsts unarmed bookmarks mouse app ";
3453 if (debug_token == NULL)
3454 {
3455 gchar* found_token;
3456
3457=== modified file 'midori/midori.vapi'
3458--- midori/midori.vapi 2014-01-26 11:33:02 +0000
3459+++ midori/midori.vapi 2014-02-20 20:31:17 +0000
3460@@ -135,6 +135,7 @@
3461 public void install_boolean (string name, bool default_value);
3462 public void install_integer (string name, int default_value);
3463 public void install_string (string name, string default_value);
3464+ public void install_string_list (string name, string[]? default_value);
3465
3466 public bool get_boolean (string name);
3467 public int get_integer (string name);
3468
3469=== modified file 'midori/webkit2gtk-3.0.vapi'
3470--- midori/webkit2gtk-3.0.vapi 2013-04-08 18:35:37 +0000
3471+++ midori/webkit2gtk-3.0.vapi 2014-02-20 20:31:17 +0000
3472@@ -552,6 +552,22 @@
3473 [CCode (has_construct_function = false)]
3474 protected WebViewBase ();
3475 }
3476+ [CCode (cheader_filename = "webkit2/webkit-web-extension.h", type_id = "webkit_web_extension_get_type ()")]
3477+ public class WebExtension : GLib.Object {
3478+ [CCode (has_construct_function = false)]
3479+ protected WebExtension ();
3480+ public WebKit.WebPage get_page (uint64 page_id);
3481+ public signal void page_created (WebKit.WebPage web_page);
3482+ }
3483+ [CCode (cheader_filename = "webkit2/webkit-web-extension.h", type_id = "webkit_web_extension_get_type ()")]
3484+ public class WebPage : GLib.Object {
3485+ [CCode (has_construct_function = false)]
3486+ protected WebPage ();
3487+ public uint64 get_id ();
3488+ public string uri { get; }
3489+ public signal bool send_request (WebKit.URIRequest request, WebKit.URIResponse? redirected_response);
3490+ public signal void document_loaded ();
3491+ }
3492 [CCode (cheader_filename = "webkit2/webkit2.h", type_id = "webkit_window_properties_get_type ()")]
3493 public class WindowProperties : GLib.Object {
3494 [CCode (has_construct_function = false)]
3495
3496=== modified file 'po/POTFILES.in'
3497--- po/POTFILES.in 2013-12-13 17:34:40 +0000
3498+++ po/POTFILES.in 2014-02-20 20:31:17 +0000
3499@@ -36,7 +36,6 @@
3500 katze/katze-preferences.c
3501 katze/midori-uri.vala
3502 katze/midori-paths.vala
3503-extensions/adblock.c
3504 extensions/addons.c
3505 extensions/colorful-tabs.c
3506 extensions/cookie-manager/cookie-manager.c
3507@@ -92,3 +91,13 @@
3508 extensions/tabby.vala
3509 extensions/flummi.vala
3510 extensions/notes.vala
3511+extensions/adblock/extension.vala
3512+extensions/adblock/filter.vala
3513+extensions/adblock/keys.vala
3514+extensions/adblock/pattern.vala
3515+extensions/adblock/options.vala
3516+extensions/adblock/whitelist.vala
3517+extensions/adblock/subscriptions.vala
3518+extensions/adblock/config.vala
3519+extensions/adblock/updater.vala
3520+extensions/adblock/element.vala

Subscribers

People subscribed via source and target branches

to all changes: