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
1=== added directory 'data/adblock'
2=== modified file 'data/adblock.list'
3--- data/adblock.list 2014-02-17 20:43:55 +0000
4+++ data/adblock.list 2014-03-10 10:27:25 +0000
5@@ -7,6 +7,7 @@
6 ! Licence: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
7 ! Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
8 !
9+! Some freeform text:
10 ! Yadayada http://example.com/ e-mail (somebody@example.com).
11 !
12 !-----Spam eggs--------!
13
14=== added file 'data/adblock/blocked.svg'
15--- data/adblock/blocked.svg 1970-01-01 00:00:00 +0000
16+++ data/adblock/blocked.svg 2014-03-10 10:27:25 +0000
17@@ -0,0 +1,46 @@
18+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
19+<!-- Created with Inkscape (http://www.inkscape.org/) -->
20+
21+<svg
22+ xmlns:dc="http://purl.org/dc/elements/1.1/"
23+ xmlns:cc="http://creativecommons.org/ns#"
24+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
25+ xmlns:svg="http://www.w3.org/2000/svg"
26+ xmlns="http://www.w3.org/2000/svg"
27+ version="1.1"
28+ width="16"
29+ height="16"
30+ id="svg7384">
31+ <title
32+ id="title9167">Gnome Symbolic Icon Theme</title>
33+ <defs
34+ id="defs10" />
35+ <metadata
36+ id="metadata90">
37+ <rdf:RDF>
38+ <cc:Work
39+ rdf:about="">
40+ <dc:format>image/svg+xml</dc:format>
41+ <dc:type
42+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
43+ <dc:title>Gnome Symbolic Icon Theme</dc:title>
44+ </cc:Work>
45+ </rdf:RDF>
46+ </metadata>
47+ <g
48+ transform="translate(-111.61542,-601.4038)"
49+ id="layer12">
50+ <path
51+ 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"
52+ id="path4222"
53+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
54+ <path
55+ d="m 231.41,609.44 -8,8 1.4062,1.4062 8,-8 -1.4062,-1.4062 z"
56+ id="path4992"
57+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
58+ </g>
59+ <path
60+ 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"
61+ id="path3016-2"
62+ 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" />
63+</svg>
64
65=== added file 'data/adblock/disabled.svg'
66--- data/adblock/disabled.svg 1970-01-01 00:00:00 +0000
67+++ data/adblock/disabled.svg 2014-03-10 10:27:25 +0000
68@@ -0,0 +1,46 @@
69+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
70+<!-- Created with Inkscape (http://www.inkscape.org/) -->
71+
72+<svg
73+ xmlns:dc="http://purl.org/dc/elements/1.1/"
74+ xmlns:cc="http://creativecommons.org/ns#"
75+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
76+ xmlns:svg="http://www.w3.org/2000/svg"
77+ xmlns="http://www.w3.org/2000/svg"
78+ version="1.1"
79+ width="16"
80+ height="16"
81+ id="svg7384">
82+ <title
83+ id="title9167">Gnome Symbolic Icon Theme</title>
84+ <defs
85+ id="defs10" />
86+ <metadata
87+ id="metadata90">
88+ <rdf:RDF>
89+ <cc:Work
90+ rdf:about="">
91+ <dc:format>image/svg+xml</dc:format>
92+ <dc:type
93+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
94+ <dc:title>Gnome Symbolic Icon Theme</dc:title>
95+ </cc:Work>
96+ </rdf:RDF>
97+ </metadata>
98+ <g
99+ transform="translate(-111.61542,-601.4038)"
100+ id="layer12">
101+ <path
102+ 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"
103+ id="path4222"
104+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
105+ <path
106+ d="m 231.41,609.44 -8,8 1.4062,1.4062 8,-8 -1.4062,-1.4062 z"
107+ id="path4992"
108+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
109+ </g>
110+ <path
111+ 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"
112+ id="path3016-2"
113+ 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" />
114+</svg>
115
116=== added file 'data/adblock/enabled.svg'
117--- data/adblock/enabled.svg 1970-01-01 00:00:00 +0000
118+++ data/adblock/enabled.svg 2014-03-10 10:27:25 +0000
119@@ -0,0 +1,80 @@
120+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
121+<!-- Created with Inkscape (http://www.inkscape.org/) -->
122+
123+<svg
124+ xmlns:dc="http://purl.org/dc/elements/1.1/"
125+ xmlns:cc="http://creativecommons.org/ns#"
126+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
127+ xmlns:svg="http://www.w3.org/2000/svg"
128+ xmlns="http://www.w3.org/2000/svg"
129+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
130+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
131+ version="1.1"
132+ width="16"
133+ height="16"
134+ id="svg7384"
135+ inkscape:version="0.48.4 r9939"
136+ sodipodi:docname="adblock-enabled.svg">
137+ <sodipodi:namedview
138+ pagecolor="#ffffff"
139+ bordercolor="#666666"
140+ borderopacity="1"
141+ objecttolerance="10"
142+ gridtolerance="10"
143+ guidetolerance="10"
144+ inkscape:pageopacity="0"
145+ inkscape:pageshadow="2"
146+ inkscape:window-width="1920"
147+ inkscape:window-height="1026"
148+ id="namedview10"
149+ showgrid="false"
150+ inkscape:zoom="14.75"
151+ inkscape:cx="8"
152+ inkscape:cy="8"
153+ inkscape:window-x="0"
154+ inkscape:window-y="32"
155+ inkscape:window-maximized="1"
156+ inkscape:current-layer="svg7384" />
157+ <title
158+ id="title9167">Gnome Symbolic Icon Theme</title>
159+ <defs
160+ id="defs10" />
161+ <metadata
162+ id="metadata90">
163+ <rdf:RDF>
164+ <cc:Work
165+ rdf:about="">
166+ <dc:format>image/svg+xml</dc:format>
167+ <dc:type
168+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
169+ <dc:title>Gnome Symbolic Icon Theme</dc:title>
170+ </cc:Work>
171+ </rdf:RDF>
172+ </metadata>
173+ <g
174+ transform="translate(-111.61542,-601.4038)"
175+ id="layer12">
176+ <path
177+ 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"
178+ id="path4222"
179+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
180+ <path
181+ d="m 231.41,609.44 -8,8 1.4062,1.4062 8,-8 -1.4062,-1.4062 z"
182+ id="path4992"
183+ style="text-indent:0;text-transform:none;block-progression:tb;color:#000000;fill:#bebebe" />
184+ </g>
185+ <path
186+ 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"
187+ id="path3016-2"
188+ 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" />
189+ <path
190+ style="fill:#000000;fill-opacity:0"
191+ 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"
192+ id="path2987"
193+ inkscape:connector-curvature="0" />
194+ <path
195+ style="fill:#000000;fill-opacity:0"
196+ 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"
197+ id="path2989"
198+ inkscape:connector-curvature="0" />
199+</svg>
200
201=== modified file 'extensions/adblock/config.vala'
202--- extensions/adblock/config.vala 2014-02-20 18:27:01 +0000
203+++ extensions/adblock/config.vala 2014-03-10 10:27:25 +0000
204@@ -12,27 +12,26 @@
205 namespace Adblock {
206 public class Config : GLib.Object {
207 List<Subscription> subscriptions;
208- string? path;
209+ public string? path { get; private set; }
210 KeyFile keyfile;
211- Subscription? custom;
212+ bool should_save;
213+ public bool enabled { get; set; }
214
215- public Config (string? path) {
216+ public Config (string? path, string? presets) {
217+ should_save = false;
218 subscriptions = new GLib.List<Subscription> ();
219-
220+ enabled = true;
221 this.path = path;
222- if (path == null)
223+ size = 0;
224+ load_file (path);
225+ load_file (presets);
226+ should_save = true;
227+ }
228+
229+ void load_file (string? filename) {
230+ if (filename == null)
231 return;
232
233- string custom_list = GLib.Path.build_filename (path, "custom.list");
234- try {
235- custom = new Subscription (Filename.to_uri (custom_list, null));
236- subscriptions.append (custom);
237- } catch (Error error) {
238- custom = null;
239- warning ("Failed to add custom list %s: %s", custom_list, error.message);
240- }
241-
242- string filename = GLib.Path.build_filename (path, "config");
243 keyfile = new GLib.KeyFile ();
244 try {
245 keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);
246@@ -40,68 +39,108 @@
247 foreach (string filter in filters) {
248 bool active = false;
249 string uri = filter;
250- if (filter.has_prefix ("http-"))
251- uri = "http:" + filter.substring (6);
252- else if (filter.has_prefix ("file-"))
253- uri = "file:" + filter.substring (6);
254- else if (filter.has_prefix ("https-"))
255- uri = "https:" + filter.substring (7);
256+ if (filter.has_prefix ("http-/"))
257+ uri = "http:" + filter.substring (5);
258+ else if (filter.has_prefix ("file-/"))
259+ uri = "file:" + filter.substring (5);
260+ else if (filter.has_prefix ("http-:"))
261+ uri = "https" + filter.substring (5);
262 else
263 active = true;
264 Subscription sub = new Subscription (uri);
265 sub.active = active;
266 sub.add_feature (new Updater ());
267- sub.notify["active"].connect (active_changed);
268- subscriptions.append (sub);
269+ add (sub);
270 }
271+ enabled = !keyfile.get_boolean ("settings", "disabled");
272+ } catch (KeyFileError.KEY_NOT_FOUND key_error) {
273+ /* It's no error if a key is missing */
274+ } catch (KeyFileError.GROUP_NOT_FOUND group_error) {
275+ /* It's no error if a group is missing */
276 } catch (FileError.NOENT exist_error) {
277 /* It's no error if no config file exists */
278 } catch (GLib.Error settings_error) {
279 warning ("Error reading settings from %s: %s\n", filename, settings_error.message);
280 }
281
282- size = subscriptions.length ();
283+ notify["enabled"].connect (enabled_changed);
284+ }
285+
286+ void enabled_changed (ParamSpec pspec) {
287+ keyfile.set_boolean ("settings", "disabled", !enabled);
288+ save ();
289 }
290
291 void active_changed (Object subscription, ParamSpec pspec) {
292+ update_filters ();
293+ }
294+
295+ void update_filters () {
296 var filters = new StringBuilder ();
297 foreach (var sub in subscriptions) {
298- if (sub == custom)
299+ if (!sub.mutable)
300 continue;
301 if (sub.uri.has_prefix ("http:") && !sub.active)
302 filters.append ("http-" + sub.uri.substring (4));
303 else if (sub.uri.has_prefix ("file:") && !sub.active)
304- filters.append ("file-" + sub.uri.substring (4));
305+ filters.append ("file-" + sub.uri.substring (5));
306 else if (sub.uri.has_prefix ("https:") && !sub.active)
307- filters.append ("https-" + sub.uri.substring (5));
308+ filters.append ("http-" + sub.uri.substring (5));
309 else
310 filters.append (sub.uri);
311 filters.append_c (';');
312 }
313
314- string[] list = (filters.str.slice (0, -1)).split (";");
315+ if (filters.str.has_suffix (";"))
316+ filters.truncate (filters.len - 1);
317+ string[] list = filters.str.split (";");
318 keyfile.set_string_list ("settings", "filters", list);
319+
320+ save ();
321+ }
322+
323+
324+ public void save () {
325 try {
326- string filename = GLib.Path.build_filename (path, "config");
327- FileUtils.set_contents (filename, keyfile.to_data ());
328+ FileUtils.set_contents (path, keyfile.to_data ());
329 } catch (Error error) {
330 warning ("Failed to save settings: %s", error.message);
331 }
332 }
333
334- public void add_custom_rule (string rule) {
335- try {
336- var file = File.new_for_uri (custom.uri);
337- file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data);
338- } catch (Error error) {
339- warning ("Failed to add custom rule: %s", error.message);
340- }
341- }
342-
343 /* foreach support */
344 public new Subscription? get (uint index) {
345 return subscriptions.nth_data (index);
346 }
347 public uint size { get; private set; }
348+
349+ bool contains (Subscription subscription) {
350+ foreach (var sub in subscriptions)
351+ if (sub.uri == subscription.uri)
352+ return true;
353+ return false;
354+ }
355+
356+ public bool add (Subscription sub) {
357+ if (contains (sub))
358+ return false;
359+
360+ sub.notify["active"].connect (active_changed);
361+ subscriptions.append (sub);
362+ size++;
363+ if (should_save)
364+ update_filters ();
365+ return true;
366+ }
367+
368+ public void remove (Subscription sub) {
369+ if (!contains (sub))
370+ return;
371+
372+ subscriptions.remove (sub);
373+ sub.notify["active"].disconnect (active_changed);
374+ update_filters ();
375+ size--;
376+ }
377 }
378 }
379
380=== modified file 'extensions/adblock/extension.vala'
381--- extensions/adblock/extension.vala 2014-02-24 00:06:18 +0000
382+++ extensions/adblock/extension.vala 2014-03-10 10:27:25 +0000
383@@ -16,9 +16,42 @@
384 BLOCK
385 }
386
387+ public enum State {
388+ ENABLED,
389+ DISABLED,
390+ BLOCKED
391+ }
392+
393+ public string? parse_subscription_uri (string? uri) {
394+ if (uri == null)
395+ return null;
396+
397+ if (uri.has_prefix ("http") || uri.has_prefix ("abp") || uri.has_prefix ("file"))
398+ {
399+ string sub_uri = uri;
400+ if (uri.has_prefix ("abp:")) {
401+ uri.replace ("abp://", "abp:");
402+ if (uri.has_prefix ("abp:subscribe?location=")) {
403+ /* abp://subscripe?location=http://example.com&title=foo */
404+ string[] parts = uri.substring (23, -1).split ("&", 2);
405+ sub_uri = parts[0];
406+ }
407+ }
408+
409+ string decoded_uri = Soup.URI.decode (sub_uri);
410+ return decoded_uri;
411+ }
412+ return null;
413+ }
414+
415 public class Extension : Midori.Extension {
416- Config config;
417- HashTable<string, Directive?> cache;
418+ internal Config config;
419+ internal Subscription custom;
420+ internal HashTable<string, Directive?> cache;
421+ internal StatusIcon status_icon;
422+ internal SubscriptionManager manager;
423+ internal State state;
424+ internal bool debug_element;
425
426 #if HAVE_WEBKIT2
427 public Extension (WebKit.WebExtension web_extension) {
428@@ -44,99 +77,7 @@
429 }
430
431 void extension_preferences () {
432- open_dialog (null);
433- }
434-
435- void open_dialog (string? uri) {
436- var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"),
437- null,
438-#if !HAVE_GTK3
439- Gtk.DialogFlags.NO_SEPARATOR |
440-#endif
441- Gtk.DialogFlags.DESTROY_WITH_PARENT,
442- Gtk.STOCK_HELP, Gtk.ResponseType.HELP,
443- Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
444-#if HAVE_GTK3
445- dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button");
446-#endif
447- dialog.set_icon_name (Gtk.STOCK_PROPERTIES);
448- dialog.set_response_sensitive (Gtk.ResponseType.HELP, false);
449-
450- var hbox = new Gtk.HBox (false, 0);
451- (dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12);
452- var vbox = new Gtk.VBox (false, 0);
453- hbox.pack_start (vbox, true, true, 4);
454- var button = new Gtk.Label (null);
455- string description = """
456- Type the address of a preconfigured filter list in the text entry
457- and click "Add" to add it to the list.
458- You can find more lists at %s %s.
459- """.printf (
460- "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>",
461- "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>");
462- button.activate_link.connect ((uri)=>{
463- var browser = Midori.Browser.get_for_widget (button);
464- var view = browser.add_uri (uri);
465- browser.tab = view;
466- return true;
467- });
468- button.set_markup (description);
469- button.set_line_wrap (true);
470- vbox.pack_start (button, false, false, 4);
471-
472- var entry = new Gtk.Entry ();
473- if (uri != null)
474- entry.set_text (uri);
475- vbox.pack_start (entry, false, false, 4);
476-
477- var liststore = new Gtk.ListStore (1, typeof (Subscription));
478- var treeview = new Gtk.TreeView.with_model (liststore);
479- treeview.set_headers_visible (false);
480- var column = new Gtk.TreeViewColumn ();
481- var renderer_toggle = new Gtk.CellRendererToggle ();
482- column.pack_start (renderer_toggle, false);
483- column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => {
484- Subscription sub;
485- liststore.get (iter, 0, out sub);
486- renderer.set ("active", sub.active,
487- "sensitive", !sub.uri.has_suffix ("custom.list"));
488- });
489- renderer_toggle.toggled.connect ((path) => {
490- Gtk.TreeIter iter;
491- if (liststore.get_iter_from_string (out iter, path)) {
492- Subscription sub;
493- liststore.get (iter, 0, out sub);
494- sub.active = !sub.active;
495- }
496- });
497- treeview.append_column (column);
498-
499- column = new Gtk.TreeViewColumn ();
500- var renderer_text = new Gtk.CellRendererText ();
501- column.pack_start (renderer_text, false);
502- renderer_text.set ("editable", true);
503- // TODO: renderer_text.edited.connect
504- column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => {
505- Subscription sub;
506- liststore.get (iter, 0, out sub);
507- renderer.set ("text", sub.uri);
508- });
509- treeview.append_column (column);
510-
511- var scrolled = new Gtk.ScrolledWindow (null, null);
512- scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
513- scrolled.add (treeview);
514- vbox.pack_start (scrolled);
515-
516- foreach (Subscription sub in config)
517- liststore.insert_with_values (null, 0, 0, sub);
518- // TODO: row-inserted row-changed row-deleted
519- // TODO vbox with add/ edit/ remove/ down/ up
520-
521- dialog.get_content_area ().show_all ();
522-
523- dialog.response.connect ((response)=>{ dialog.destroy (); });
524- dialog.show ();
525+ manager.add_subscription (null);
526 }
527
528 void extension_activated (Midori.App app) {
529@@ -150,14 +91,24 @@
530 foreach (var tab in browser.get_tabs ())
531 tab_added (tab);
532 browser.add_tab.connect (tab_added);
533+
534+ var toggle_button = new StatusIcon.IconButton ();
535+ toggle_button.set_status (config.enabled ? "enabled" : "disabled");
536+ browser.statusbar.pack_start (toggle_button, false, false, 3);
537+ toggle_button.show ();
538+ toggle_button.clicked.connect (status_icon.icon_clicked);
539+ status_icon.toggle_buttons.append (toggle_button);
540 }
541
542+
543 void tab_added (Midori.View view) {
544 view.web_view.resource_request_starting.connect (resource_requested);
545 view.web_view.navigation_policy_decision_requested.connect (navigation_requested);
546 view.notify["load-status"].connect ((pspec) => {
547- if (view.load_status == Midori.LoadStatus.FINISHED)
548- inject_css (view, view.uri);
549+ if (config.enabled) {
550+ if (view.load_status == Midori.LoadStatus.FINISHED)
551+ inject_css (view, view.uri);
552+ }
553 });
554 view.context_menu.connect (context_menu);
555 }
556@@ -174,54 +125,31 @@
557 return;
558 var action = new Gtk.Action ("BlockElement", label, null, null);
559 action.activate.connect ((action) => {
560- edit_rule_dialog (uri);
561+ CustomRulesEditor custom_rules_editor = new CustomRulesEditor (custom);
562+ custom_rules_editor.set_uri (uri);
563+ custom_rules_editor.show();
564 });
565 menu.add (action);
566 }
567
568- void edit_rule_dialog (string uri) {
569- var dialog = new Gtk.Dialog.with_buttons (_("Edit rule"),
570- null,
571-#if !HAVE_GTK3
572- Gtk.DialogFlags.NO_SEPARATOR |
573-#endif
574- Gtk.DialogFlags.DESTROY_WITH_PARENT,
575- Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
576- Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT);
577- dialog.set_icon_name (Gtk.STOCK_ADD);
578- dialog.resizable = false;
579-
580- var hbox = new Gtk.HBox (false, 8);
581- var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
582- hbox.border_width = 5;
583- var label = new Gtk.Label.with_mnemonic (_("_Rule:"));
584- sizegroup.add_widget (label);
585- hbox.pack_start (label, false, false, 0);
586- (dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0);
587-
588- var entry = new Gtk.Entry ();
589- sizegroup.add_widget (entry);
590- entry.activates_default = true;
591- entry.set_text (uri);
592- hbox.pack_start (entry, true, true, 0);
593-
594- dialog.get_content_area ().show_all ();
595-
596- dialog.set_default_response (Gtk.ResponseType.ACCEPT);
597- if (dialog.run () != Gtk.ResponseType.ACCEPT)
598- return;
599-
600- string new_rule = entry.get_text ();
601- dialog.destroy ();
602- config.add_custom_rule (new_rule);
603+ Adblock.State adblock_get_state (Adblock.Directive directive)
604+ {
605+ if (directive == Directive.BLOCK)
606+ return State.BLOCKED;
607+ if (config.enabled)
608+ return State.ENABLED;
609+ else
610+ return State.DISABLED;
611 }
612
613-
614 void resource_requested (WebKit.WebView web_view, WebKit.WebFrame frame,
615 WebKit.WebResource resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) {
616
617- if (request_handled (web_view.uri, request.uri))
618+ if (request_handled (web_view.uri, request.uri)) {
619 request.set_uri ("about:blank");
620+ state = adblock_get_state (get_directive_for_uri (web_view.uri));
621+ status_icon.set_state (state);
622+ }
623 }
624
625 bool navigation_requested (WebKit.WebFrame frame, WebKit.NetworkRequest request,
626@@ -229,39 +157,28 @@
627
628 string uri = request.uri;
629 if (uri.has_prefix ("abp:")) {
630- uri = uri.replace ("abp://", "abp:");
631- if (uri.has_prefix ("abp:subscribe?location=")) {
632- /* abp://subscripe?location=http://example.com&title=foo */
633- string[] parts = uri.substring (23, -1).split ("&", 2);
634- decision.ignore ();
635- open_dialog (parts[0]);
636- return true;
637- }
638+ decision.ignore ();
639+ string parsed_uri = parse_subscription_uri (uri);
640+ manager.add_subscription (parsed_uri);
641+ return true;
642 }
643+ state = adblock_get_state (get_directive_for_uri (request.uri));
644+ status_icon.set_state (state);
645 return false;
646 }
647
648- void inject_css (Midori.View view, string page_uri) {
649- /* Don't block ads on internal pages */
650- if (!Midori.URI.is_http (page_uri))
651- return;
652- string domain = Midori.URI.parse_hostname (page_uri, null);
653- string[] subdomains = domain.split (".");
654- if (subdomains == null)
655- return;
656- int cnt = subdomains.length - 1;
657- var subdomain = new StringBuilder (subdomains[cnt]);
658- subdomain.prepend_c ('.');
659- cnt--;
660- var code = new StringBuilder ();
661- bool debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
662- string hider_css;
663-
664+ string? get_hider_css_for_blocked_resources () {
665 /* Hide elements that were blocked, otherwise we will get "broken image" icon */
666+ var code = new StringBuilder ();
667 cache.foreach ((key, val) => {
668 if (val == Adblock.Directive.BLOCK)
669 code.append ("img[src*=\"%s\"] , iframe[src*=\"%s\"] , ".printf (key, key));
670 });
671+
672+ if (code.str == "")
673+ return null;
674+
675+ string hider_css;
676 if (debug_element)
677 hider_css = " { background-color: red; border: 4px solid green; }";
678 else
679@@ -271,55 +188,96 @@
680 code.append (hider_css);
681 if (debug_element)
682 stdout.printf ("hider css: %s\n", code.str);
683- view.inject_stylesheet (code.str);
684+ return code.str;
685+ }
686
687- code.erase ();
688- int blockscnt = 0;
689+ string[]? get_domains_for_uri (string uri) {
690+ if (uri == null)
691+ return null;
692+ string[]? domains = null;
693+ string domain = Midori.URI.parse_hostname (uri, null);
694+ string[] subdomains = domain.split (".");
695+ if (subdomains == null)
696+ return null;
697+ int cnt = subdomains.length - 1;
698+ var subdomain = new StringBuilder (subdomains[cnt]);
699+ subdomain.prepend_c ('.');
700+ cnt--;
701 while (cnt >= 0) {
702 subdomain.prepend (subdomains[cnt]);
703- string? style = null;
704- foreach (Subscription sub in config) {
705- foreach (var feature in sub) {
706- if (feature is Adblock.Element) {
707- style = (feature as Adblock.Element).lookup (subdomain.str);
708- break;
709+ domains += subdomain.str;
710+ subdomain.prepend_c ('.');
711+ cnt--;
712+ }
713+ return domains;
714+ }
715+
716+ string? get_hider_css_rules_for_uri (string page_uri) {
717+ if (page_uri == null)
718+ return null;
719+ string[]? domains = get_domains_for_uri (page_uri);
720+ if (domains == null)
721+ return null;
722+ var code = new StringBuilder ();
723+ int blockscnt = 0;
724+ string? style = null;
725+ foreach (Subscription sub in config) {
726+ foreach (var feature in sub) {
727+ if (feature is Adblock.Element) {
728+ foreach (var subdomain in domains) {
729+ style = (feature as Adblock.Element).lookup (subdomain);
730+ if (style != null) {
731+ code.append (style);
732+ code.append_c (',');
733+ blockscnt++;
734+ }
735 }
736 }
737 }
738- if (style != null) {
739- code.append (style);
740- code.append_c (',');
741- blockscnt++;
742- }
743- subdomain.prepend_c ('.');
744- cnt--;
745 }
746+
747 if (blockscnt == 0)
748- return;
749+ return null;
750 code.truncate (code.len - 1);
751
752+ string hider_css;
753 if (debug_element)
754 hider_css = " { background-color: red !important; border: 4px solid green !important; }";
755 else
756 hider_css = " { display: none !important }";
757
758 code.append (hider_css);
759- view.inject_stylesheet (code.str);
760 if (debug_element)
761 stdout.printf ("css: %s\n", code.str);
762+
763+ return code.str;
764+ }
765+
766+ void inject_css (Midori.View view, string page_uri) {
767+ /* Don't block ads on internal pages */
768+ if (!Midori.URI.is_http (page_uri))
769+ return;
770+
771+ if ("adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? ""))
772+ debug_element = true;
773+ else
774+ debug_element = status_icon.debug_element_toggled;
775+
776+ string? blocked_css = get_hider_css_for_blocked_resources ();
777+ if (blocked_css != null)
778+ view.inject_stylesheet (blocked_css);
779+
780+ string? style = get_hider_css_rules_for_uri (page_uri);
781+ if (style != null)
782+ view.inject_stylesheet (style);
783 }
784 #endif
785
786 internal void init () {
787- debug ("Adblock2");
788-
789- string config_dir = Midori.Paths.get_extension_config_dir ("adblock");
790- config = new Config (config_dir);
791- reload_rules ();
792- }
793-
794- void reload_rules () {
795 cache = new HashTable<string, Directive?> (str_hash, str_equal);
796+ load_config ();
797+ status_icon = new StatusIcon (config);
798+ manager = new SubscriptionManager (config);
799 foreach (Subscription sub in config) {
800 try {
801 sub.parse ();
802@@ -327,24 +285,61 @@
803 warning ("Error parsing %s: %s", sub.uri, error.message);
804 }
805 }
806- }
807-
808- bool request_handled (string page_uri, string request_uri) {
809- /* Always allow the main page */
810- if (request_uri == page_uri)
811- return false;
812-
813- /* Skip adblock on internal pages */
814- if (Midori.URI.is_blank (page_uri))
815- return false;
816+ config.notify["size"].connect (subscriptions_added_removed);
817+ manager.description_label.activate_link.connect (open_link);
818+ }
819+
820+ bool open_link (string uri) {
821+ var browser = get_app ().browser;
822+ var view = browser.add_uri (uri);
823+ browser.tab = view;
824+ return true;
825+ }
826+
827+ void subscriptions_added_removed (ParamSpec pspec) {
828+ cache.remove_all ();
829+ }
830+
831+ void load_config () {
832+ string config_dir = Midori.Paths.get_extension_config_dir ("adblock");
833+ string presets = Midori.Paths.get_extension_preset_filename ("adblock", "config");
834+ string filename = Path.build_filename (config_dir, "config");
835+ config = new Config (filename, presets);
836+ string custom_list = GLib.Path.build_filename (config_dir, "custom.list");
837+ try {
838+ custom = new Subscription (Filename.to_uri (custom_list, null));
839+ custom.mutable = false;
840+ custom.title = _("Custom");
841+ config.add (custom);
842+ } catch (Error error) {
843+ custom = null;
844+ warning ("Failed to add custom list %s: %s", custom_list, error.message);
845+ }
846+ }
847+
848+ public Adblock.Directive get_directive_for_uri (string request_uri, string? page_uri = null) {
849+ if (!config.enabled)
850+ return Directive.ALLOW;
851+
852+ if (page_uri != null) {
853+ /* Always allow the main page */
854+ if (request_uri == page_uri)
855+ return Directive.ALLOW;
856+
857+ /* Skip adblock on internal pages */
858+ if (Midori.URI.is_blank (page_uri))
859+ return Directive.ALLOW;
860+ }
861
862 /* Skip adblock on favicons and non http schemes */
863 if (!Midori.URI.is_http (request_uri) || request_uri.has_suffix ("favicon.ico"))
864- return false;
865+ return Directive.ALLOW;
866
867 Directive? directive = cache.lookup (request_uri);
868 if (directive == null) {
869 foreach (Subscription sub in config) {
870+ if (page_uri == null)
871+ page_uri = request_uri;
872 directive = sub.get_directive (request_uri, page_uri);
873 if (directive != null)
874 break;
875@@ -352,8 +347,14 @@
876 if (directive == null)
877 directive = Directive.ALLOW;
878 cache.insert (request_uri, directive);
879+ if (directive == Directive.BLOCK)
880+ cache.insert (page_uri, directive);
881 }
882- return directive == Directive.BLOCK;
883+ return directive;
884+ }
885+
886+ internal bool request_handled (string page_uri, string request_uri) {
887+ return get_directive_for_uri (request_uri, page_uri) == Directive.BLOCK;
888 }
889 }
890
891@@ -411,6 +412,179 @@
892 #endif
893
894 #if !HAVE_WEBKIT2
895+static string? tmp_folder = null;
896+string get_test_file (string contents) {
897+ if (tmp_folder == null)
898+ tmp_folder = Midori.Paths.make_tmp_dir ("adblockXXXXXX");
899+ string checksum = Checksum.compute_for_string (ChecksumType.MD5, contents);
900+ string file = Path.build_path (Path.DIR_SEPARATOR_S, tmp_folder, checksum);
901+ try {
902+ FileUtils.set_contents (file, contents, -1);
903+ } catch (Error file_error) {
904+ GLib.error (file_error.message);
905+ }
906+ return file;
907+}
908+
909+struct TestCaseConfig {
910+ public string content;
911+ public uint size;
912+ public bool enabled;
913+}
914+
915+const TestCaseConfig[] configs = {
916+ { "", 0, true },
917+ { "[settings]", 0, true },
918+ { "[settings]\nfilters=foo;", 1, true },
919+ { "[settings]\nfilters=foo;\ndisabled=true", 1, false }
920+};
921+
922+void test_adblock_config () {
923+ assert (new Adblock.Config (null, null).size == 0);
924+
925+ foreach (var conf in configs) {
926+ var config = new Adblock.Config (get_test_file (conf.content), null);
927+ if (config.size != conf.size)
928+ error ("Wrong size %s rather than %s:\n%s",
929+ config.size.to_string (), conf.size.to_string (), conf.content);
930+ if (config.enabled != conf.enabled)
931+ error ("Wrongly got enabled=%s rather than %s:\n%s",
932+ config.enabled.to_string (), conf.enabled.to_string (), conf.content);
933+ }
934+}
935+
936+struct TestCaseSub {
937+ public string uri;
938+ public bool active;
939+}
940+
941+const TestCaseSub[] subs = {
942+ { "http://foo.com", true },
943+ { "http://bar.com", false },
944+ { "https://spam.com", true },
945+ { "https://eggs.com", false },
946+ { "file:///bla", true },
947+ { "file:///blub", false }
948+};
949+
950+void test_adblock_subs () {
951+ var config = new Adblock.Config (get_test_file ("""
952+[settings]
953+filters=http://foo.com;http-//bar.com;https://spam.com;http-://eggs.com;file:///bla;file-///blub;http://foo.com;
954+"""), null);
955+
956+ assert (config.enabled);
957+ foreach (var sub in subs) {
958+ bool found = false;
959+ foreach (var subscription in config) {
960+ if (subscription.uri == sub.uri) {
961+ assert (subscription.active == sub.active);
962+ found = true;
963+ }
964+ }
965+ if (!found)
966+ error ("%s not found", sub.uri);
967+ }
968+
969+ /* 6 unique URLs, 1 duplicate */
970+ assert (config.size == 6);
971+ /* Duplicates aren't added again either */
972+ assert (!config.add (new Adblock.Subscription ("https://spam.com")));
973+
974+ /* Saving the config and loading it should give back identical results */
975+ config.save ();
976+ var copy = new Adblock.Config (config.path, null);
977+ assert (copy.size == config.size);
978+ assert (copy.enabled == config.enabled);
979+ for (int i = 0; i < config.size; i++) {
980+ assert (copy[i].uri == config[i].uri);
981+ assert (copy[i].active == config[i].active);
982+ }
983+ /* Enabled status should be saved and loaded */
984+ config.enabled = false;
985+ copy = new Adblock.Config (config.path, null);
986+ assert (copy.enabled == config.enabled);
987+ /* Flipping individual active values should be retained after saving */
988+ foreach (var sub in config)
989+ sub.active = !sub.active;
990+ copy = new Adblock.Config (config.path, null);
991+ for (uint i = 0; i < config.size; i++) {
992+ if (config[i].active != copy[i].active) {
993+ string contents;
994+ try {
995+ FileUtils.get_contents (config.path, out contents, null);
996+ } catch (Error file_error) {
997+ error (file_error.message);
998+ }
999+ error ("%s is %s but should be %s:\n%s",
1000+ copy[i].uri, copy[i].active ? "active" : "disabled", config[i].active ? "active" : "disabled", contents);
1001+ }
1002+ }
1003+
1004+ /* Adding and removing works, changes size */
1005+ var s = new Adblock.Subscription ("http://en.de");
1006+ assert (config.add (s));
1007+ assert (config.size == 7);
1008+ config.remove (s);
1009+ assert (config.size == 6);
1010+ /* If it was removed before we should be able to add it again */
1011+ assert (config.add (s));
1012+ assert (config.size == 7);
1013+}
1014+
1015+void test_adblock_init () {
1016+ /* No config */
1017+ var extension = new Adblock.Extension ();
1018+ extension.init ();
1019+ assert (extension.config.enabled);
1020+ /* Defaults plus custom */
1021+ if (extension.config.size != 3)
1022+ error ("Expected 3 initial subs, got %s".printf (
1023+ extension.config.size.to_string ()));
1024+ assert (extension.cache.size () == 0);
1025+
1026+ /* Add new subscription */
1027+ string path = Midori.Paths.get_res_filename ("adblock.list");
1028+ string uri;
1029+ try {
1030+ uri = Filename.to_uri (path, null);
1031+ } catch (Error error) {
1032+ GLib.error (error.message);
1033+ }
1034+ var sub = new Adblock.Subscription (uri);
1035+ extension.config.add (sub);
1036+ assert (extension.cache.size () == 0);
1037+ assert (extension.config.size == 4);
1038+ try {
1039+ sub.parse ();
1040+ } catch (GLib.Error error) {
1041+ GLib.error (error.message);
1042+ }
1043+ /* The page itself never hits */
1044+ assert (!extension.request_handled ("https://ads.bogus.name/blub", "https://ads.bogus.name/blub"));
1045+ /* Favicons don't either */
1046+ assert (!extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub/favicon.ico"));
1047+ assert (extension.cache.size () == 0);
1048+ /* Some sanity checks to be sure there's no earlier problem */
1049+ assert (sub.title == "Exercise");
1050+ assert (sub.get_directive ("https://ads.bogus.name/blub", "") == Adblock.Directive.BLOCK);
1051+ /* A rule hit should add to the cache */
1052+ assert (extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub"));
1053+ assert (extension.cache.size () > 0);
1054+ /* Disabled means no request should be handled */
1055+ extension.config.enabled = false;
1056+ assert (!extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub"));
1057+ /* Removing a subscription should clear the cache */
1058+ extension.config.remove (sub);
1059+ assert (extension.cache.size () == 0);
1060+ assert (extension.config.size == 3);
1061+ /* Now let's add a custom rule */
1062+ extension.config.enabled = true;
1063+ extension.custom.add_rule ("*.png");
1064+ assert (!extension.request_handled ("https://foo.com", "http://alpha.beta.com/images/yota.png"));
1065+ assert (extension.cache.size () > 0);
1066+ }
1067+
1068 struct TestCaseLine {
1069 public string line;
1070 public string fixed;
1071@@ -499,19 +673,27 @@
1072 }
1073 }
1074
1075+string pretty_date (DateTime? date) {
1076+ if (date == null)
1077+ return "N/A";
1078+ return date.to_string ();
1079+}
1080+
1081 struct TestUpdateExample {
1082 public string content;
1083 public bool result;
1084+ public bool valid;
1085 }
1086
1087 const TestUpdateExample[] examples = {
1088- { "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true },
1089- { "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true },
1090- { "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false },
1091- { "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false },
1092- { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true },
1093- { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true },
1094- { "[Adblock]\n ! dummy, i dont have any dates\n", false }
1095+ { "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true, true },
1096+ { "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true, true },
1097+ { "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false, true },
1098+ { "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false, true },
1099+ { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true, true },
1100+ { "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true, true },
1101+ { "[Adblock]\n ! dummy, i dont have any dates\n", false, true },
1102+ { "\n", false, false }
1103 };
1104
1105 void test_subscription_update () {
1106@@ -531,23 +713,52 @@
1107 foreach (var example in examples) {
1108 try {
1109 file.replace_contents (example.content.data, null, false, FileCreateFlags.NONE, null);
1110- updater.last_mod_meta = null;
1111- updater.expires_meta = null;
1112+ sub.clear ();
1113 sub.parse ();
1114 } catch (Error error) {
1115 GLib.error (error.message);
1116 }
1117- if (example.result == true)
1118- assert (updater.needs_updating());
1119- else
1120- assert (!updater.needs_updating());
1121+ if (example.valid != sub.valid)
1122+ error ("Subscription expected to be %svalid but %svalid:\n%s",
1123+ example.valid ? "" : "in", sub.valid ? "" : "in", example.content);
1124+ if (example.result != updater.needs_update)
1125+ error ("Update%s expected for:\n%s\nLast Updated: %s\nExpires: %s",
1126+ example.result ? "" : " not", example.content,
1127+ pretty_date (updater.last_updated), pretty_date (updater.expires));
1128+ }
1129+}
1130+
1131+struct TestSubUri {
1132+ public string? src_uri;
1133+ public string? dst_uri;
1134+}
1135+
1136+const TestSubUri[] suburis =
1137+{
1138+ { null, null },
1139+ { "not-a-link", null },
1140+ { "http://some.uri", "http://some.uri" },
1141+ { "abp:subscribe?location=https%3A%2F%2Feasylist-downloads.adblockplus.org%2Fabpindo%2Beasylist.txt&title=ABPindo%2BEasyList", "https://easylist-downloads.adblockplus.org/abpindo+easylist.txt" }
1142+};
1143+
1144+void test_subscription_uri_parsing () {
1145+ string? parsed_uri;
1146+ foreach (var example in suburis) {
1147+ parsed_uri = Adblock.parse_subscription_uri (example.src_uri);
1148+ if (parsed_uri != example.dst_uri)
1149+ error ("Subscription expected to be %svalid but %svalid:\n%s",
1150+ example.dst_uri, parsed_uri, example.src_uri);
1151 }
1152 }
1153
1154 public void extension_test () {
1155+ Test.add_func ("/extensions/adblock2/config", test_adblock_config);
1156+ Test.add_func ("/extensions/adblock2/subs", test_adblock_subs);
1157+ Test.add_func ("/extensions/adblock2/init", test_adblock_init);
1158 Test.add_func ("/extensions/adblock2/parse", test_adblock_fixup_regexp);
1159 Test.add_func ("/extensions/adblock2/pattern", test_adblock_pattern);
1160 Test.add_func ("/extensions/adblock2/update", test_subscription_update);
1161+ Test.add_func ("/extensions/adblock2/subsparse", test_subscription_uri_parsing);
1162 }
1163 #endif
1164
1165
1166=== modified file 'extensions/adblock/subscriptions.vala'
1167--- extensions/adblock/subscriptions.vala 2014-02-20 18:27:01 +0000
1168+++ extensions/adblock/subscriptions.vala 2014-03-10 10:27:25 +0000
1169@@ -15,6 +15,9 @@
1170 public virtual bool header (string key, string value) {
1171 return false;
1172 }
1173+ public virtual bool parsed (File file) {
1174+ return true;
1175+ }
1176 public virtual Directive? match (string request_uri, string page_uri) throws Error {
1177 return null;
1178 }
1179@@ -24,8 +27,12 @@
1180
1181 public class Subscription : GLib.Object {
1182 public string? path;
1183+ bool debug_parse;
1184 public string uri { get; set; default = null; }
1185+ public string title { get; set; default = null; }
1186 public bool active { get; set; default = true; }
1187+ public bool mutable { get; set; default = true; }
1188+ public bool valid { get; private set; default = true; }
1189 List<Feature> features;
1190 public Pattern pattern;
1191 public Keys keys;
1192@@ -35,6 +42,8 @@
1193 WebKit.Download? download;
1194
1195 public Subscription (string uri) {
1196+ debug_parse = "adblock:parse" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
1197+
1198 this.uri = uri;
1199
1200 this.optslist = new Options ();
1201@@ -172,7 +181,8 @@
1202 return;
1203
1204 string format_patt = fixup_regex (prefix, patt);
1205- debug ("got: %s opts %s", format_patt, opts);
1206+ if (debug_parse)
1207+ stdout.printf ("got: %s opts %s\n", format_patt, opts);
1208 compile_regexp (format_patt, opts);
1209 /* return format_patt */
1210 }
1211@@ -186,7 +196,8 @@
1212 if (Regex.match_simple ("^/.*[\\^\\$\\*].*/$", patt,
1213 RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY)
1214 || opts != null && opts.contains ("whitelist")) {
1215- debug ("patt: %s", patt);
1216+ if (debug_parse)
1217+ stdout.printf ("patt: %s\n", patt);
1218 if (opts.contains ("whitelist"))
1219 this.whitelist.insert (patt, regex);
1220 else
1221@@ -233,12 +244,15 @@
1222 string value = "";
1223 if (header.contains (":")) {
1224 string[] parts = header.split (":", 2);
1225- if (parts[0] != null) {
1226+ if (parts[0] != null && parts[0] != ""
1227+ && parts[1] != null && parts[1] != "") {
1228 key = parts[0].substring (2, -1);
1229 value = parts[1].substring (1, -1);
1230 }
1231 }
1232 debug ("Header '%s' says '%s'", key, value);
1233+ if (key == "Title")
1234+ title = value;
1235 foreach (var feature in features) {
1236 if (feature.header (key, value))
1237 break;
1238@@ -283,18 +297,25 @@
1239 #if HAVE_WEBKIT2
1240 /* TODO */
1241 #else
1242- if (download != null)
1243- return;
1244+ /* Don't bother trying to download local files */
1245+ if (!uri.has_prefix ("file://")) {
1246+ if (download != null)
1247+ return;
1248
1249- download = new WebKit.Download (new WebKit.NetworkRequest (uri));
1250- download.destination_uri = Filename.to_uri (path, null);
1251- download.notify["status"].connect (download_status);
1252- debug ("Fetching %s to %s now", uri, download.destination_uri);
1253- download.start ();
1254+ string destination_uri = Filename.to_uri (path, null);
1255+ debug ("Fetching %s to %s now", uri, destination_uri);
1256+ download = new WebKit.Download (new WebKit.NetworkRequest (uri));
1257+ if (!Midori.Download.has_enough_space (download, destination_uri, true))
1258+ throw new FileError.EXIST ("Can't download to \"%s\"", path);
1259+ download.destination_uri = destination_uri;
1260+ download.notify["status"].connect (download_status);
1261+ download.start ();
1262 #endif
1263+ }
1264 return;
1265 }
1266
1267+ valid = false;
1268 string? line;
1269 while ((line = stream.read_line (null)) != null) {
1270 if (line == null)
1271@@ -306,6 +327,13 @@
1272 parse_header (chomped);
1273 else
1274 parse_line (chomped);
1275+ /* The file isn't completely empty */
1276+ valid = true;
1277+ }
1278+
1279+ foreach (var feature in features) {
1280+ if (!feature.parsed (filter_file))
1281+ valid = false;
1282 }
1283 }
1284
1285@@ -324,5 +352,15 @@
1286 }
1287 return null;
1288 }
1289+
1290+ public void add_rule (string rule) {
1291+ try {
1292+ var file = File.new_for_uri (uri);
1293+ file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data);
1294+ parse ();
1295+ } catch (Error error) {
1296+ warning ("Failed to add custom rule: %s", error.message);
1297+ }
1298+ }
1299 }
1300 }
1301
1302=== modified file 'extensions/adblock/updater.vala'
1303--- extensions/adblock/updater.vala 2014-02-20 00:14:15 +0000
1304+++ extensions/adblock/updater.vala 2014-03-10 10:27:25 +0000
1305@@ -12,15 +12,23 @@
1306
1307 namespace Adblock {
1308 public class Updater : Feature {
1309- public string expires_meta { get; set; default = null; }
1310- public string last_mod_meta { get; set; default = null; }
1311- public int64 update_tstamp { get; set; default = 0; }
1312- public int64 last_mod_tstamp { get; set; default = 0; }
1313- public int64 last_check_tstamp { get; set; default = 0; }
1314+ string expires_meta;
1315+ string last_mod_meta;
1316+ public DateTime last_updated { get; set; }
1317+ public DateTime expires { get; set; }
1318+ public bool needs_update { get; set; }
1319
1320 public Updater () {
1321 }
1322
1323+ public override void clear () {
1324+ expires_meta = null;
1325+ last_mod_meta = null;
1326+ last_updated = null;
1327+ expires = null;
1328+ needs_update = false;
1329+ }
1330+
1331 public override bool header (string key, string value) {
1332 if (key.has_prefix ("Last mod") || key == "Updated") {
1333 last_mod_meta = value;
1334@@ -37,6 +45,12 @@
1335 return false;
1336 }
1337
1338+ public override bool parsed (File file) {
1339+ process_dates (file);
1340+ /* It's not an error to have no update headers, we go for defaults */
1341+ return true;
1342+ }
1343+
1344 int get_month_from_string (string? month) {
1345 if (month == null)
1346 return 0;
1347@@ -50,19 +64,17 @@
1348 return 0;
1349 }
1350
1351- public bool needs_updating () {
1352+ void process_dates (File file) {
1353 DateTime now = new DateTime.now_local ();
1354- DateTime expire_date = null;
1355- DateTime last_mod_date = null;
1356- string? last_mod = last_mod_meta;
1357- string? expires = expires_meta;
1358+ last_updated = null;
1359+ expires = null;
1360
1361 /* We have "last modification" metadata */
1362- if (last_mod != null) {
1363+ if (last_mod_meta != null) {
1364 int h = 0, min = 0, d, m, y;
1365 /* Date in a form of: 20.08.2012 12:34 */
1366- if (last_mod.contains (".") || last_mod.contains("-")) {
1367- string[] parts = last_mod.split (" ", 2);
1368+ if (last_mod_meta.contains (".") || last_mod_meta.contains("-")) {
1369+ string[] parts = last_mod_meta.split (" ", 2);
1370 string[] date_parts;
1371 string split_char = " ";
1372
1373@@ -89,7 +101,7 @@
1374 d = int.parse(date_parts[2]);
1375 }
1376 } else { /* Date in a form of: 20 Mar 2012 12:34 */
1377- string[] parts = last_mod.split (" ", 4);
1378+ string[] parts = last_mod_meta.split (" ", 4);
1379 /* contains time part ? */
1380 if (parts[3] != null && parts[3].contains (":")) {
1381 string[] time_parts = parts[3].split (":", 2);
1382@@ -107,33 +119,37 @@
1383 }
1384 }
1385
1386- last_mod_date = new DateTime.local (y, m, d, h, min, 0.0);
1387+ last_updated = new DateTime.local (y, m, d, h, min, 0.0);
1388+ } else {
1389+ /* FIXME: use file modification date if there's no update header
1390+ try {
1391+ string modified = FileAttribute.TIME_MODIFIED;
1392+ var info = file.query_filesystem_info (modified);
1393+ last_updated = new DateTime.from_timeval_local (info.get_modification_time ());
1394+ } catch (Error error) {
1395+ last_updated = now;
1396+ }
1397+ */
1398+ last_updated = now;
1399 }
1400
1401- if (last_mod_date == null)
1402- last_mod_date = now;
1403-
1404 /* We have "expires" metadata */
1405- if (expires != null) {
1406- if (expires.contains ("days")) {
1407- string[] parts = expires.split (" ");
1408- expire_date = last_mod_date.add_days (int.parse (parts[0]));
1409- } else if (expires.contains ("hours")) {
1410- string[] parts = expires.split (" ");
1411- expire_date = last_mod_date.add_hours (int.parse (parts[0]));
1412+ if (expires_meta != null) {
1413+ if (expires_meta.contains ("days")) {
1414+ string[] parts = expires_meta.split (" ");
1415+ expires = last_updated.add_days (int.parse (parts[0]));
1416+ } else if (expires_meta.contains ("hours")) {
1417+ string[] parts = expires_meta.split (" ");
1418+ expires = last_updated.add_hours (int.parse (parts[0]));
1419 }
1420 } else {
1421 /* No expire metadata found, assume x days */
1422 int days_to_expire = 7;
1423- expire_date = last_mod_date.add_days (days_to_expire);
1424+ expires = last_updated.add_days (days_to_expire);
1425 }
1426
1427- last_mod_tstamp = last_mod_date.to_unix ();
1428- last_check_tstamp = now.to_unix ();
1429- update_tstamp = expire_date.to_unix ();
1430-
1431 /* Check if we are past expire date */
1432- return now.compare (expire_date) == 1;
1433+ needs_update = now.compare (expires) == 1;
1434 }
1435 }
1436 }
1437
1438=== added file 'extensions/adblock/widgets.vala'
1439--- extensions/adblock/widgets.vala 1970-01-01 00:00:00 +0000
1440+++ extensions/adblock/widgets.vala 2014-03-10 10:27:25 +0000
1441@@ -0,0 +1,310 @@
1442+/*
1443+ Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
1444+ Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
1445+
1446+ This library is free software; you can redistribute it and/or
1447+ modify it under the terms of the GNU Lesser General Public
1448+ License as published by the Free Software Foundation; either
1449+ version 2.1 of the License, or (at your option) any later version.
1450+
1451+ See the file COPYING for the full license text.
1452+*/
1453+
1454+namespace Adblock {
1455+
1456+
1457+ public class StatusIcon {
1458+ Config config;
1459+ State state;
1460+ public bool debug_element_toggled;
1461+ public List<IconButton> toggle_buttons;
1462+
1463+ public StatusIcon (Adblock.Config config) {
1464+ this.config = config;
1465+ this.debug_element_toggled = false;
1466+ }
1467+
1468+ public void set_state (Adblock.State state) {
1469+ this.state = state;
1470+ update_buttons ();
1471+ }
1472+
1473+ public class IconButton : Gtk.Button {
1474+ Gtk.Image icon;
1475+
1476+ public IconButton () {
1477+ icon = new Gtk.Image ();
1478+ add (icon);
1479+ icon.show ();
1480+ }
1481+
1482+ public void set_status (string status) {
1483+ string filename = Midori.Paths.get_res_filename ("adblock/%s.svg".printf (status));
1484+ icon.set_from_file (filename);
1485+ }
1486+ }
1487+
1488+ public void update_buttons () {
1489+ string state = "";
1490+ foreach (var toggle_button in toggle_buttons) {
1491+ if (this.state == State.BLOCKED) {
1492+ toggle_button.set_status ("blocked");
1493+ state = _("Blocking");
1494+ }
1495+ if (this.state == State.ENABLED) {
1496+ toggle_button.set_status ("enabled");
1497+ state = _("Enabled");
1498+ }
1499+ if (this.state == State.DISABLED) {
1500+ toggle_button.set_status ("disabled");
1501+ state = _("Disabled");
1502+ }
1503+ toggle_button.set_tooltip_text (_("Adblock state: %s").printf (state));
1504+ }
1505+ }
1506+
1507+ public void icon_clicked (Gtk.Button toggle_button) {
1508+ var menu = new Gtk.Menu ();
1509+ var checkitem = new Gtk.CheckMenuItem.with_label (_("Disabled"));
1510+ checkitem.set_active (!config.enabled);
1511+ checkitem.toggled.connect (() => {
1512+ config.enabled = !checkitem.active;
1513+ set_state (config.enabled ? Adblock.State.ENABLED : Adblock.State.DISABLED);
1514+ });
1515+ menu.append (checkitem);
1516+
1517+ var hideritem = new Gtk.CheckMenuItem.with_label (_("Display hidden elements"));
1518+ hideritem.set_active (debug_element_toggled);
1519+ hideritem.toggled.connect (() => {
1520+ this.debug_element_toggled = hideritem.active;
1521+ });
1522+ menu.append (hideritem);
1523+
1524+ var menuitem = new Gtk.ImageMenuItem.with_label (_("Preferences"));
1525+ var image = new Gtk.Image.from_stock (Gtk.STOCK_PREFERENCES, Gtk.IconSize.MENU);
1526+ menuitem.always_show_image = true;
1527+ menuitem.set_image (image);
1528+ menuitem.activate.connect (() => {
1529+ SubscriptionManager manager = new SubscriptionManager (config);
1530+ manager.add_subscription (null);
1531+ });
1532+ menu.append (menuitem);
1533+
1534+ menu.show_all ();
1535+ Katze.widget_popup (toggle_button, menu, null, Katze.MenuPos.CURSOR);
1536+ }
1537+ }
1538+
1539+ public class SubscriptionManager {
1540+ Gtk.TreeView treeview;
1541+ Gtk.ListStore liststore;
1542+ Adblock.Config config;
1543+ public Gtk.Label description_label;
1544+ string description;
1545+
1546+ public SubscriptionManager (Config config) {
1547+ this.config = config;
1548+ this.liststore = new Gtk.ListStore (1, typeof (Subscription));
1549+ this.description_label = new Gtk.Label (null);
1550+ this.description = _("Type the address of a preconfigured filter list in the text entry and hit Enter.\n");
1551+ this.description += _("You can find more lists by visiting following sites:\n %s, %s\n".printf (
1552+ "<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>",
1553+ "<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>"
1554+ ));
1555+ }
1556+
1557+ public void add_subscription (string? uri) {
1558+ var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"),
1559+ null,
1560+#if !HAVE_GTK3
1561+ Gtk.DialogFlags.NO_SEPARATOR |
1562+#endif
1563+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
1564+ Gtk.STOCK_HELP, Gtk.ResponseType.HELP,
1565+ Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
1566+#if HAVE_GTK3
1567+ dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button");
1568+#endif
1569+ dialog.set_icon_name (Gtk.STOCK_PROPERTIES);
1570+ dialog.set_response_sensitive (Gtk.ResponseType.HELP, false);
1571+
1572+ var hbox = new Gtk.HBox (false, 0);
1573+ (dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12);
1574+ var vbox = new Gtk.VBox (false, 0);
1575+ hbox.pack_start (vbox, true, true, 4);
1576+ this.description_label.set_markup (this.description);
1577+ this.description_label.set_line_wrap (true);
1578+ vbox.pack_start (this.description_label, false, false, 4);
1579+
1580+ var entry = new Gtk.Entry ();
1581+ if (uri != null)
1582+ entry.set_text (uri);
1583+ vbox.pack_start (entry, false, false, 4);
1584+
1585+ liststore = new Gtk.ListStore (1, typeof (Subscription));
1586+ treeview = new Gtk.TreeView.with_model (liststore);
1587+ treeview.set_headers_visible (false);
1588+ var column = new Gtk.TreeViewColumn ();
1589+ var renderer_toggle = new Gtk.CellRendererToggle ();
1590+ column.pack_start (renderer_toggle, false);
1591+ column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => {
1592+ Subscription sub;
1593+ liststore.get (iter, 0, out sub);
1594+ renderer.set ("active", sub.active,
1595+ "sensitive", sub.mutable);
1596+ });
1597+ renderer_toggle.toggled.connect ((path) => {
1598+ Gtk.TreeIter iter;
1599+ if (liststore.get_iter_from_string (out iter, path)) {
1600+ Subscription sub;
1601+ liststore.get (iter, 0, out sub);
1602+ sub.active = !sub.active;
1603+ }
1604+ });
1605+ treeview.append_column (column);
1606+
1607+ column = new Gtk.TreeViewColumn ();
1608+ var renderer_text = new Gtk.CellRendererText ();
1609+ column.pack_start (renderer_text, false);
1610+ renderer_text.set ("editable", true);
1611+ // TODO: renderer_text.edited.connect
1612+ column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => {
1613+ Subscription sub;
1614+ liststore.get (iter, 0, out sub);
1615+ string status = "";
1616+ foreach (var feature in sub) {
1617+ if (feature is Adblock.Updater) {
1618+ var updater = feature as Adblock.Updater;
1619+ if (updater.last_updated != null)
1620+ status = updater.last_updated.format (_("Last update: %x %X"));
1621+ }
1622+ }
1623+ if (!sub.valid)
1624+ status = _("File incomplete - broken download?");
1625+ renderer.set ("markup", (Markup.printf_escaped ("<b>%s</b>\n%s",
1626+ sub.title ?? sub.uri, status)));
1627+ });
1628+ treeview.append_column (column);
1629+
1630+ column = new Gtk.TreeViewColumn ();
1631+ Gtk.CellRendererPixbuf renderer_button = new Gtk.CellRendererPixbuf ();
1632+ column.pack_start (renderer_button, false);
1633+ column.set_cell_data_func (renderer_button, on_render_button);
1634+ treeview.append_column (column);
1635+
1636+ var scrolled = new Gtk.ScrolledWindow (null, null);
1637+ scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
1638+ scrolled.add (treeview);
1639+ vbox.pack_start (scrolled);
1640+
1641+ foreach (Subscription sub in config)
1642+ liststore.insert_with_values (null, 0, 0, sub);
1643+ treeview.button_release_event.connect (button_released);
1644+
1645+ entry.activate.connect (() => {
1646+ string? parsed_uri = Adblock.parse_subscription_uri (entry.text);
1647+ if (parsed_uri != null) {
1648+ var sub = new Subscription (parsed_uri);
1649+ if (config.add (sub)) {
1650+ liststore.insert_with_values (null, 0, 0, sub);
1651+ try {
1652+ sub.parse ();
1653+ } catch (GLib.Error error) {
1654+ warning ("Error parsing %s: %s", sub.uri, error.message);
1655+ }
1656+ }
1657+ }
1658+ entry.text = "";
1659+ });
1660+
1661+ dialog.get_content_area ().show_all ();
1662+
1663+ dialog.response.connect ((response)=>{ dialog.destroy (); });
1664+ dialog.show ();
1665+ }
1666+
1667+ void on_render_button (Gtk.CellLayout column, Gtk.CellRenderer renderer,
1668+ Gtk.TreeModel model, Gtk.TreeIter iter) {
1669+
1670+ Subscription sub;
1671+ liststore.get (iter, 0, out sub);
1672+
1673+ renderer.set ("stock-id", sub.mutable ? Gtk.STOCK_DELETE : null,
1674+ "stock-size", Gtk.IconSize.MENU);
1675+ }
1676+
1677+ public bool button_released (Gdk.EventButton event) {
1678+ Gtk.TreePath? path;
1679+ Gtk.TreeViewColumn column;
1680+ if (treeview.get_path_at_pos ((int)event.x, (int)event.y, out path, out column, null, null)) {
1681+ if (path != null) {
1682+ if (column == treeview.get_column (2)) {
1683+ Gtk.TreeIter iter;
1684+ if (liststore.get_iter (out iter, path)) {
1685+ Subscription sub;
1686+ liststore.get (iter, 0, out sub);
1687+ if (sub.mutable) {
1688+ config.remove (sub);
1689+ liststore.remove (iter);
1690+ return true;
1691+ }
1692+ }
1693+ }
1694+ }
1695+ }
1696+ return false;
1697+ }
1698+ }
1699+
1700+ class CustomRulesEditor {
1701+ Gtk.Dialog dialog;
1702+ Subscription custom;
1703+ public string? rule { get; set; }
1704+
1705+ public CustomRulesEditor (Subscription custom) {
1706+ this.custom = custom;
1707+ }
1708+
1709+ public void set_uri (string uri) {
1710+ this.rule = uri;
1711+ }
1712+
1713+ public void show () {
1714+ this.dialog = new Gtk.Dialog.with_buttons (_("Edit rule"),
1715+ null,
1716+#if !HAVE_GTK3
1717+ Gtk.DialogFlags.NO_SEPARATOR |
1718+#endif
1719+ Gtk.DialogFlags.DESTROY_WITH_PARENT,
1720+ Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
1721+ Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT);
1722+ dialog.set_icon_name (Gtk.STOCK_ADD);
1723+ dialog.resizable = false;
1724+
1725+ var hbox = new Gtk.HBox (false, 8);
1726+ var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
1727+ hbox.border_width = 5;
1728+ var label = new Gtk.Label.with_mnemonic (_("_Rule:"));
1729+ sizegroup.add_widget (label);
1730+ hbox.pack_start (label, false, false, 0);
1731+ (dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0);
1732+
1733+ var entry = new Gtk.Entry ();
1734+ sizegroup.add_widget (entry);
1735+ entry.activates_default = true;
1736+ entry.set_text (this.rule);
1737+ hbox.pack_start (entry, true, true, 0);
1738+
1739+ dialog.get_content_area ().show_all ();
1740+
1741+ dialog.set_default_response (Gtk.ResponseType.ACCEPT);
1742+ if (dialog.run () != Gtk.ResponseType.ACCEPT)
1743+ return;
1744+
1745+ this.rule = entry.get_text ();
1746+ this.dialog.destroy ();
1747+ custom.add_rule (this.rule);
1748+ }
1749+ }
1750+
1751+}
1752
1753=== modified file 'katze/midori-paths.vala'
1754--- katze/midori-paths.vala 2014-02-20 18:27:01 +0000
1755+++ katze/midori-paths.vala 2014-03-10 10:27:25 +0000
1756@@ -366,17 +366,24 @@
1757 if (Posix.access (path, Posix.F_OK) == 0)
1758 return path;
1759
1760+ return build_folder ("data", null, filename) ??
1761+ Path.build_filename (MDATADIR, PACKAGE_NAME, "res", filename);
1762+ #endif
1763+ }
1764+
1765+ string? build_folder (string folder, string? middle, string filename) {
1766 /* Fallback to build folder */
1767 File? parent = File.new_for_path (exec_path);
1768 while (parent != null) {
1769- var data = parent.get_child ("data");
1770+ var data = parent.get_child (folder);
1771+ if (middle != null)
1772+ data = data.get_child (middle);
1773 var child = data.get_child (filename);
1774 if (child.query_exists ())
1775 return child.get_path ();
1776 parent = parent.get_parent ();
1777 }
1778- return Path.build_filename (MDATADIR, PACKAGE_NAME, "res", filename);
1779- #endif
1780+ return null;
1781 }
1782
1783 /* returns the path to a file containing read-only data installed with the application
1784@@ -416,7 +423,8 @@
1785 return path;
1786 }
1787
1788- return Path.build_filename (SYSCONFDIR, "xdg", PACKAGE_NAME, folder ?? "", filename);
1789+ return build_folder ("config", folder, filename) ??
1790+ Path.build_filename (SYSCONFDIR, "xdg", PACKAGE_NAME, folder ?? "", filename);
1791 #endif
1792 }
1793
1794
1795=== modified file 'midori/midori-app.c'
1796--- midori/midori-app.c 2014-02-24 22:16:00 +0000
1797+++ midori/midori-app.c 2014-03-10 10:27:25 +0000
1798@@ -1257,7 +1257,7 @@
1799 midori_debug (const gchar* token)
1800 {
1801 static const gchar* debug_token = NULL;
1802- const gchar* debug_tokens = "adblock:match adblock:time adblock:element startup headers body referer cookies paths hsts unarmed bookmarks mouse app ";
1803+ const gchar* debug_tokens = "adblock:match adblock:parse adblock:time adblock:element startup headers body referer cookies paths hsts unarmed bookmarks mouse app ";
1804 if (debug_token == NULL)
1805 {
1806 gchar* found_token;
1807
1808=== modified file 'midori/midori-browser.c'
1809--- midori/midori-browser.c 2014-03-03 21:28:53 +0000
1810+++ midori/midori-browser.c 2014-03-10 10:27:25 +0000
1811@@ -1237,7 +1237,7 @@
1812
1813 {
1814 #ifndef HAVE_WEBKIT2
1815- if (!midori_download_has_enough_space (download, uri))
1816+ if (!midori_download_has_enough_space (download, uri, FALSE))
1817 return FALSE;
1818 webkit_download_set_destination_uri (download, uri);
1819 g_signal_emit (browser, signals[ADD_DOWNLOAD], 0, download);
1820
1821=== modified file 'midori/midori-download.vala'
1822--- midori/midori-download.vala 2014-03-05 07:17:45 +0000
1823+++ midori/midori-download.vala 2014-03-10 10:27:25 +0000
1824@@ -348,7 +348,7 @@
1825 * Returns whether it seems possible to save @download to the path specified by
1826 * @destination_uri, considering space on disk and permissions
1827 */
1828- public static bool has_enough_space (WebKit.Download download, string destination_uri) {
1829+ public static bool has_enough_space (WebKit.Download download, string destination_uri, bool quiet=false) {
1830 #if !HAVE_WEBKIT2
1831 var folder = File.new_for_uri (destination_uri).get_parent ();
1832 bool can_write;
1833@@ -380,7 +380,8 @@
1834 }
1835 else
1836 assert_not_reached ();
1837- Sokoke.message_dialog (Gtk.MessageType.ERROR, message, detailed_message, false);
1838+ if (!quiet)
1839+ Sokoke.message_dialog (Gtk.MessageType.ERROR, message, detailed_message, false);
1840 return false;
1841 }
1842 #endif
1843
1844=== modified file 'po/POTFILES.in'
1845--- po/POTFILES.in 2014-03-02 23:15:34 +0000
1846+++ po/POTFILES.in 2014-03-10 10:27:25 +0000
1847@@ -102,4 +102,5 @@
1848 extensions/adblock/config.vala
1849 extensions/adblock/updater.vala
1850 extensions/adblock/element.vala
1851+extensions/adblock/widgets.vala
1852 extensions/domain-keys.vala

Subscribers

People subscribed via source and target branches

to all changes: