OLD | NEW |
| (Empty) |
1 /* | |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | |
3 * Copyright (C) 2006-2016 Eyeo GmbH | |
4 * | |
5 * Adblock Plus is free software: you can redistribute it and/or modify | |
6 * it under the terms of the GNU General Public License version 3 as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * Adblock Plus is distributed in the hope that it will be useful, | |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 * GNU General Public License for more details. | |
13 * | |
14 * You should have received a copy of the GNU General Public License | |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | |
16 */ | |
17 | |
18 /** | |
19 * @fileOverview Component synchronizing filter storage with Matcher instances a
nd ElemHide. | |
20 */ | |
21 | |
22 Cu.import("resource://gre/modules/XPCOMUtils.jsm"); | |
23 Cu.import("resource://gre/modules/Services.jsm"); | |
24 | |
25 let {FilterStorage} = require("filterStorage"); | |
26 let {FilterNotifier} = require("filterNotifier"); | |
27 let {ElemHide} = require("elemHide"); | |
28 let {CSSRules} = require("cssRules"); | |
29 let {defaultMatcher} = require("matcher"); | |
30 let {ActiveFilter, RegExpFilter, ElemHideBase, CSSPropertyFilter} = | |
31 require("filterClasses"); | |
32 let {Prefs} = require("prefs"); | |
33 | |
34 /** | |
35 * Value of the FilterListener.batchMode property. | |
36 * @type Boolean | |
37 */ | |
38 let batchMode = false; | |
39 | |
40 /** | |
41 * Increases on filter changes, filters will be saved if it exceeds 1. | |
42 * @type Integer | |
43 */ | |
44 let isDirty = 0; | |
45 | |
46 /** | |
47 * This object can be used to change properties of the filter change listeners. | |
48 * @class | |
49 */ | |
50 let FilterListener = | |
51 { | |
52 /** | |
53 * Set to true when executing many changes, changes will only be fully applied
after this variable is set to false again. | |
54 * @type Boolean | |
55 */ | |
56 get batchMode() | |
57 { | |
58 return batchMode; | |
59 }, | |
60 set batchMode(value) | |
61 { | |
62 batchMode = value; | |
63 flushElemHide(); | |
64 }, | |
65 | |
66 /** | |
67 * Increases "dirty factor" of the filters and calls FilterStorage.saveToDisk(
) | |
68 * if it becomes 1 or more. Save is executed delayed to prevent multiple | |
69 * subsequent calls. If the parameter is 0 it forces saving filters if any | |
70 * changes were recorded after the previous save. | |
71 */ | |
72 setDirty: function(/**Integer*/ factor) | |
73 { | |
74 if (factor == 0 && isDirty > 0) | |
75 isDirty = 1; | |
76 else | |
77 isDirty += factor; | |
78 if (isDirty >= 1) | |
79 { | |
80 isDirty = 0; | |
81 FilterStorage.saveToDisk(); | |
82 } | |
83 } | |
84 }; | |
85 | |
86 /** | |
87 * Observer listening to history purge actions. | |
88 * @class | |
89 */ | |
90 let HistoryPurgeObserver = | |
91 { | |
92 observe: function(subject, topic, data) | |
93 { | |
94 if (topic == "browser:purge-session-history" && Prefs.clearStatsOnHistoryPur
ge) | |
95 { | |
96 FilterStorage.resetHitCounts(); | |
97 FilterListener.setDirty(0); // Force saving to disk | |
98 | |
99 Prefs.recentReports = []; | |
100 } | |
101 }, | |
102 QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference, Ci.nsIObse
rver]) | |
103 }; | |
104 | |
105 /** | |
106 * Initializes filter listener on startup, registers the necessary hooks. | |
107 */ | |
108 function init() | |
109 { | |
110 FilterNotifier.addListener(function(action, item, newValue, oldValue) | |
111 { | |
112 let match = /^(\w+)\.(.*)/.exec(action); | |
113 if (match && match[1] == "filter") | |
114 onFilterChange(match[2], item, newValue, oldValue); | |
115 else if (match && match[1] == "subscription") | |
116 onSubscriptionChange(match[2], item, newValue, oldValue); | |
117 else | |
118 onGenericChange(action, item); | |
119 }); | |
120 | |
121 if ("nsIStyleSheetService" in Ci) | |
122 ElemHide.init(); | |
123 else | |
124 flushElemHide = function() {}; // No global stylesheet in Chrome & Co. | |
125 FilterStorage.loadFromDisk(); | |
126 | |
127 Services.obs.addObserver(HistoryPurgeObserver, "browser:purge-session-history"
, true); | |
128 onShutdown.add(function() | |
129 { | |
130 Services.obs.removeObserver(HistoryPurgeObserver, "browser:purge-session-his
tory"); | |
131 }); | |
132 } | |
133 init(); | |
134 | |
135 /** | |
136 * Calls ElemHide.apply() if necessary. | |
137 */ | |
138 function flushElemHide() | |
139 { | |
140 if (!batchMode && ElemHide.isDirty) | |
141 ElemHide.apply(); | |
142 } | |
143 | |
144 /** | |
145 * Notifies Matcher instances or ElemHide object about a new filter | |
146 * if necessary. | |
147 * @param {Filter} filter filter that has been added | |
148 */ | |
149 function addFilter(filter) | |
150 { | |
151 if (!(filter instanceof ActiveFilter) || filter.disabled) | |
152 return; | |
153 | |
154 let hasEnabled = false; | |
155 for (let i = 0; i < filter.subscriptions.length; i++) | |
156 if (!filter.subscriptions[i].disabled) | |
157 hasEnabled = true; | |
158 if (!hasEnabled) | |
159 return; | |
160 | |
161 if (filter instanceof RegExpFilter) | |
162 defaultMatcher.add(filter); | |
163 else if (filter instanceof ElemHideBase) | |
164 { | |
165 if (filter instanceof CSSPropertyFilter) | |
166 CSSRules.add(filter); | |
167 else | |
168 ElemHide.add(filter); | |
169 } | |
170 } | |
171 | |
172 /** | |
173 * Notifies Matcher instances or ElemHide object about removal of a filter | |
174 * if necessary. | |
175 * @param {Filter} filter filter that has been removed | |
176 */ | |
177 function removeFilter(filter) | |
178 { | |
179 if (!(filter instanceof ActiveFilter)) | |
180 return; | |
181 | |
182 if (!filter.disabled) | |
183 { | |
184 let hasEnabled = false; | |
185 for (let i = 0; i < filter.subscriptions.length; i++) | |
186 if (!filter.subscriptions[i].disabled) | |
187 hasEnabled = true; | |
188 if (hasEnabled) | |
189 return; | |
190 } | |
191 | |
192 if (filter instanceof RegExpFilter) | |
193 defaultMatcher.remove(filter); | |
194 else if (filter instanceof ElemHideBase) | |
195 { | |
196 if (filter instanceof CSSPropertyFilter) | |
197 CSSRules.remove(filter); | |
198 else | |
199 ElemHide.remove(filter); | |
200 } | |
201 } | |
202 | |
203 /** | |
204 * Subscription change listener | |
205 */ | |
206 function onSubscriptionChange(action, subscription, newValue, oldValue) | |
207 { | |
208 FilterListener.setDirty(1); | |
209 | |
210 if (action != "added" && action != "removed" && action != "disabled" && action
!= "updated") | |
211 return; | |
212 | |
213 if (action != "removed" && !(subscription.url in FilterStorage.knownSubscripti
ons)) | |
214 { | |
215 // Ignore updates for subscriptions not in the list | |
216 return; | |
217 } | |
218 | |
219 if ((action == "added" || action == "removed" || action == "updated") && subsc
ription.disabled) | |
220 { | |
221 // Ignore adding/removing/updating of disabled subscriptions | |
222 return; | |
223 } | |
224 | |
225 if (action == "added" || action == "removed" || action == "disabled") | |
226 { | |
227 let method = (action == "added" || (action == "disabled" && newValue == fals
e) ? addFilter : removeFilter); | |
228 if (subscription.filters) | |
229 subscription.filters.forEach(method); | |
230 } | |
231 else if (action == "updated") | |
232 { | |
233 subscription.oldFilters.forEach(removeFilter); | |
234 subscription.filters.forEach(addFilter); | |
235 } | |
236 | |
237 flushElemHide(); | |
238 } | |
239 | |
240 /** | |
241 * Filter change listener | |
242 */ | |
243 function onFilterChange(action, filter, newValue, oldValue) | |
244 { | |
245 if (action == "hitCount" && newValue == 0) | |
246 { | |
247 // Filter hits are being reset, make sure these changes are saved. | |
248 FilterListener.setDirty(0); | |
249 } | |
250 else if (action == "hitCount" || action == "lastHit") | |
251 FilterListener.setDirty(0.002); | |
252 else | |
253 FilterListener.setDirty(1); | |
254 | |
255 if (action != "added" && action != "removed" && action != "disabled") | |
256 return; | |
257 | |
258 if ((action == "added" || action == "removed") && filter.disabled) | |
259 { | |
260 // Ignore adding/removing of disabled filters | |
261 return; | |
262 } | |
263 | |
264 if (action == "added" || (action == "disabled" && newValue == false)) | |
265 addFilter(filter); | |
266 else | |
267 removeFilter(filter); | |
268 flushElemHide(); | |
269 } | |
270 | |
271 /** | |
272 * Generic notification listener | |
273 */ | |
274 function onGenericChange(action) | |
275 { | |
276 if (action == "load") | |
277 { | |
278 isDirty = 0; | |
279 | |
280 defaultMatcher.clear(); | |
281 ElemHide.clear(); | |
282 CSSRules.clear(); | |
283 for (let subscription of FilterStorage.subscriptions) | |
284 if (!subscription.disabled) | |
285 subscription.filters.forEach(addFilter); | |
286 flushElemHide(); | |
287 } | |
288 else if (action == "save") | |
289 isDirty = 0; | |
290 } | |
OLD | NEW |