Merge lp:~midori/midori/addingDefaultsAndFilters into lp:midori

Proposed by Cris Dywan
Status: Merged
Approved by: Cris Dywan
Approved revision: 6610
Merged at revision: 6594
Proposed branch: lp:~midori/midori/addingDefaultsAndFilters
Merge into: lp:midori
Diff against target: 1852 lines (+1095/-298)
14 files modified
data/adblock.list (+1/-0)
data/adblock/blocked.svg (+46/-0)
data/adblock/disabled.svg (+46/-0)
data/adblock/enabled.svg (+80/-0)
extensions/adblock/config.vala (+78/-39)
extensions/adblock/extension.vala (+421/-210)
extensions/adblock/subscriptions.vala (+48/-10)
extensions/adblock/updater.vala (+47/-31)
extensions/adblock/widgets.vala (+310/-0)
katze/midori-paths.vala (+12/-4)
midori/midori-app.c (+1/-1)
midori/midori-browser.c (+1/-1)
midori/midori-download.vala (+3/-2)
po/POTFILES.in (+1/-0)
To merge this branch: bzr merge lp:~midori/midori/addingDefaultsAndFilters
Reviewer Review Type Date Requested Status
Cris Dywan Approve
Review via email: mp+208255@code.launchpad.net

Commit message

Add filters and defaults

Description of the change

TODO:
- Statusbar icon [x] Debug Matching
- Edit/ view custom rules
- Check checksum hash of the subscription after downloading
- Open local files in editor
- Ability to disable adblock for the current domain
- 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.
6582. By Cris Dywan

Only truncate filters string if there's a trailing ;

6583. By Cris Dywan

Get browser from app instead of get_for_widget

We don't actually get a window we could use as a reference

6584. By Paweł Forysiuk

Don't show scarry warning if 'disabled' setting is not found

6585. By Paweł Forysiuk

Don't bother downloading local files

6586. By Cris Dywan

Refactor Updater properties and show update in treeview

6587. By Cris Dywan

Handling presets within Config and add first unit tests

6588. By Cris Dywan

Move enabled switch into Config and add unit tests

6589. By Cris Dywan

Unit test parsing of subscriptions in Config

6590. By Paweł Forysiuk

Toggle displaying of hidden elements from adblock icon

6591. By Paweł Forysiuk

Display current adblock state in a tooltip

6592. By Paweł Forysiuk

Fix building with older vala version

6593. By Paweł Forysiuk

Don't reuse menu item variables

6594. By Cris Dywan

Unit test that saving and loading gives back the same results

6595. By Cris Dywan

Add adblock:parse for patt/ got messages

6596. By Cris Dywan

Add fallback to empty string to debug variable checks

6597. By Cris Dywan

Unit test init, add, remove and request on the whole Extension

6598. By Cris Dywan

Correct request_handled test case and custom rule test

6599. By Cris Dywan

Check header parts more carefully in Subscription.parse_header

6600. By Cris Dywan

Implement a notion of validity in subscriptions if there're no date headers

6601. By Cris Dywan

Fix and unit test writing of disabled URLs with dashes

6602. By Cris Dywan

Consider a subscription valid if anything at all gets picked up

There's no requirement to have any update or expiry header.

6603. By Cris Dywan

Merge lp:midori

6604. By Paweł Forysiuk

Extract widgets into separate file

6605. By Cris Dywan

preset_filename needs to fallback to build folder

6606. By Paweł Forysiuk

Parse subscription uris with a helper function

6607. By Paweł Forysiuk

Separate subscription parsing helper from subscription manager

6608. By Paweł Forysiuk

Split preparing hider css into helper functions

6609. By Paweł Forysiuk

Return if there is no blocked element to hide

6610. By Paweł Forysiuk

Split getting domains for uri into a separate function

Revision history for this message
Cris Dywan (kalikiana) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'data/adblock'
=== modified file 'data/adblock.list'
--- data/adblock.list 2014-02-17 20:43:55 +0000
+++ data/adblock.list 2014-03-10 10:27:25 +0000
@@ -7,6 +7,7 @@
7! Licence: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt7! Licence: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
8! Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>8! Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
9!9!
10! Some freeform text:
10! Yadayada http://example.com/ e-mail (somebody@example.com).11! Yadayada http://example.com/ e-mail (somebody@example.com).
11!12!
12!-----Spam eggs--------!13!-----Spam eggs--------!
1314
=== added file 'data/adblock/blocked.svg'
--- data/adblock/blocked.svg 1970-01-01 00:00:00 +0000
+++ data/adblock/blocked.svg 2014-03-10 10:27:25 +0000
@@ -0,0 +1,46 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 version="1.1"
11 width="16"
12 height="16"
13 id="svg7384">
14 <title
15 id="title9167">Gnome Symbolic Icon Theme</title>
16 <defs
17 id="defs10" />
18 <metadata
19 id="metadata90">
20 <rdf:RDF>
21 <cc:Work
22 rdf:about="">
23 <dc:format>image/svg+xml</dc:format>
24 <dc:type
25 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
26 <dc:title>Gnome Symbolic Icon Theme</dc:title>
27 </cc:Work>
28 </rdf:RDF>
29 </metadata>
30 <g
31 transform="translate(-111.61542,-601.4038)"
32 id="layer12">
33 <path
34 d="m 228.01,607.02 c -3.86,0 -7.0073,3.166 -7.0073,7.0103 0,3.8443 3.1472,6.9787 7.0073,6.9787 3.86,0 7.0073,-3.1344 7.0073,-6.9787 0,-3.8443 -3.1472,-7.0103 -7.0073,-7.0103 z m 0,2.021 c 2.7769,0 5.0097,2.2237 5.0097,4.9893 0,2.7656 -2.2328,4.9577 -5.0097,4.9577 -2.777,0 -5.0097,-2.1921 -5.0097,-4.9577 0,-2.7656 2.2328,-4.9893 5.0097,-4.9893 z"
35 id="path4222"
36 style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
37 <path
38 d="m 231.41,609.44 -8,8 1.4062,1.4062 8,-8 -1.4062,-1.4062 z"
39 id="path4992"
40 style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
41 </g>
42 <path
43 d="m 7.96875,0.6875 c -2.8420838,0 -2.8771973,1.9949742 -6.625,2.3125 0,0 -0.98752557,2.5363552 0.34375,6 0.8399195,2.18526 4.3935259,5.981297 6.1875,6 l 0.25,0 C 9.9189736,14.9813 13.472581,11.18526 14.3125,9 15.643775,5.5363552 14.65625,3 14.65625,3 10.908448,2.6824742 10.873333,0.6875 8.03125,0.6875 l -0.0625,0 z m -0.625,2 c 0.1719617,-0.02159 0.3665606,0 0.625,0 l 0.0625,0 c 1.0337572,0 1.1746055,0.1513488 2.03125,0.625 0.133299,0.073703 0.361318,0.1808693 0.53125,0.28125 L 4.0625,9.25 C 3.8118632,8.860346 3.6127069,8.4931804 3.53125,8.28125 2.8541774,6.5196771 2.8806279,5.4183698 3,4.65625 4.2800363,4.3579974 5.3166759,3.6557627 5.9375,3.3125 6.5799833,2.9572616 6.8278648,2.7522692 7.34375,2.6875 z m 5.21875,1.84375 c 0.146121,0.046432 0.28409,0.089255 0.4375,0.125 0.119372,0.7621199 0.145822,1.863427 -0.53125,3.625 -0.223946,0.5826518 -1.210812,2.079776 -2.28125,3.15625 -0.535219,0.538237 -1.0931989,0.993886 -1.53125,1.28125 -0.2903916,0.190498 -0.5086712,0.283423 -0.5625,0.3125 l -0.15625,0 C 7.9117131,13.007739 7.6583409,12.925123 7.34375,12.71875 6.905699,12.431386 6.3477189,11.975737 5.8125,11.4375 5.6307383,11.254713 5.4548666,11.044257 5.28125,10.84375 l 7.28125,-6.3125 z"
44 id="path3016-2"
45 style="color:#000000;fill:#ffa500;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
46</svg>
047
=== added file 'data/adblock/disabled.svg'
--- data/adblock/disabled.svg 1970-01-01 00:00:00 +0000
+++ data/adblock/disabled.svg 2014-03-10 10:27:25 +0000
@@ -0,0 +1,46 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 version="1.1"
11 width="16"
12 height="16"
13 id="svg7384">
14 <title
15 id="title9167">Gnome Symbolic Icon Theme</title>
16 <defs
17 id="defs10" />
18 <metadata
19 id="metadata90">
20 <rdf:RDF>
21 <cc:Work
22 rdf:about="">
23 <dc:format>image/svg+xml</dc:format>
24 <dc:type
25 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
26 <dc:title>Gnome Symbolic Icon Theme</dc:title>
27 </cc:Work>
28 </rdf:RDF>
29 </metadata>
30 <g
31 transform="translate(-111.61542,-601.4038)"
32 id="layer12">
33 <path
34 d="m 228.01,607.02 c -3.86,0 -7.0073,3.166 -7.0073,7.0103 0,3.8443 3.1472,6.9787 7.0073,6.9787 3.86,0 7.0073,-3.1344 7.0073,-6.9787 0,-3.8443 -3.1472,-7.0103 -7.0073,-7.0103 z m 0,2.021 c 2.7769,0 5.0097,2.2237 5.0097,4.9893 0,2.7656 -2.2328,4.9577 -5.0097,4.9577 -2.777,0 -5.0097,-2.1921 -5.0097,-4.9577 0,-2.7656 2.2328,-4.9893 5.0097,-4.9893 z"
35 id="path4222"
36 style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
37 <path
38 d="m 231.41,609.44 -8,8 1.4062,1.4062 8,-8 -1.4062,-1.4062 z"
39 id="path4992"
40 style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
41 </g>
42 <path
43 d="m 7.96875,0.6875 c -2.8420838,0 -2.8771973,1.9949742 -6.625,2.3125 0,0 -0.98752557,2.5363552 0.34375,6 0.8399195,2.18526 4.3935259,5.981297 6.1875,6 l 0.25,0 C 9.9189736,14.9813 13.472581,11.18526 14.3125,9 15.643775,5.5363552 14.65625,3 14.65625,3 10.908448,2.6824742 10.873333,0.6875 8.03125,0.6875 l -0.0625,0 z m -0.625,2 c 0.1719617,-0.02159 0.3665606,0 0.625,0 l 0.0625,0 c 1.0337572,0 1.1746055,0.1513488 2.03125,0.625 0.133299,0.073703 0.361318,0.1808693 0.53125,0.28125 L 4.0625,9.25 C 3.8118632,8.860346 3.6127069,8.4931804 3.53125,8.28125 2.8541774,6.5196771 2.8806279,5.4183698 3,4.65625 4.2800363,4.3579974 5.3166759,3.6557627 5.9375,3.3125 6.5799833,2.9572616 6.8278648,2.7522692 7.34375,2.6875 z m 5.21875,1.84375 c 0.146121,0.046432 0.28409,0.089255 0.4375,0.125 0.119372,0.7621199 0.145822,1.863427 -0.53125,3.625 -0.223946,0.5826518 -1.210812,2.079776 -2.28125,3.15625 -0.535219,0.538237 -1.0931989,0.993886 -1.53125,1.28125 -0.2903916,0.190498 -0.5086712,0.283423 -0.5625,0.3125 l -0.15625,0 C 7.9117131,13.007739 7.6583409,12.925123 7.34375,12.71875 6.905699,12.431386 6.3477189,11.975737 5.8125,11.4375 5.6307383,11.254713 5.4548666,11.044257 5.28125,10.84375 l 7.28125,-6.3125 z"
44 id="path3016-2"
45 style="color:#000000;fill:#cc0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
46</svg>
047
=== added file 'data/adblock/enabled.svg'
--- data/adblock/enabled.svg 1970-01-01 00:00:00 +0000
+++ data/adblock/enabled.svg 2014-03-10 10:27:25 +0000
@@ -0,0 +1,80 @@
1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!-- Created with Inkscape (http://www.inkscape.org/) -->
3
4<svg
5 xmlns:dc="http://purl.org/dc/elements/1.1/"
6 xmlns:cc="http://creativecommons.org/ns#"
7 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
8 xmlns:svg="http://www.w3.org/2000/svg"
9 xmlns="http://www.w3.org/2000/svg"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 version="1.1"
13 width="16"
14 height="16"
15 id="svg7384"
16 inkscape:version="0.48.4 r9939"
17 sodipodi:docname="adblock-enabled.svg">
18 <sodipodi:namedview
19 pagecolor="#ffffff"
20 bordercolor="#666666"
21 borderopacity="1"
22 objecttolerance="10"
23 gridtolerance="10"
24 guidetolerance="10"
25 inkscape:pageopacity="0"
26 inkscape:pageshadow="2"
27 inkscape:window-width="1920"
28 inkscape:window-height="1026"
29 id="namedview10"
30 showgrid="false"
31 inkscape:zoom="14.75"
32 inkscape:cx="8"
33 inkscape:cy="8"
34 inkscape:window-x="0"
35 inkscape:window-y="32"
36 inkscape:window-maximized="1"
37 inkscape:current-layer="svg7384" />
38 <title
39 id="title9167">Gnome Symbolic Icon Theme</title>
40 <defs
41 id="defs10" />
42 <metadata
43 id="metadata90">
44 <rdf:RDF>
45 <cc:Work
46 rdf:about="">
47 <dc:format>image/svg+xml</dc:format>
48 <dc:type
49 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
50 <dc:title>Gnome Symbolic Icon Theme</dc:title>
51 </cc:Work>
52 </rdf:RDF>
53 </metadata>
54 <g
55 transform="translate(-111.61542,-601.4038)"
56 id="layer12">
57 <path
58 d="m 228.01,607.02 c -3.86,0 -7.0073,3.166 -7.0073,7.0103 0,3.8443 3.1472,6.9787 7.0073,6.9787 3.86,0 7.0073,-3.1344 7.0073,-6.9787 0,-3.8443 -3.1472,-7.0103 -7.0073,-7.0103 z m 0,2.021 c 2.7769,0 5.0097,2.2237 5.0097,4.9893 0,2.7656 -2.2328,4.9577 -5.0097,4.9577 -2.777,0 -5.0097,-2.1921 -5.0097,-4.9577 0,-2.7656 2.2328,-4.9893 5.0097,-4.9893 z"
59 id="path4222"
60 style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
61 <path
62 d="m 231.41,609.44 -8,8 1.4062,1.4062 8,-8 -1.4062,-1.4062 z"
63 id="path4992"
64 style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
65 </g>
66 <path
67 d="m 7.96875,0.6875 c -2.8420838,0 -2.8771973,1.9949742 -6.625,2.3125 0,0 -0.98752557,2.5363552 0.34375,6 0.8399195,2.18526 4.3935259,5.981297 6.1875,6 l 0.25,0 C 9.9189736,14.9813 13.472581,11.18526 14.3125,9 15.643775,5.5363552 14.65625,3 14.65625,3 10.908448,2.6824742 10.873333,0.6875 8.03125,0.6875 l -0.0625,0 z m -0.625,2 c 0.1719617,-0.02159 0.3665606,0 0.625,0 l 0.0625,0 c 1.0337572,0 1.1746055,0.1513488 2.03125,0.625 0.133299,0.073703 0.361318,0.1808693 0.53125,0.28125 L 4.0625,9.25 C 3.8118632,8.860346 3.6127069,8.4931804 3.53125,8.28125 2.8541774,6.5196771 2.8806279,5.4183698 3,4.65625 4.2800363,4.3579974 5.3166759,3.6557627 5.9375,3.3125 6.5799833,2.9572616 6.8278648,2.7522692 7.34375,2.6875 z m 5.21875,1.84375 c 0.146121,0.046432 0.28409,0.089255 0.4375,0.125 0.119372,0.7621199 0.145822,1.863427 -0.53125,3.625 -0.223946,0.5826518 -1.210812,2.079776 -2.28125,3.15625 -0.535219,0.538237 -1.0931989,0.993886 -1.53125,1.28125 -0.2903916,0.190498 -0.5086712,0.283423 -0.5625,0.3125 l -0.15625,0 C 7.9117131,13.007739 7.6583409,12.925123 7.34375,12.71875 6.905699,12.431386 6.3477189,11.975737 5.8125,11.4375 5.6307383,11.254713 5.4548666,11.044257 5.28125,10.84375 l 7.28125,-6.3125 z"
68 id="path3016-2"
69 style="color:#000000;fill:#4e9a06;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
70 <path
71 style="fill:#000000;fill-opacity:0"
72 d="M 3.8914706,8.8841635 C 3.4103263,8.1221753 3.0583616,6.8749414 3.003735,5.7383534 2.9786317,5.2160416 2.9904131,4.8722447 3.036357,4.7863976 3.0800139,4.7048241 3.2418563,4.6177843 3.4482218,4.5648944 3.8914458,4.4512995 4.5265161,4.1442388 5.8961843,3.3812883 L 7.0127075,2.7593478 7.7697676,2.7353155 C 8.6627475,2.7069685 9.052772,2.7983017 9.8276868,3.2172249 10.1089,3.3692502 10.375652,3.52161 10.420468,3.5558023 10.491273,3.609822 8.8213493,5.090833 4.1912034,9.0803713 4.0886836,9.1687069 4.0587803,9.149132 3.8914706,8.8841635 l 0,0 z"
73 id="path2987"
74 inkscape:connector-curvature="0" />
75 <path
76 style="fill:#000000;fill-opacity:0"
77 d="M 7.5676774,12.795393 C 7.0256619,12.469009 6.4290755,11.979639 5.8305085,11.370422 5.33023,10.861243 5.3242581,10.851054 5.4600194,10.738315 5.5359114,10.675293 7.1641368,9.2661017 9.0782981,7.6067797 11.684369,5.3476641 12.594872,4.5953246 12.703024,4.6117025 c 0.291451,0.044135 0.329784,0.1886522 0.290923,1.0967721 -0.07204,1.6834953 -0.589725,2.9526655 -1.866622,4.5762714 -0.495094,0.629525 -1.366327,1.526375 -1.8854314,1.94087 C 8.8466967,12.541173 8.091686,13.030508 8,13.030508 c -0.023031,0 -0.2175765,-0.105802 -0.4323226,-0.235115 z"
78 id="path2989"
79 inkscape:connector-curvature="0" />
80</svg>
081
=== modified file 'extensions/adblock/config.vala'
--- extensions/adblock/config.vala 2014-02-20 18:27:01 +0000
+++ extensions/adblock/config.vala 2014-03-10 10:27:25 +0000
@@ -12,27 +12,26 @@
12namespace Adblock {12namespace Adblock {
13 public class Config : GLib.Object {13 public class Config : GLib.Object {
14 List<Subscription> subscriptions;14 List<Subscription> subscriptions;
15 string? path;15 public string? path { get; private set; }
16 KeyFile keyfile;16 KeyFile keyfile;
17 Subscription? custom;17 bool should_save;
18 public bool enabled { get; set; }
1819
19 public Config (string? path) {20 public Config (string? path, string? presets) {
21 should_save = false;
20 subscriptions = new GLib.List<Subscription> ();22 subscriptions = new GLib.List<Subscription> ();
2123 enabled = true;
22 this.path = path;24 this.path = path;
23 if (path == null)25 size = 0;
26 load_file (path);
27 load_file (presets);
28 should_save = true;
29 }
30
31 void load_file (string? filename) {
32 if (filename == null)
24 return;33 return;
2534
26 string custom_list = GLib.Path.build_filename (path, "custom.list");
27 try {
28 custom = new Subscription (Filename.to_uri (custom_list, null));
29 subscriptions.append (custom);
30 } catch (Error error) {
31 custom = null;
32 warning ("Failed to add custom list %s: %s", custom_list, error.message);
33 }
34
35 string filename = GLib.Path.build_filename (path, "config");
36 keyfile = new GLib.KeyFile ();35 keyfile = new GLib.KeyFile ();
37 try {36 try {
38 keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);37 keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);
@@ -40,68 +39,108 @@
40 foreach (string filter in filters) {39 foreach (string filter in filters) {
41 bool active = false;40 bool active = false;
42 string uri = filter;41 string uri = filter;
43 if (filter.has_prefix ("http-"))42 if (filter.has_prefix ("http-/"))
44 uri = "http:" + filter.substring (6);43 uri = "http:" + filter.substring (5);
45 else if (filter.has_prefix ("file-"))44 else if (filter.has_prefix ("file-/"))
46 uri = "file:" + filter.substring (6);45 uri = "file:" + filter.substring (5);
47 else if (filter.has_prefix ("https-"))46 else if (filter.has_prefix ("http-:"))
48 uri = "https:" + filter.substring (7);47 uri = "https" + filter.substring (5);
49 else48 else
50 active = true;49 active = true;
51 Subscription sub = new Subscription (uri);50 Subscription sub = new Subscription (uri);
52 sub.active = active;51 sub.active = active;
53 sub.add_feature (new Updater ());52 sub.add_feature (new Updater ());
54 sub.notify["active"].connect (active_changed);53 add (sub);
55 subscriptions.append (sub);
56 }54 }
55 enabled = !keyfile.get_boolean ("settings", "disabled");
56 } catch (KeyFileError.KEY_NOT_FOUND key_error) {
57 /* It's no error if a key is missing */
58 } catch (KeyFileError.GROUP_NOT_FOUND group_error) {
59 /* It's no error if a group is missing */
57 } catch (FileError.NOENT exist_error) {60 } catch (FileError.NOENT exist_error) {
58 /* It's no error if no config file exists */61 /* It's no error if no config file exists */
59 } catch (GLib.Error settings_error) {62 } catch (GLib.Error settings_error) {
60 warning ("Error reading settings from %s: %s\n", filename, settings_error.message);63 warning ("Error reading settings from %s: %s\n", filename, settings_error.message);
61 }64 }
6265
63 size = subscriptions.length ();66 notify["enabled"].connect (enabled_changed);
67 }
68
69 void enabled_changed (ParamSpec pspec) {
70 keyfile.set_boolean ("settings", "disabled", !enabled);
71 save ();
64 }72 }
6573
66 void active_changed (Object subscription, ParamSpec pspec) {74 void active_changed (Object subscription, ParamSpec pspec) {
75 update_filters ();
76 }
77
78 void update_filters () {
67 var filters = new StringBuilder ();79 var filters = new StringBuilder ();
68 foreach (var sub in subscriptions) {80 foreach (var sub in subscriptions) {
69 if (sub == custom)81 if (!sub.mutable)
70 continue;82 continue;
71 if (sub.uri.has_prefix ("http:") && !sub.active)83 if (sub.uri.has_prefix ("http:") && !sub.active)
72 filters.append ("http-" + sub.uri.substring (4));84 filters.append ("http-" + sub.uri.substring (4));
73 else if (sub.uri.has_prefix ("file:") && !sub.active)85 else if (sub.uri.has_prefix ("file:") && !sub.active)
74 filters.append ("file-" + sub.uri.substring (4));86 filters.append ("file-" + sub.uri.substring (5));
75 else if (sub.uri.has_prefix ("https:") && !sub.active)87 else if (sub.uri.has_prefix ("https:") && !sub.active)
76 filters.append ("https-" + sub.uri.substring (5));88 filters.append ("http-" + sub.uri.substring (5));
77 else89 else
78 filters.append (sub.uri);90 filters.append (sub.uri);
79 filters.append_c (';');91 filters.append_c (';');
80 }92 }
8193
82 string[] list = (filters.str.slice (0, -1)).split (";");94 if (filters.str.has_suffix (";"))
95 filters.truncate (filters.len - 1);
96 string[] list = filters.str.split (";");
83 keyfile.set_string_list ("settings", "filters", list);97 keyfile.set_string_list ("settings", "filters", list);
98
99 save ();
100 }
101
102
103 public void save () {
84 try {104 try {
85 string filename = GLib.Path.build_filename (path, "config");105 FileUtils.set_contents (path, keyfile.to_data ());
86 FileUtils.set_contents (filename, keyfile.to_data ());
87 } catch (Error error) {106 } catch (Error error) {
88 warning ("Failed to save settings: %s", error.message);107 warning ("Failed to save settings: %s", error.message);
89 }108 }
90 }109 }
91110
92 public void add_custom_rule (string rule) {
93 try {
94 var file = File.new_for_uri (custom.uri);
95 file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data);
96 } catch (Error error) {
97 warning ("Failed to add custom rule: %s", error.message);
98 }
99 }
100
101 /* foreach support */111 /* foreach support */
102 public new Subscription? get (uint index) {112 public new Subscription? get (uint index) {
103 return subscriptions.nth_data (index);113 return subscriptions.nth_data (index);
104 }114 }
105 public uint size { get; private set; }115 public uint size { get; private set; }
116
117 bool contains (Subscription subscription) {
118 foreach (var sub in subscriptions)
119 if (sub.uri == subscription.uri)
120 return true;
121 return false;
122 }
123
124 public bool add (Subscription sub) {
125 if (contains (sub))
126 return false;
127
128 sub.notify["active"].connect (active_changed);
129 subscriptions.append (sub);
130 size++;
131 if (should_save)
132 update_filters ();
133 return true;
134 }
135
136 public void remove (Subscription sub) {
137 if (!contains (sub))
138 return;
139
140 subscriptions.remove (sub);
141 sub.notify["active"].disconnect (active_changed);
142 update_filters ();
143 size--;
144 }
106 }145 }
107}146}
108147
=== modified file 'extensions/adblock/extension.vala'
--- extensions/adblock/extension.vala 2014-02-24 00:06:18 +0000
+++ extensions/adblock/extension.vala 2014-03-10 10:27:25 +0000
@@ -16,9 +16,42 @@
16 BLOCK16 BLOCK
17 }17 }
1818
19 public enum State {
20 ENABLED,
21 DISABLED,
22 BLOCKED
23 }
24
25 public string? parse_subscription_uri (string? uri) {
26 if (uri == null)
27 return null;
28
29 if (uri.has_prefix ("http") || uri.has_prefix ("abp") || uri.has_prefix ("file"))
30 {
31 string sub_uri = uri;
32 if (uri.has_prefix ("abp:")) {
33 uri.replace ("abp://", "abp:");
34 if (uri.has_prefix ("abp:subscribe?location=")) {
35 /* abp://subscripe?location=http://example.com&title=foo */
36 string[] parts = uri.substring (23, -1).split ("&", 2);
37 sub_uri = parts[0];
38 }
39 }
40
41 string decoded_uri = Soup.URI.decode (sub_uri);
42 return decoded_uri;
43 }
44 return null;
45 }
46
19 public class Extension : Midori.Extension {47 public class Extension : Midori.Extension {
20 Config config;48 internal Config config;
21 HashTable<string, Directive?> cache;49 internal Subscription custom;
50 internal HashTable<string, Directive?> cache;
51 internal StatusIcon status_icon;
52 internal SubscriptionManager manager;
53 internal State state;
54 internal bool debug_element;
2255
23#if HAVE_WEBKIT256#if HAVE_WEBKIT2
24 public Extension (WebKit.WebExtension web_extension) {57 public Extension (WebKit.WebExtension web_extension) {
@@ -44,99 +77,7 @@
44 }77 }
4578
46 void extension_preferences () {79 void extension_preferences () {
47 open_dialog (null);80 manager.add_subscription (null);
48 }
49
50 void open_dialog (string? uri) {
51 var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"),
52 null,
53#if !HAVE_GTK3
54 Gtk.DialogFlags.NO_SEPARATOR |
55#endif
56 Gtk.DialogFlags.DESTROY_WITH_PARENT,
57 Gtk.STOCK_HELP, Gtk.ResponseType.HELP,
58 Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
59#if HAVE_GTK3
60 dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button");
61#endif
62 dialog.set_icon_name (Gtk.STOCK_PROPERTIES);
63 dialog.set_response_sensitive (Gtk.ResponseType.HELP, false);
64
65 var hbox = new Gtk.HBox (false, 0);
66 (dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12);
67 var vbox = new Gtk.VBox (false, 0);
68 hbox.pack_start (vbox, true, true, 4);
69 var button = new Gtk.Label (null);
70 string description = """
71 Type the address of a preconfigured filter list in the text entry
72 and click "Add" to add it to the list.
73 You can find more lists at %s %s.
74 """.printf (
75 "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>",
76 "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>");
77 button.activate_link.connect ((uri)=>{
78 var browser = Midori.Browser.get_for_widget (button);
79 var view = browser.add_uri (uri);
80 browser.tab = view;
81 return true;
82 });
83 button.set_markup (description);
84 button.set_line_wrap (true);
85 vbox.pack_start (button, false, false, 4);
86
87 var entry = new Gtk.Entry ();
88 if (uri != null)
89 entry.set_text (uri);
90 vbox.pack_start (entry, false, false, 4);
91
92 var liststore = new Gtk.ListStore (1, typeof (Subscription));
93 var treeview = new Gtk.TreeView.with_model (liststore);
94 treeview.set_headers_visible (false);
95 var column = new Gtk.TreeViewColumn ();
96 var renderer_toggle = new Gtk.CellRendererToggle ();
97 column.pack_start (renderer_toggle, false);
98 column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => {
99 Subscription sub;
100 liststore.get (iter, 0, out sub);
101 renderer.set ("active", sub.active,
102 "sensitive", !sub.uri.has_suffix ("custom.list"));
103 });
104 renderer_toggle.toggled.connect ((path) => {
105 Gtk.TreeIter iter;
106 if (liststore.get_iter_from_string (out iter, path)) {
107 Subscription sub;
108 liststore.get (iter, 0, out sub);
109 sub.active = !sub.active;
110 }
111 });
112 treeview.append_column (column);
113
114 column = new Gtk.TreeViewColumn ();
115 var renderer_text = new Gtk.CellRendererText ();
116 column.pack_start (renderer_text, false);
117 renderer_text.set ("editable", true);
118 // TODO: renderer_text.edited.connect
119 column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => {
120 Subscription sub;
121 liststore.get (iter, 0, out sub);
122 renderer.set ("text", sub.uri);
123 });
124 treeview.append_column (column);
125
126 var scrolled = new Gtk.ScrolledWindow (null, null);
127 scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
128 scrolled.add (treeview);
129 vbox.pack_start (scrolled);
130
131 foreach (Subscription sub in config)
132 liststore.insert_with_values (null, 0, 0, sub);
133 // TODO: row-inserted row-changed row-deleted
134 // TODO vbox with add/ edit/ remove/ down/ up
135
136 dialog.get_content_area ().show_all ();
137
138 dialog.response.connect ((response)=>{ dialog.destroy (); });
139 dialog.show ();
140 }81 }
14182
142 void extension_activated (Midori.App app) {83 void extension_activated (Midori.App app) {
@@ -150,14 +91,24 @@
150 foreach (var tab in browser.get_tabs ())91 foreach (var tab in browser.get_tabs ())
151 tab_added (tab);92 tab_added (tab);
152 browser.add_tab.connect (tab_added);93 browser.add_tab.connect (tab_added);
94
95 var toggle_button = new StatusIcon.IconButton ();
96 toggle_button.set_status (config.enabled ? "enabled" : "disabled");
97 browser.statusbar.pack_start (toggle_button, false, false, 3);
98 toggle_button.show ();
99 toggle_button.clicked.connect (status_icon.icon_clicked);
100 status_icon.toggle_buttons.append (toggle_button);
153 }101 }
154102
103
155 void tab_added (Midori.View view) {104 void tab_added (Midori.View view) {
156 view.web_view.resource_request_starting.connect (resource_requested);105 view.web_view.resource_request_starting.connect (resource_requested);
157 view.web_view.navigation_policy_decision_requested.connect (navigation_requested);106 view.web_view.navigation_policy_decision_requested.connect (navigation_requested);
158 view.notify["load-status"].connect ((pspec) => {107 view.notify["load-status"].connect ((pspec) => {
159 if (view.load_status == Midori.LoadStatus.FINISHED)108 if (config.enabled) {
160 inject_css (view, view.uri);109 if (view.load_status == Midori.LoadStatus.FINISHED)
110 inject_css (view, view.uri);
111 }
161 });112 });
162 view.context_menu.connect (context_menu);113 view.context_menu.connect (context_menu);
163 }114 }
@@ -174,54 +125,31 @@
174 return;125 return;
175 var action = new Gtk.Action ("BlockElement", label, null, null);126 var action = new Gtk.Action ("BlockElement", label, null, null);
176 action.activate.connect ((action) => {127 action.activate.connect ((action) => {
177 edit_rule_dialog (uri);128 CustomRulesEditor custom_rules_editor = new CustomRulesEditor (custom);
129 custom_rules_editor.set_uri (uri);
130 custom_rules_editor.show();
178 });131 });
179 menu.add (action);132 menu.add (action);
180 }133 }
181134
182 void edit_rule_dialog (string uri) {135 Adblock.State adblock_get_state (Adblock.Directive directive)
183 var dialog = new Gtk.Dialog.with_buttons (_("Edit rule"),136 {
184 null,137 if (directive == Directive.BLOCK)
185#if !HAVE_GTK3138 return State.BLOCKED;
186 Gtk.DialogFlags.NO_SEPARATOR |139 if (config.enabled)
187#endif140 return State.ENABLED;
188 Gtk.DialogFlags.DESTROY_WITH_PARENT,141 else
189 Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,142 return State.DISABLED;
190 Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT);
191 dialog.set_icon_name (Gtk.STOCK_ADD);
192 dialog.resizable = false;
193
194 var hbox = new Gtk.HBox (false, 8);
195 var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
196 hbox.border_width = 5;
197 var label = new Gtk.Label.with_mnemonic (_("_Rule:"));
198 sizegroup.add_widget (label);
199 hbox.pack_start (label, false, false, 0);
200 (dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0);
201
202 var entry = new Gtk.Entry ();
203 sizegroup.add_widget (entry);
204 entry.activates_default = true;
205 entry.set_text (uri);
206 hbox.pack_start (entry, true, true, 0);
207
208 dialog.get_content_area ().show_all ();
209
210 dialog.set_default_response (Gtk.ResponseType.ACCEPT);
211 if (dialog.run () != Gtk.ResponseType.ACCEPT)
212 return;
213
214 string new_rule = entry.get_text ();
215 dialog.destroy ();
216 config.add_custom_rule (new_rule);
217 }143 }
218144
219
220 void resource_requested (WebKit.WebView web_view, WebKit.WebFrame frame,145 void resource_requested (WebKit.WebView web_view, WebKit.WebFrame frame,
221 WebKit.WebResource resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) {146 WebKit.WebResource resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) {
222147
223 if (request_handled (web_view.uri, request.uri))148 if (request_handled (web_view.uri, request.uri)) {
224 request.set_uri ("about:blank");149 request.set_uri ("about:blank");
150 state = adblock_get_state (get_directive_for_uri (web_view.uri));
151 status_icon.set_state (state);
152 }
225 }153 }
226154
227 bool navigation_requested (WebKit.WebFrame frame, WebKit.NetworkRequest request,155 bool navigation_requested (WebKit.WebFrame frame, WebKit.NetworkRequest request,
@@ -229,39 +157,28 @@
229157
230 string uri = request.uri;158 string uri = request.uri;
231 if (uri.has_prefix ("abp:")) {159 if (uri.has_prefix ("abp:")) {
232 uri = uri.replace ("abp://", "abp:");160 decision.ignore ();
233 if (uri.has_prefix ("abp:subscribe?location=")) {161 string parsed_uri = parse_subscription_uri (uri);
234 /* abp://subscripe?location=http://example.com&title=foo */162 manager.add_subscription (parsed_uri);
235 string[] parts = uri.substring (23, -1).split ("&", 2);163 return true;
236 decision.ignore ();
237 open_dialog (parts[0]);
238 return true;
239 }
240 }164 }
165 state = adblock_get_state (get_directive_for_uri (request.uri));
166 status_icon.set_state (state);
241 return false;167 return false;
242 }168 }
243169
244 void inject_css (Midori.View view, string page_uri) {170 string? get_hider_css_for_blocked_resources () {
245 /* Don't block ads on internal pages */
246 if (!Midori.URI.is_http (page_uri))
247 return;
248 string domain = Midori.URI.parse_hostname (page_uri, null);
249 string[] subdomains = domain.split (".");
250 if (subdomains == null)
251 return;
252 int cnt = subdomains.length - 1;
253 var subdomain = new StringBuilder (subdomains[cnt]);
254 subdomain.prepend_c ('.');
255 cnt--;
256 var code = new StringBuilder ();
257 bool debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
258 string hider_css;
259
260 /* Hide elements that were blocked, otherwise we will get "broken image" icon */171 /* Hide elements that were blocked, otherwise we will get "broken image" icon */
172 var code = new StringBuilder ();
261 cache.foreach ((key, val) => {173 cache.foreach ((key, val) => {
262 if (val == Adblock.Directive.BLOCK)174 if (val == Adblock.Directive.BLOCK)
263 code.append ("img[src*=\"%s\"] , iframe[src*=\"%s\"] , ".printf (key, key));175 code.append ("img[src*=\"%s\"] , iframe[src*=\"%s\"] , ".printf (key, key));
264 });176 });
177
178 if (code.str == "")
179 return null;
180
181 string hider_css;
265 if (debug_element)182 if (debug_element)
266 hider_css = " { background-color: red; border: 4px solid green; }";183 hider_css = " { background-color: red; border: 4px solid green; }";
267 else184 else
@@ -271,55 +188,96 @@
271 code.append (hider_css);188 code.append (hider_css);
272 if (debug_element)189 if (debug_element)
273 stdout.printf ("hider css: %s\n", code.str);190 stdout.printf ("hider css: %s\n", code.str);
274 view.inject_stylesheet (code.str);191 return code.str;
192 }
275193
276 code.erase ();194 string[]? get_domains_for_uri (string uri) {
277 int blockscnt = 0;195 if (uri == null)
196 return null;
197 string[]? domains = null;
198 string domain = Midori.URI.parse_hostname (uri, null);
199 string[] subdomains = domain.split (".");
200 if (subdomains == null)
201 return null;
202 int cnt = subdomains.length - 1;
203 var subdomain = new StringBuilder (subdomains[cnt]);
204 subdomain.prepend_c ('.');
205 cnt--;
278 while (cnt >= 0) {206 while (cnt >= 0) {
279 subdomain.prepend (subdomains[cnt]);207 subdomain.prepend (subdomains[cnt]);
280 string? style = null;208 domains += subdomain.str;
281 foreach (Subscription sub in config) {209 subdomain.prepend_c ('.');
282 foreach (var feature in sub) {210 cnt--;
283 if (feature is Adblock.Element) {211 }
284 style = (feature as Adblock.Element).lookup (subdomain.str);212 return domains;
285 break;213 }
214
215 string? get_hider_css_rules_for_uri (string page_uri) {
216 if (page_uri == null)
217 return null;
218 string[]? domains = get_domains_for_uri (page_uri);
219 if (domains == null)
220 return null;
221 var code = new StringBuilder ();
222 int blockscnt = 0;
223 string? style = null;
224 foreach (Subscription sub in config) {
225 foreach (var feature in sub) {
226 if (feature is Adblock.Element) {
227 foreach (var subdomain in domains) {
228 style = (feature as Adblock.Element).lookup (subdomain);
229 if (style != null) {
230 code.append (style);
231 code.append_c (',');
232 blockscnt++;
233 }
286 }234 }
287 }235 }
288 }236 }
289 if (style != null) {
290 code.append (style);
291 code.append_c (',');
292 blockscnt++;
293 }
294 subdomain.prepend_c ('.');
295 cnt--;
296 }237 }
238
297 if (blockscnt == 0)239 if (blockscnt == 0)
298 return;240 return null;
299 code.truncate (code.len - 1);241 code.truncate (code.len - 1);
300242
243 string hider_css;
301 if (debug_element)244 if (debug_element)
302 hider_css = " { background-color: red !important; border: 4px solid green !important; }";245 hider_css = " { background-color: red !important; border: 4px solid green !important; }";
303 else246 else
304 hider_css = " { display: none !important }";247 hider_css = " { display: none !important }";
305248
306 code.append (hider_css);249 code.append (hider_css);
307 view.inject_stylesheet (code.str);
308 if (debug_element)250 if (debug_element)
309 stdout.printf ("css: %s\n", code.str);251 stdout.printf ("css: %s\n", code.str);
252
253 return code.str;
254 }
255
256 void inject_css (Midori.View view, string page_uri) {
257 /* Don't block ads on internal pages */
258 if (!Midori.URI.is_http (page_uri))
259 return;
260
261 if ("adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? ""))
262 debug_element = true;
263 else
264 debug_element = status_icon.debug_element_toggled;
265
266 string? blocked_css = get_hider_css_for_blocked_resources ();
267 if (blocked_css != null)
268 view.inject_stylesheet (blocked_css);
269
270 string? style = get_hider_css_rules_for_uri (page_uri);
271 if (style != null)
272 view.inject_stylesheet (style);
310 }273 }
311#endif274#endif
312275
313 internal void init () {276 internal void init () {
314 debug ("Adblock2");
315
316 string config_dir = Midori.Paths.get_extension_config_dir ("adblock");
317 config = new Config (config_dir);
318 reload_rules ();
319 }
320
321 void reload_rules () {
322 cache = new HashTable<string, Directive?> (str_hash, str_equal);277 cache = new HashTable<string, Directive?> (str_hash, str_equal);
278 load_config ();
279 status_icon = new StatusIcon (config);
280 manager = new SubscriptionManager (config);
323 foreach (Subscription sub in config) {281 foreach (Subscription sub in config) {
324 try {282 try {
325 sub.parse ();283 sub.parse ();
@@ -327,24 +285,61 @@
327 warning ("Error parsing %s: %s", sub.uri, error.message);285 warning ("Error parsing %s: %s", sub.uri, error.message);
328 }286 }
329 }287 }
330 }288 config.notify["size"].connect (subscriptions_added_removed);
331289 manager.description_label.activate_link.connect (open_link);
332 bool request_handled (string page_uri, string request_uri) {290 }
333 /* Always allow the main page */291
334 if (request_uri == page_uri)292 bool open_link (string uri) {
335 return false;293 var browser = get_app ().browser;
336294 var view = browser.add_uri (uri);
337 /* Skip adblock on internal pages */295 browser.tab = view;
338 if (Midori.URI.is_blank (page_uri))296 return true;
339 return false;297 }
298
299 void subscriptions_added_removed (ParamSpec pspec) {
300 cache.remove_all ();
301 }
302
303 void load_config () {
304 string config_dir = Midori.Paths.get_extension_config_dir ("adblock");
305 string presets = Midori.Paths.get_extension_preset_filename ("adblock", "config");
306 string filename = Path.build_filename (config_dir, "config");
307 config = new Config (filename, presets);
308 string custom_list = GLib.Path.build_filename (config_dir, "custom.list");
309 try {
310 custom = new Subscription (Filename.to_uri (custom_list, null));
311 custom.mutable = false;
312 custom.title = _("Custom");
313 config.add (custom);
314 } catch (Error error) {
315 custom = null;
316 warning ("Failed to add custom list %s: %s", custom_list, error.message);
317 }
318 }
319
320 public Adblock.Directive get_directive_for_uri (string request_uri, string? page_uri = null) {
321 if (!config.enabled)
322 return Directive.ALLOW;
323
324 if (page_uri != null) {
325 /* Always allow the main page */
326 if (request_uri == page_uri)
327 return Directive.ALLOW;
328
329 /* Skip adblock on internal pages */
330 if (Midori.URI.is_blank (page_uri))
331 return Directive.ALLOW;
332 }
340333
341 /* Skip adblock on favicons and non http schemes */334 /* Skip adblock on favicons and non http schemes */
342 if (!Midori.URI.is_http (request_uri) || request_uri.has_suffix ("favicon.ico"))335 if (!Midori.URI.is_http (request_uri) || request_uri.has_suffix ("favicon.ico"))
343 return false;336 return Directive.ALLOW;
344337
345 Directive? directive = cache.lookup (request_uri);338 Directive? directive = cache.lookup (request_uri);
346 if (directive == null) {339 if (directive == null) {
347 foreach (Subscription sub in config) {340 foreach (Subscription sub in config) {
341 if (page_uri == null)
342 page_uri = request_uri;
348 directive = sub.get_directive (request_uri, page_uri);343 directive = sub.get_directive (request_uri, page_uri);
349 if (directive != null)344 if (directive != null)
350 break;345 break;
@@ -352,8 +347,14 @@
352 if (directive == null)347 if (directive == null)
353 directive = Directive.ALLOW;348 directive = Directive.ALLOW;
354 cache.insert (request_uri, directive);349 cache.insert (request_uri, directive);
350 if (directive == Directive.BLOCK)
351 cache.insert (page_uri, directive);
355 }352 }
356 return directive == Directive.BLOCK;353 return directive;
354 }
355
356 internal bool request_handled (string page_uri, string request_uri) {
357 return get_directive_for_uri (request_uri, page_uri) == Directive.BLOCK;
357 }358 }
358 }359 }
359360
@@ -411,6 +412,179 @@
411#endif412#endif
412413
413#if !HAVE_WEBKIT2414#if !HAVE_WEBKIT2
415static string? tmp_folder = null;
416string get_test_file (string contents) {
417 if (tmp_folder == null)
418 tmp_folder = Midori.Paths.make_tmp_dir ("adblockXXXXXX");
419 string checksum = Checksum.compute_for_string (ChecksumType.MD5, contents);
420 string file = Path.build_path (Path.DIR_SEPARATOR_S, tmp_folder, checksum);
421 try {
422 FileUtils.set_contents (file, contents, -1);
423 } catch (Error file_error) {
424 GLib.error (file_error.message);
425 }
426 return file;
427}
428
429struct TestCaseConfig {
430 public string content;
431 public uint size;
432 public bool enabled;
433}
434
435const TestCaseConfig[] configs = {
436 { "", 0, true },
437 { "[settings]", 0, true },
438 { "[settings]\nfilters=foo;", 1, true },
439 { "[settings]\nfilters=foo;\ndisabled=true", 1, false }
440};
441
442void test_adblock_config () {
443 assert (new Adblock.Config (null, null).size == 0);
444
445 foreach (var conf in configs) {
446 var config = new Adblock.Config (get_test_file (conf.content), null);
447 if (config.size != conf.size)
448 error ("Wrong size %s rather than %s:\n%s",
449 config.size.to_string (), conf.size.to_string (), conf.content);
450 if (config.enabled != conf.enabled)
451 error ("Wrongly got enabled=%s rather than %s:\n%s",
452 config.enabled.to_string (), conf.enabled.to_string (), conf.content);
453 }
454}
455
456struct TestCaseSub {
457 public string uri;
458 public bool active;
459}
460
461const TestCaseSub[] subs = {
462 { "http://foo.com", true },
463 { "http://bar.com", false },
464 { "https://spam.com", true },
465 { "https://eggs.com", false },
466 { "file:///bla", true },
467 { "file:///blub", false }
468};
469
470void test_adblock_subs () {
471 var config = new Adblock.Config (get_test_file ("""
472[settings]
473filters=http://foo.com;http-//bar.com;https://spam.com;http-://eggs.com;file:///bla;file-///blub;http://foo.com;
474"""), null);
475
476 assert (config.enabled);
477 foreach (var sub in subs) {
478 bool found = false;
479 foreach (var subscription in config) {
480 if (subscription.uri == sub.uri) {
481 assert (subscription.active == sub.active);
482 found = true;
483 }
484 }
485 if (!found)
486 error ("%s not found", sub.uri);
487 }
488
489 /* 6 unique URLs, 1 duplicate */
490 assert (config.size == 6);
491 /* Duplicates aren't added again either */
492 assert (!config.add (new Adblock.Subscription ("https://spam.com")));
493
494 /* Saving the config and loading it should give back identical results */
495 config.save ();
496 var copy = new Adblock.Config (config.path, null);
497 assert (copy.size == config.size);
498 assert (copy.enabled == config.enabled);
499 for (int i = 0; i < config.size; i++) {
500 assert (copy[i].uri == config[i].uri);
501 assert (copy[i].active == config[i].active);
502 }
503 /* Enabled status should be saved and loaded */
504 config.enabled = false;
505 copy = new Adblock.Config (config.path, null);
506 assert (copy.enabled == config.enabled);
507 /* Flipping individual active values should be retained after saving */
508 foreach (var sub in config)
509 sub.active = !sub.active;
510 copy = new Adblock.Config (config.path, null);
511 for (uint i = 0; i < config.size; i++) {
512 if (config[i].active != copy[i].active) {
513 string contents;
514 try {
515 FileUtils.get_contents (config.path, out contents, null);
516 } catch (Error file_error) {
517 error (file_error.message);
518 }
519 error ("%s is %s but should be %s:\n%s",
520 copy[i].uri, copy[i].active ? "active" : "disabled", config[i].active ? "active" : "disabled", contents);
521 }
522 }
523
524 /* Adding and removing works, changes size */
525 var s = new Adblock.Subscription ("http://en.de");
526 assert (config.add (s));
527 assert (config.size == 7);
528 config.remove (s);
529 assert (config.size == 6);
530 /* If it was removed before we should be able to add it again */
531 assert (config.add (s));
532 assert (config.size == 7);
533}
534
535void test_adblock_init () {
536 /* No config */
537 var extension = new Adblock.Extension ();
538 extension.init ();
539 assert (extension.config.enabled);
540 /* Defaults plus custom */
541 if (extension.config.size != 3)
542 error ("Expected 3 initial subs, got %s".printf (
543 extension.config.size.to_string ()));
544 assert (extension.cache.size () == 0);
545
546 /* Add new subscription */
547 string path = Midori.Paths.get_res_filename ("adblock.list");
548 string uri;
549 try {
550 uri = Filename.to_uri (path, null);
551 } catch (Error error) {
552 GLib.error (error.message);
553 }
554 var sub = new Adblock.Subscription (uri);
555 extension.config.add (sub);
556 assert (extension.cache.size () == 0);
557 assert (extension.config.size == 4);
558 try {
559 sub.parse ();
560 } catch (GLib.Error error) {
561 GLib.error (error.message);
562 }
563 /* The page itself never hits */
564 assert (!extension.request_handled ("https://ads.bogus.name/blub", "https://ads.bogus.name/blub"));
565 /* Favicons don't either */
566 assert (!extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub/favicon.ico"));
567 assert (extension.cache.size () == 0);
568 /* Some sanity checks to be sure there's no earlier problem */
569 assert (sub.title == "Exercise");
570 assert (sub.get_directive ("https://ads.bogus.name/blub", "") == Adblock.Directive.BLOCK);
571 /* A rule hit should add to the cache */
572 assert (extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub"));
573 assert (extension.cache.size () > 0);
574 /* Disabled means no request should be handled */
575 extension.config.enabled = false;
576 assert (!extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub"));
577 /* Removing a subscription should clear the cache */
578 extension.config.remove (sub);
579 assert (extension.cache.size () == 0);
580 assert (extension.config.size == 3);
581 /* Now let's add a custom rule */
582 extension.config.enabled = true;
583 extension.custom.add_rule ("*.png");
584 assert (!extension.request_handled ("https://foo.com", "http://alpha.beta.com/images/yota.png"));
585 assert (extension.cache.size () > 0);
586 }
587
414struct TestCaseLine {588struct TestCaseLine {
415 public string line;589 public string line;
416 public string fixed;590 public string fixed;
@@ -499,19 +673,27 @@
499 }673 }
500}674}
501675
676string pretty_date (DateTime? date) {
677 if (date == null)
678 return "N/A";
679 return date.to_string ();
680}
681
502struct TestUpdateExample {682struct TestUpdateExample {
503 public string content;683 public string content;
504 public bool result;684 public bool result;
685 public bool valid;
505}686}
506687
507 const TestUpdateExample[] examples = {688 const TestUpdateExample[] examples = {
508 { "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true },689 { "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true, true },
509 { "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true },690 { "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true, true },
510 { "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false },691 { "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false, true },
511 { "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false },692 { "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false, true },
512 { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true },693 { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true, true },
513 { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true },694 { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true, true },
514 { "[Adblock]\n ! dummy, i dont have any dates\n", false }695 { "[Adblock]\n ! dummy, i dont have any dates\n", false, true },
696 { "\n", false, false }
515 };697 };
516698
517void test_subscription_update () {699void test_subscription_update () {
@@ -531,23 +713,52 @@
531 foreach (var example in examples) {713 foreach (var example in examples) {
532 try {714 try {
533 file.replace_contents (example.content.data, null, false, FileCreateFlags.NONE, null);715 file.replace_contents (example.content.data, null, false, FileCreateFlags.NONE, null);
534 updater.last_mod_meta = null;716 sub.clear ();
535 updater.expires_meta = null;
536 sub.parse ();717 sub.parse ();
537 } catch (Error error) {718 } catch (Error error) {
538 GLib.error (error.message);719 GLib.error (error.message);
539 }720 }
540 if (example.result == true)721 if (example.valid != sub.valid)
541 assert (updater.needs_updating());722 error ("Subscription expected to be %svalid but %svalid:\n%s",
542 else723 example.valid ? "" : "in", sub.valid ? "" : "in", example.content);
543 assert (!updater.needs_updating());724 if (example.result != updater.needs_update)
725 error ("Update%s expected for:\n%s\nLast Updated: %s\nExpires: %s",
726 example.result ? "" : " not", example.content,
727 pretty_date (updater.last_updated), pretty_date (updater.expires));
728 }
729}
730
731struct TestSubUri {
732 public string? src_uri;
733 public string? dst_uri;
734}
735
736const TestSubUri[] suburis =
737{
738 { null, null },
739 { "not-a-link", null },
740 { "http://some.uri", "http://some.uri" },
741 { "abp:subscribe?location=https%3A%2F%2Feasylist-downloads.adblockplus.org%2Fabpindo%2Beasylist.txt&title=ABPindo%2BEasyList", "https://easylist-downloads.adblockplus.org/abpindo+easylist.txt" }
742};
743
744void test_subscription_uri_parsing () {
745 string? parsed_uri;
746 foreach (var example in suburis) {
747 parsed_uri = Adblock.parse_subscription_uri (example.src_uri);
748 if (parsed_uri != example.dst_uri)
749 error ("Subscription expected to be %svalid but %svalid:\n%s",
750 example.dst_uri, parsed_uri, example.src_uri);
544 }751 }
545}752}
546753
547public void extension_test () {754public void extension_test () {
755 Test.add_func ("/extensions/adblock2/config", test_adblock_config);
756 Test.add_func ("/extensions/adblock2/subs", test_adblock_subs);
757 Test.add_func ("/extensions/adblock2/init", test_adblock_init);
548 Test.add_func ("/extensions/adblock2/parse", test_adblock_fixup_regexp);758 Test.add_func ("/extensions/adblock2/parse", test_adblock_fixup_regexp);
549 Test.add_func ("/extensions/adblock2/pattern", test_adblock_pattern);759 Test.add_func ("/extensions/adblock2/pattern", test_adblock_pattern);
550 Test.add_func ("/extensions/adblock2/update", test_subscription_update);760 Test.add_func ("/extensions/adblock2/update", test_subscription_update);
761 Test.add_func ("/extensions/adblock2/subsparse", test_subscription_uri_parsing);
551}762}
552#endif763#endif
553764
554765
=== modified file 'extensions/adblock/subscriptions.vala'
--- extensions/adblock/subscriptions.vala 2014-02-20 18:27:01 +0000
+++ extensions/adblock/subscriptions.vala 2014-03-10 10:27:25 +0000
@@ -15,6 +15,9 @@
15 public virtual bool header (string key, string value) {15 public virtual bool header (string key, string value) {
16 return false;16 return false;
17 }17 }
18 public virtual bool parsed (File file) {
19 return true;
20 }
18 public virtual Directive? match (string request_uri, string page_uri) throws Error {21 public virtual Directive? match (string request_uri, string page_uri) throws Error {
19 return null;22 return null;
20 }23 }
@@ -24,8 +27,12 @@
2427
25 public class Subscription : GLib.Object {28 public class Subscription : GLib.Object {
26 public string? path;29 public string? path;
30 bool debug_parse;
27 public string uri { get; set; default = null; }31 public string uri { get; set; default = null; }
32 public string title { get; set; default = null; }
28 public bool active { get; set; default = true; }33 public bool active { get; set; default = true; }
34 public bool mutable { get; set; default = true; }
35 public bool valid { get; private set; default = true; }
29 List<Feature> features;36 List<Feature> features;
30 public Pattern pattern;37 public Pattern pattern;
31 public Keys keys;38 public Keys keys;
@@ -35,6 +42,8 @@
35 WebKit.Download? download;42 WebKit.Download? download;
3643
37 public Subscription (string uri) {44 public Subscription (string uri) {
45 debug_parse = "adblock:parse" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
46
38 this.uri = uri;47 this.uri = uri;
3948
40 this.optslist = new Options ();49 this.optslist = new Options ();
@@ -172,7 +181,8 @@
172 return;181 return;
173182
174 string format_patt = fixup_regex (prefix, patt);183 string format_patt = fixup_regex (prefix, patt);
175 debug ("got: %s opts %s", format_patt, opts);184 if (debug_parse)
185 stdout.printf ("got: %s opts %s\n", format_patt, opts);
176 compile_regexp (format_patt, opts);186 compile_regexp (format_patt, opts);
177 /* return format_patt */187 /* return format_patt */
178 }188 }
@@ -186,7 +196,8 @@
186 if (Regex.match_simple ("^/.*[\\^\\$\\*].*/$", patt,196 if (Regex.match_simple ("^/.*[\\^\\$\\*].*/$", patt,
187 RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY)197 RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY)
188 || opts != null && opts.contains ("whitelist")) {198 || opts != null && opts.contains ("whitelist")) {
189 debug ("patt: %s", patt);199 if (debug_parse)
200 stdout.printf ("patt: %s\n", patt);
190 if (opts.contains ("whitelist"))201 if (opts.contains ("whitelist"))
191 this.whitelist.insert (patt, regex);202 this.whitelist.insert (patt, regex);
192 else203 else
@@ -233,12 +244,15 @@
233 string value = "";244 string value = "";
234 if (header.contains (":")) {245 if (header.contains (":")) {
235 string[] parts = header.split (":", 2);246 string[] parts = header.split (":", 2);
236 if (parts[0] != null) {247 if (parts[0] != null && parts[0] != ""
248 && parts[1] != null && parts[1] != "") {
237 key = parts[0].substring (2, -1);249 key = parts[0].substring (2, -1);
238 value = parts[1].substring (1, -1);250 value = parts[1].substring (1, -1);
239 }251 }
240 }252 }
241 debug ("Header '%s' says '%s'", key, value);253 debug ("Header '%s' says '%s'", key, value);
254 if (key == "Title")
255 title = value;
242 foreach (var feature in features) {256 foreach (var feature in features) {
243 if (feature.header (key, value))257 if (feature.header (key, value))
244 break;258 break;
@@ -283,18 +297,25 @@
283#if HAVE_WEBKIT2297#if HAVE_WEBKIT2
284 /* TODO */298 /* TODO */
285#else299#else
286 if (download != null)300 /* Don't bother trying to download local files */
287 return;301 if (!uri.has_prefix ("file://")) {
302 if (download != null)
303 return;
288304
289 download = new WebKit.Download (new WebKit.NetworkRequest (uri));305 string destination_uri = Filename.to_uri (path, null);
290 download.destination_uri = Filename.to_uri (path, null);306 debug ("Fetching %s to %s now", uri, destination_uri);
291 download.notify["status"].connect (download_status);307 download = new WebKit.Download (new WebKit.NetworkRequest (uri));
292 debug ("Fetching %s to %s now", uri, download.destination_uri);308 if (!Midori.Download.has_enough_space (download, destination_uri, true))
293 download.start ();309 throw new FileError.EXIST ("Can't download to \"%s\"", path);
310 download.destination_uri = destination_uri;
311 download.notify["status"].connect (download_status);
312 download.start ();
294#endif313#endif
314 }
295 return;315 return;
296 }316 }
297317
318 valid = false;
298 string? line;319 string? line;
299 while ((line = stream.read_line (null)) != null) {320 while ((line = stream.read_line (null)) != null) {
300 if (line == null)321 if (line == null)
@@ -306,6 +327,13 @@
306 parse_header (chomped);327 parse_header (chomped);
307 else328 else
308 parse_line (chomped);329 parse_line (chomped);
330 /* The file isn't completely empty */
331 valid = true;
332 }
333
334 foreach (var feature in features) {
335 if (!feature.parsed (filter_file))
336 valid = false;
309 }337 }
310 }338 }
311339
@@ -324,5 +352,15 @@
324 }352 }
325 return null;353 return null;
326 }354 }
355
356 public void add_rule (string rule) {
357 try {
358 var file = File.new_for_uri (uri);
359 file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data);
360 parse ();
361 } catch (Error error) {
362 warning ("Failed to add custom rule: %s", error.message);
363 }
364 }
327 }365 }
328}366}
329367
=== modified file 'extensions/adblock/updater.vala'
--- extensions/adblock/updater.vala 2014-02-20 00:14:15 +0000
+++ extensions/adblock/updater.vala 2014-03-10 10:27:25 +0000
@@ -12,15 +12,23 @@
1212
13namespace Adblock {13namespace Adblock {
14 public class Updater : Feature {14 public class Updater : Feature {
15 public string expires_meta { get; set; default = null; }15 string expires_meta;
16 public string last_mod_meta { get; set; default = null; }16 string last_mod_meta;
17 public int64 update_tstamp { get; set; default = 0; }17 public DateTime last_updated { get; set; }
18 public int64 last_mod_tstamp { get; set; default = 0; }18 public DateTime expires { get; set; }
19 public int64 last_check_tstamp { get; set; default = 0; }19 public bool needs_update { get; set; }
2020
21 public Updater () {21 public Updater () {
22 }22 }
2323
24 public override void clear () {
25 expires_meta = null;
26 last_mod_meta = null;
27 last_updated = null;
28 expires = null;
29 needs_update = false;
30 }
31
24 public override bool header (string key, string value) {32 public override bool header (string key, string value) {
25 if (key.has_prefix ("Last mod") || key == "Updated") {33 if (key.has_prefix ("Last mod") || key == "Updated") {
26 last_mod_meta = value;34 last_mod_meta = value;
@@ -37,6 +45,12 @@
37 return false;45 return false;
38 }46 }
3947
48 public override bool parsed (File file) {
49 process_dates (file);
50 /* It's not an error to have no update headers, we go for defaults */
51 return true;
52 }
53
40 int get_month_from_string (string? month) {54 int get_month_from_string (string? month) {
41 if (month == null)55 if (month == null)
42 return 0;56 return 0;
@@ -50,19 +64,17 @@
50 return 0;64 return 0;
51 }65 }
5266
53 public bool needs_updating () {67 void process_dates (File file) {
54 DateTime now = new DateTime.now_local ();68 DateTime now = new DateTime.now_local ();
55 DateTime expire_date = null;69 last_updated = null;
56 DateTime last_mod_date = null;70 expires = null;
57 string? last_mod = last_mod_meta;
58 string? expires = expires_meta;
5971
60 /* We have "last modification" metadata */72 /* We have "last modification" metadata */
61 if (last_mod != null) {73 if (last_mod_meta != null) {
62 int h = 0, min = 0, d, m, y;74 int h = 0, min = 0, d, m, y;
63 /* Date in a form of: 20.08.2012 12:34 */75 /* Date in a form of: 20.08.2012 12:34 */
64 if (last_mod.contains (".") || last_mod.contains("-")) {76 if (last_mod_meta.contains (".") || last_mod_meta.contains("-")) {
65 string[] parts = last_mod.split (" ", 2);77 string[] parts = last_mod_meta.split (" ", 2);
66 string[] date_parts;78 string[] date_parts;
67 string split_char = " ";79 string split_char = " ";
6880
@@ -89,7 +101,7 @@
89 d = int.parse(date_parts[2]);101 d = int.parse(date_parts[2]);
90 }102 }
91 } else { /* Date in a form of: 20 Mar 2012 12:34 */103 } else { /* Date in a form of: 20 Mar 2012 12:34 */
92 string[] parts = last_mod.split (" ", 4);104 string[] parts = last_mod_meta.split (" ", 4);
93 /* contains time part ? */105 /* contains time part ? */
94 if (parts[3] != null && parts[3].contains (":")) {106 if (parts[3] != null && parts[3].contains (":")) {
95 string[] time_parts = parts[3].split (":", 2);107 string[] time_parts = parts[3].split (":", 2);
@@ -107,33 +119,37 @@
107 }119 }
108 }120 }
109121
110 last_mod_date = new DateTime.local (y, m, d, h, min, 0.0);122 last_updated = new DateTime.local (y, m, d, h, min, 0.0);
123 } else {
124 /* FIXME: use file modification date if there's no update header
125 try {
126 string modified = FileAttribute.TIME_MODIFIED;
127 var info = file.query_filesystem_info (modified);
128 last_updated = new DateTime.from_timeval_local (info.get_modification_time ());
129 } catch (Error error) {
130 last_updated = now;
131 }
132 */
133 last_updated = now;
111 }134 }
112135
113 if (last_mod_date == null)
114 last_mod_date = now;
115
116 /* We have "expires" metadata */136 /* We have "expires" metadata */
117 if (expires != null) {137 if (expires_meta != null) {
118 if (expires.contains ("days")) {138 if (expires_meta.contains ("days")) {
119 string[] parts = expires.split (" ");139 string[] parts = expires_meta.split (" ");
120 expire_date = last_mod_date.add_days (int.parse (parts[0]));140 expires = last_updated.add_days (int.parse (parts[0]));
121 } else if (expires.contains ("hours")) {141 } else if (expires_meta.contains ("hours")) {
122 string[] parts = expires.split (" ");142 string[] parts = expires_meta.split (" ");
123 expire_date = last_mod_date.add_hours (int.parse (parts[0]));143 expires = last_updated.add_hours (int.parse (parts[0]));
124 }144 }
125 } else {145 } else {
126 /* No expire metadata found, assume x days */146 /* No expire metadata found, assume x days */
127 int days_to_expire = 7;147 int days_to_expire = 7;
128 expire_date = last_mod_date.add_days (days_to_expire);148 expires = last_updated.add_days (days_to_expire);
129 }149 }
130150
131 last_mod_tstamp = last_mod_date.to_unix ();
132 last_check_tstamp = now.to_unix ();
133 update_tstamp = expire_date.to_unix ();
134
135 /* Check if we are past expire date */151 /* Check if we are past expire date */
136 return now.compare (expire_date) == 1;152 needs_update = now.compare (expires) == 1;
137 }153 }
138 }154 }
139}155}
140156
=== added file 'extensions/adblock/widgets.vala'
--- extensions/adblock/widgets.vala 1970-01-01 00:00:00 +0000
+++ extensions/adblock/widgets.vala 2014-03-10 10:27:25 +0000
@@ -0,0 +1,310 @@
1/*
2 Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
3 Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 See the file COPYING for the full license text.
11*/
12
13namespace Adblock {
14
15
16 public class StatusIcon {
17 Config config;
18 State state;
19 public bool debug_element_toggled;
20 public List<IconButton> toggle_buttons;
21
22 public StatusIcon (Adblock.Config config) {
23 this.config = config;
24 this.debug_element_toggled = false;
25 }
26
27 public void set_state (Adblock.State state) {
28 this.state = state;
29 update_buttons ();
30 }
31
32 public class IconButton : Gtk.Button {
33 Gtk.Image icon;
34
35 public IconButton () {
36 icon = new Gtk.Image ();
37 add (icon);
38 icon.show ();
39 }
40
41 public void set_status (string status) {
42 string filename = Midori.Paths.get_res_filename ("adblock/%s.svg".printf (status));
43 icon.set_from_file (filename);
44 }
45 }
46
47 public void update_buttons () {
48 string state = "";
49 foreach (var toggle_button in toggle_buttons) {
50 if (this.state == State.BLOCKED) {
51 toggle_button.set_status ("blocked");
52 state = _("Blocking");
53 }
54 if (this.state == State.ENABLED) {
55 toggle_button.set_status ("enabled");
56 state = _("Enabled");
57 }
58 if (this.state == State.DISABLED) {
59 toggle_button.set_status ("disabled");
60 state = _("Disabled");
61 }
62 toggle_button.set_tooltip_text (_("Adblock state: %s").printf (state));
63 }
64 }
65
66 public void icon_clicked (Gtk.Button toggle_button) {
67 var menu = new Gtk.Menu ();
68 var checkitem = new Gtk.CheckMenuItem.with_label (_("Disabled"));
69 checkitem.set_active (!config.enabled);
70 checkitem.toggled.connect (() => {
71 config.enabled = !checkitem.active;
72 set_state (config.enabled ? Adblock.State.ENABLED : Adblock.State.DISABLED);
73 });
74 menu.append (checkitem);
75
76 var hideritem = new Gtk.CheckMenuItem.with_label (_("Display hidden elements"));
77 hideritem.set_active (debug_element_toggled);
78 hideritem.toggled.connect (() => {
79 this.debug_element_toggled = hideritem.active;
80 });
81 menu.append (hideritem);
82
83 var menuitem = new Gtk.ImageMenuItem.with_label (_("Preferences"));
84 var image = new Gtk.Image.from_stock (Gtk.STOCK_PREFERENCES, Gtk.IconSize.MENU);
85 menuitem.always_show_image = true;
86 menuitem.set_image (image);
87 menuitem.activate.connect (() => {
88 SubscriptionManager manager = new SubscriptionManager (config);
89 manager.add_subscription (null);
90 });
91 menu.append (menuitem);
92
93 menu.show_all ();
94 Katze.widget_popup (toggle_button, menu, null, Katze.MenuPos.CURSOR);
95 }
96 }
97
98 public class SubscriptionManager {
99 Gtk.TreeView treeview;
100 Gtk.ListStore liststore;
101 Adblock.Config config;
102 public Gtk.Label description_label;
103 string description;
104
105 public SubscriptionManager (Config config) {
106 this.config = config;
107 this.liststore = new Gtk.ListStore (1, typeof (Subscription));
108 this.description_label = new Gtk.Label (null);
109 this.description = _("Type the address of a preconfigured filter list in the text entry and hit Enter.\n");
110 this.description += _("You can find more lists by visiting following sites:\n %s, %s\n".printf (
111 "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>",
112 "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>"
113 ));
114 }
115
116 public void add_subscription (string? uri) {
117 var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"),
118 null,
119#if !HAVE_GTK3
120 Gtk.DialogFlags.NO_SEPARATOR |
121#endif
122 Gtk.DialogFlags.DESTROY_WITH_PARENT,
123 Gtk.STOCK_HELP, Gtk.ResponseType.HELP,
124 Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
125#if HAVE_GTK3
126 dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button");
127#endif
128 dialog.set_icon_name (Gtk.STOCK_PROPERTIES);
129 dialog.set_response_sensitive (Gtk.ResponseType.HELP, false);
130
131 var hbox = new Gtk.HBox (false, 0);
132 (dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12);
133 var vbox = new Gtk.VBox (false, 0);
134 hbox.pack_start (vbox, true, true, 4);
135 this.description_label.set_markup (this.description);
136 this.description_label.set_line_wrap (true);
137 vbox.pack_start (this.description_label, false, false, 4);
138
139 var entry = new Gtk.Entry ();
140 if (uri != null)
141 entry.set_text (uri);
142 vbox.pack_start (entry, false, false, 4);
143
144 liststore = new Gtk.ListStore (1, typeof (Subscription));
145 treeview = new Gtk.TreeView.with_model (liststore);
146 treeview.set_headers_visible (false);
147 var column = new Gtk.TreeViewColumn ();
148 var renderer_toggle = new Gtk.CellRendererToggle ();
149 column.pack_start (renderer_toggle, false);
150 column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => {
151 Subscription sub;
152 liststore.get (iter, 0, out sub);
153 renderer.set ("active", sub.active,
154 "sensitive", sub.mutable);
155 });
156 renderer_toggle.toggled.connect ((path) => {
157 Gtk.TreeIter iter;
158 if (liststore.get_iter_from_string (out iter, path)) {
159 Subscription sub;
160 liststore.get (iter, 0, out sub);
161 sub.active = !sub.active;
162 }
163 });
164 treeview.append_column (column);
165
166 column = new Gtk.TreeViewColumn ();
167 var renderer_text = new Gtk.CellRendererText ();
168 column.pack_start (renderer_text, false);
169 renderer_text.set ("editable", true);
170 // TODO: renderer_text.edited.connect
171 column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => {
172 Subscription sub;
173 liststore.get (iter, 0, out sub);
174 string status = "";
175 foreach (var feature in sub) {
176 if (feature is Adblock.Updater) {
177 var updater = feature as Adblock.Updater;
178 if (updater.last_updated != null)
179 status = updater.last_updated.format (_("Last update: %x %X"));
180 }
181 }
182 if (!sub.valid)
183 status = _("File incomplete - broken download?");
184 renderer.set ("markup", (Markup.printf_escaped ("<b>%s</b>\n%s",
185 sub.title ?? sub.uri, status)));
186 });
187 treeview.append_column (column);
188
189 column = new Gtk.TreeViewColumn ();
190 Gtk.CellRendererPixbuf renderer_button = new Gtk.CellRendererPixbuf ();
191 column.pack_start (renderer_button, false);
192 column.set_cell_data_func (renderer_button, on_render_button);
193 treeview.append_column (column);
194
195 var scrolled = new Gtk.ScrolledWindow (null, null);
196 scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
197 scrolled.add (treeview);
198 vbox.pack_start (scrolled);
199
200 foreach (Subscription sub in config)
201 liststore.insert_with_values (null, 0, 0, sub);
202 treeview.button_release_event.connect (button_released);
203
204 entry.activate.connect (() => {
205 string? parsed_uri = Adblock.parse_subscription_uri (entry.text);
206 if (parsed_uri != null) {
207 var sub = new Subscription (parsed_uri);
208 if (config.add (sub)) {
209 liststore.insert_with_values (null, 0, 0, sub);
210 try {
211 sub.parse ();
212 } catch (GLib.Error error) {
213 warning ("Error parsing %s: %s", sub.uri, error.message);
214 }
215 }
216 }
217 entry.text = "";
218 });
219
220 dialog.get_content_area ().show_all ();
221
222 dialog.response.connect ((response)=>{ dialog.destroy (); });
223 dialog.show ();
224 }
225
226 void on_render_button (Gtk.CellLayout column, Gtk.CellRenderer renderer,
227 Gtk.TreeModel model, Gtk.TreeIter iter) {
228
229 Subscription sub;
230 liststore.get (iter, 0, out sub);
231
232 renderer.set ("stock-id", sub.mutable ? Gtk.STOCK_DELETE : null,
233 "stock-size", Gtk.IconSize.MENU);
234 }
235
236 public bool button_released (Gdk.EventButton event) {
237 Gtk.TreePath? path;
238 Gtk.TreeViewColumn column;
239 if (treeview.get_path_at_pos ((int)event.x, (int)event.y, out path, out column, null, null)) {
240 if (path != null) {
241 if (column == treeview.get_column (2)) {
242 Gtk.TreeIter iter;
243 if (liststore.get_iter (out iter, path)) {
244 Subscription sub;
245 liststore.get (iter, 0, out sub);
246 if (sub.mutable) {
247 config.remove (sub);
248 liststore.remove (iter);
249 return true;
250 }
251 }
252 }
253 }
254 }
255 return false;
256 }
257 }
258
259 class CustomRulesEditor {
260 Gtk.Dialog dialog;
261 Subscription custom;
262 public string? rule { get; set; }
263
264 public CustomRulesEditor (Subscription custom) {
265 this.custom = custom;
266 }
267
268 public void set_uri (string uri) {
269 this.rule = uri;
270 }
271
272 public void show () {
273 this.dialog = new Gtk.Dialog.with_buttons (_("Edit rule"),
274 null,
275#if !HAVE_GTK3
276 Gtk.DialogFlags.NO_SEPARATOR |
277#endif
278 Gtk.DialogFlags.DESTROY_WITH_PARENT,
279 Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
280 Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT);
281 dialog.set_icon_name (Gtk.STOCK_ADD);
282 dialog.resizable = false;
283
284 var hbox = new Gtk.HBox (false, 8);
285 var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
286 hbox.border_width = 5;
287 var label = new Gtk.Label.with_mnemonic (_("_Rule:"));
288 sizegroup.add_widget (label);
289 hbox.pack_start (label, false, false, 0);
290 (dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0);
291
292 var entry = new Gtk.Entry ();
293 sizegroup.add_widget (entry);
294 entry.activates_default = true;
295 entry.set_text (this.rule);
296 hbox.pack_start (entry, true, true, 0);
297
298 dialog.get_content_area ().show_all ();
299
300 dialog.set_default_response (Gtk.ResponseType.ACCEPT);
301 if (dialog.run () != Gtk.ResponseType.ACCEPT)
302 return;
303
304 this.rule = entry.get_text ();
305 this.dialog.destroy ();
306 custom.add_rule (this.rule);
307 }
308 }
309
310}
0311
=== modified file 'katze/midori-paths.vala'
--- katze/midori-paths.vala 2014-02-20 18:27:01 +0000
+++ katze/midori-paths.vala 2014-03-10 10:27:25 +0000
@@ -366,17 +366,24 @@
366 if (Posix.access (path, Posix.F_OK) == 0)366 if (Posix.access (path, Posix.F_OK) == 0)
367 return path;367 return path;
368368
369 return build_folder ("data", null, filename) ??
370 Path.build_filename (MDATADIR, PACKAGE_NAME, "res", filename);
371 #endif
372 }
373
374 string? build_folder (string folder, string? middle, string filename) {
369 /* Fallback to build folder */375 /* Fallback to build folder */
370 File? parent = File.new_for_path (exec_path);376 File? parent = File.new_for_path (exec_path);
371 while (parent != null) {377 while (parent != null) {
372 var data = parent.get_child ("data");378 var data = parent.get_child (folder);
379 if (middle != null)
380 data = data.get_child (middle);
373 var child = data.get_child (filename);381 var child = data.get_child (filename);
374 if (child.query_exists ())382 if (child.query_exists ())
375 return child.get_path ();383 return child.get_path ();
376 parent = parent.get_parent ();384 parent = parent.get_parent ();
377 }385 }
378 return Path.build_filename (MDATADIR, PACKAGE_NAME, "res", filename);386 return null;
379 #endif
380 }387 }
381388
382 /* returns the path to a file containing read-only data installed with the application389 /* returns the path to a file containing read-only data installed with the application
@@ -416,7 +423,8 @@
416 return path;423 return path;
417 }424 }
418425
419 return Path.build_filename (SYSCONFDIR, "xdg", PACKAGE_NAME, folder ?? "", filename);426 return build_folder ("config", folder, filename) ??
427 Path.build_filename (SYSCONFDIR, "xdg", PACKAGE_NAME, folder ?? "", filename);
420 #endif428 #endif
421 }429 }
422430
423431
=== modified file 'midori/midori-app.c'
--- midori/midori-app.c 2014-02-24 22:16:00 +0000
+++ midori/midori-app.c 2014-03-10 10:27:25 +0000
@@ -1257,7 +1257,7 @@
1257midori_debug (const gchar* token)1257midori_debug (const gchar* token)
1258{1258{
1259 static const gchar* debug_token = NULL;1259 static const gchar* debug_token = NULL;
1260 const gchar* debug_tokens = "adblock:match adblock:time adblock:element startup headers body referer cookies paths hsts unarmed bookmarks mouse app ";1260 const gchar* debug_tokens = "adblock:match adblock:parse adblock:time adblock:element startup headers body referer cookies paths hsts unarmed bookmarks mouse app ";
1261 if (debug_token == NULL)1261 if (debug_token == NULL)
1262 {1262 {
1263 gchar* found_token;1263 gchar* found_token;
12641264
=== modified file 'midori/midori-browser.c'
--- midori/midori-browser.c 2014-03-03 21:28:53 +0000
+++ midori/midori-browser.c 2014-03-10 10:27:25 +0000
@@ -1237,7 +1237,7 @@
12371237
1238{1238{
1239#ifndef HAVE_WEBKIT21239#ifndef HAVE_WEBKIT2
1240 if (!midori_download_has_enough_space (download, uri))1240 if (!midori_download_has_enough_space (download, uri, FALSE))
1241 return FALSE;1241 return FALSE;
1242 webkit_download_set_destination_uri (download, uri);1242 webkit_download_set_destination_uri (download, uri);
1243 g_signal_emit (browser, signals[ADD_DOWNLOAD], 0, download);1243 g_signal_emit (browser, signals[ADD_DOWNLOAD], 0, download);
12441244
=== modified file 'midori/midori-download.vala'
--- midori/midori-download.vala 2014-03-05 07:17:45 +0000
+++ midori/midori-download.vala 2014-03-10 10:27:25 +0000
@@ -348,7 +348,7 @@
348 * Returns whether it seems possible to save @download to the path specified by348 * Returns whether it seems possible to save @download to the path specified by
349 * @destination_uri, considering space on disk and permissions349 * @destination_uri, considering space on disk and permissions
350 */350 */
351 public static bool has_enough_space (WebKit.Download download, string destination_uri) {351 public static bool has_enough_space (WebKit.Download download, string destination_uri, bool quiet=false) {
352#if !HAVE_WEBKIT2352#if !HAVE_WEBKIT2
353 var folder = File.new_for_uri (destination_uri).get_parent ();353 var folder = File.new_for_uri (destination_uri).get_parent ();
354 bool can_write;354 bool can_write;
@@ -380,7 +380,8 @@
380 }380 }
381 else381 else
382 assert_not_reached ();382 assert_not_reached ();
383 Sokoke.message_dialog (Gtk.MessageType.ERROR, message, detailed_message, false);383 if (!quiet)
384 Sokoke.message_dialog (Gtk.MessageType.ERROR, message, detailed_message, false);
384 return false;385 return false;
385 }386 }
386#endif387#endif
387388
=== modified file 'po/POTFILES.in'
--- po/POTFILES.in 2014-03-02 23:15:34 +0000
+++ po/POTFILES.in 2014-03-10 10:27:25 +0000
@@ -102,4 +102,5 @@
102extensions/adblock/config.vala102extensions/adblock/config.vala
103extensions/adblock/updater.vala103extensions/adblock/updater.vala
104extensions/adblock/element.vala104extensions/adblock/element.vala
105extensions/adblock/widgets.vala
105extensions/domain-keys.vala106extensions/domain-keys.vala

Subscribers

People subscribed via source and target branches

to all changes: