OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 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 | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 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/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 var EXPORTED_SYMBOLS = ["AdblockPlusApi"]; | 18 "use strict"; |
19 | 19 |
20 const Cc = Components.classes; | 20 const {defaultMatcher} = require("matcher"); |
21 const Ci = Components.interfaces; | 21 const {Filter, RegExpFilter} = require("filterClasses"); |
22 const Cr = Components.results; | 22 const {FilterNotifier} = require("filterNotifier"); |
23 const Cu = Components.utils; | 23 const {FilterStorage} = require("filterStorage"); |
24 | 24 const {Prefs} = require("prefs"); |
25 Cu.import("resource://gre/modules/Services.jsm"); | 25 const {Subscription, DownloadableSubscription, SpecialSubscription} = |
26 Cu.import("resource://gre/modules/Messaging.jsm"); | 26 require("subscriptionClasses"); |
27 | 27 const {Synchronizer} = require("synchronizer"); |
28 let XMLHttpRequest = Components.Constructor( | 28 |
29 "@mozilla.org/xmlextras/xmlhttprequest;1", "nsIXMLHttpRequest"); | 29 const ENABLE_PROP = "enable"; |
30 | 30 const HOST_PROP = "host"; |
31 function require(module) | 31 const TITLE_PROP = "title"; |
32 { | 32 const URL_PROP = "url"; |
33 let result = {}; | 33 |
34 result.wrappedJSObject = result; | 34 function init() |
35 Services.obs.notifyObservers(result, "adblockplus-require", module); | 35 { |
36 return result.exports; | 36 Promise.all([FilterNotifier.once("load"), Prefs.untilLoaded]).then(onLoaded); |
37 } | 37 } |
38 | 38 |
39 let {Filter, RegExpFilter} = require("filterClasses"); | 39 function onLoaded() |
40 let {FilterNotifier} = require("filterNotifier"); | 40 { |
41 let {FilterStorage} = require("filterStorage"); | 41 browser.runtime.registerAbbMessageListener(handleMessage); |
42 let {defaultMatcher} = require("matcher"); | 42 browser.runtime.sendAbbMessage("OnLoaded"); |
43 let {Prefs} = require("prefs"); | 43 FilterNotifier.on("save", onFiltersSaved); |
44 let {Subscription, SpecialSubscription, RegularSubscription, DownloadableSubscri
ption, ExternalSubscription} = require("subscriptionClasses"); | 44 } |
45 let {Synchronizer} = require("synchronizer"); | 45 |
46 let {UI} = require("ui"); | 46 function onFiltersSaved() |
47 | 47 { |
48 const SUBSCRIPTIONS_SAVED_PREF = "subscriptions_saved"; | 48 browser.runtime.sendAbbMessage("OnFiltersSaved"); |
49 const USER_REMOVED_BLOCK_SUBS_PREF = "user_removed_block_subscriptions"; | 49 } |
50 const USER_REMOVED_EXCEPTIONS_SUB_PREF = "user_removed_exception_subscription"; | 50 |
51 | 51 function handleMessage(data) |
52 function initFilterListeners() | 52 { |
53 { | 53 switch ((data["action"])) |
54 FilterNotifier.on("load", onFiltersLoad); | 54 { |
55 FilterNotifier.on("save", onFiltersSave); | 55 case "getAdblockPlusEnabled": |
56 } | 56 return successData(Prefs.enabled); |
57 | 57 |
58 function onFiltersLoad() | 58 case "setAdblockPlusEnabled": |
59 { | 59 if (!checkData(data, ENABLE_PROP)) break; |
60 let detectedSubscriptionFailure = !hasBlockSubscription() && | 60 Prefs.enabled = !!data[ENABLE_PROP]; |
61 (!getBoolPref(SUBSCRIPTIONS_SAVED_PREF) || !getBoolPref(USER_REMOVED_BLOCK_S
UBS_PREF) || FilterStorage.loadFromDiskFailed); | 61 return successData(); |
62 | 62 |
63 // We will only try to recover the default subscription settings if the addonV
ersion hasn't changed, | 63 case "getAcceptableAdsEnabled": |
64 // otherwise it will be handled in firstRunActions(), inside ui.js | 64 return successData(isSubscriptionEnabled( |
65 let {addonVersion} = require("info"); | 65 Prefs.subscriptions_exceptionsurl)); |
66 if (Prefs.currentVersion == addonVersion && detectedSubscriptionFailure) | 66 |
67 { | 67 case "setAcceptableAdsEnabled": |
68 if (getBoolPref(USER_REMOVED_EXCEPTIONS_SUB_PREF)) | 68 if (!checkData(data, ENABLE_PROP)) break; |
| 69 const acceptableAdsTitle = "Allow non-intrusive advertising"; |
| 70 setSubscriptionEnabled(!!data[ENABLE_PROP], |
| 71 Prefs.subscriptions_exceptionsurl, acceptableAdsTitle); |
| 72 return successData(); |
| 73 |
| 74 case "getEnabledSubscriptions": |
| 75 return successData(getEnabledSubscriptions()); |
| 76 |
| 77 case "isSubscriptionEnabled": |
| 78 if (!checkData(data, URL_PROP)) break; |
| 79 return successData(isSubscriptionEnabled(data[URL_PROP])); |
| 80 |
| 81 case "addSubscription": |
| 82 if (!checkData(data, URL_PROP)) break; |
| 83 setSubscriptionEnabled(true, data[URL_PROP], data[TITLE_PROP]); |
| 84 return successData(); |
| 85 |
| 86 case "removeSubscription": |
| 87 if (!checkData(data, URL_PROP)) break; |
| 88 setSubscriptionEnabled(false, data[URL_PROP]); |
| 89 return successData(); |
| 90 |
| 91 case "getWhitelistedDomains": |
| 92 return successData(getWhitelistedDomains()); |
| 93 |
| 94 case "isDomainWhitelisted": |
| 95 if (!checkData(data, [URL_PROP, HOST_PROP])) break; |
| 96 return successData(isDomainWhitelisted(data[URL_PROP], data[HOST_PROP])); |
| 97 |
| 98 case "whitelistDomain": |
| 99 if (!checkData(data, [ENABLE_PROP, URL_PROP, HOST_PROP])) break; |
| 100 setDomainWhitelisted(!!data[ENABLE_PROP], data[URL_PROP], data[HOST_PROP])
; |
| 101 return successData(); |
| 102 |
| 103 } |
| 104 return errorData("Malformed request"); |
| 105 } |
| 106 |
| 107 function getEnabledSubscriptions() |
| 108 { |
| 109 return FilterStorage.subscriptions.filter(sub => !sub.disabled).map(sub => |
| 110 ({[TITLE_PROP]: sub.title, [URL_PROP]: sub.url})); |
| 111 } |
| 112 |
| 113 function isSubscriptionEnabled(url) |
| 114 { |
| 115 const sub = Subscription.fromURL(url); |
| 116 return sub.url in FilterStorage.knownSubscriptions && !sub.disabled; |
| 117 } |
| 118 |
| 119 function setSubscriptionEnabled(isEnabled, url, title) |
| 120 { |
| 121 if (isSubscriptionEnabled(url) == isEnabled) return; |
| 122 if (isEnabled) addSubscription(url, title); |
| 123 else removeSubscription(url); |
| 124 } |
| 125 |
| 126 function addSubscription(url, title) |
| 127 { |
| 128 const sub = Subscription.fromURL(url); |
| 129 sub.disabled = false; |
| 130 if (title) sub.title = title; |
| 131 FilterStorage.addSubscription(sub); |
| 132 |
| 133 if (sub instanceof DownloadableSubscription && !sub.lastDownload) |
| 134 { |
| 135 Synchronizer.execute(sub); |
| 136 } |
| 137 } |
| 138 |
| 139 function removeSubscription(url) |
| 140 { |
| 141 FilterStorage.removeSubscription(Subscription.fromURL(url)); |
| 142 } |
| 143 |
| 144 function addFilter(text) |
| 145 { |
| 146 const filter = Filter.fromText(text); |
| 147 if (filter.disabled) filter.disabled = false; |
| 148 if (!filter.subscriptions.length) FilterStorage.addFilter(filter); |
| 149 } |
| 150 |
| 151 function removeFilter(filter) |
| 152 { |
| 153 FilterStorage.removeFilter(filter); |
| 154 if (filter.subscriptions.length) filter.disabled = true; |
| 155 } |
| 156 |
| 157 function getWhitelistedDomains() |
| 158 { |
| 159 const whitelistRegex = /^@@\|\|([^/:]+)\^\$document$/; |
| 160 const results = []; |
| 161 |
| 162 for (const sub of FilterStorage.subscriptions) |
| 163 { |
| 164 if (!(sub instanceof SpecialSubscription) || sub.disabled) continue; |
| 165 for (const filter of sub.filters) |
69 { | 166 { |
70 UI.addSubscription(UI.currentWindow, Prefs.currentVersion); | 167 const match = filter.text.match(whitelistRegex); |
| 168 if (match) results.push({[URL_PROP]: match[1]}); |
71 } | 169 } |
72 else | 170 } |
73 { | 171 |
74 UI.addSubscription(UI.currentWindow, "0.0"); | 172 return results; |
75 } | 173 } |
76 } | 174 |
77 EventDispatcher.instance.sendRequest({type: "Abb:OnFiltersLoad"}); | 175 function isDomainWhitelisted(url, host) |
78 } | 176 { |
79 | 177 return !!getWhitelistingFilter(url, host); |
80 function onFiltersSave() | 178 } |
81 { | 179 |
82 if (hasBlockSubscription()) | 180 function setDomainWhitelisted(isWhitelisted, url, host) |
83 { | 181 { |
84 setBoolPref(SUBSCRIPTIONS_SAVED_PREF, true); | 182 if (isDomainWhitelisted(url, host) == isWhitelisted) return; |
85 } | 183 if (isWhitelisted) addDomainToWhitelist(host); |
86 EventDispatcher.instance.sendRequest({type: "Abb:OnFiltersSave"}); | 184 else removeDomainFromWhitelist(url, host); |
87 } | 185 } |
88 | 186 |
89 function getBoolPref(name) | 187 function addDomainToWhitelist(host) |
90 { | 188 { |
91 let branch = getPrefsBranch(); | 189 addFilter("@@||" + host + "^$document"); |
| 190 } |
| 191 |
| 192 function removeDomainFromWhitelist(url, host) |
| 193 { |
| 194 let filter = getWhitelistingFilter(url, host); |
| 195 while (filter) |
| 196 { |
| 197 removeFilter(filter); |
| 198 filter = getWhitelistingFilter(url, host); |
| 199 } |
| 200 } |
| 201 |
| 202 function getWhitelistingFilter(url, host) |
| 203 { |
92 try | 204 try |
93 { | 205 { |
94 return branch.getBoolPref(name); | |
95 } | |
96 catch (e) | |
97 { | |
98 return null; | |
99 } | |
100 } | |
101 | |
102 function setBoolPref(name, value) | |
103 { | |
104 let branch = getPrefsBranch(); | |
105 branch.setBoolPref(name, value); | |
106 Services.prefs.savePrefFile(null); | |
107 } | |
108 | |
109 function getPrefsBranch() | |
110 { | |
111 let {addonRoot, addonName} = require("info"); | |
112 let branchName = "extensions." + addonName + "."; | |
113 return Services.prefs.getBranch(branchName); | |
114 } | |
115 | |
116 function hasBlockSubscription() | |
117 { | |
118 return FilterStorage.subscriptions.some( | |
119 subscription => subscription instanceof DownloadableSubscription && subscrip
tion.url != Prefs.subscriptions_exceptionsurl); | |
120 } | |
121 | |
122 function getWhitelistingFilter(url, host) | |
123 { | |
124 try | |
125 { | |
126 return defaultMatcher.whitelist.matchesAny( | 206 return defaultMatcher.whitelist.matchesAny( |
127 url, RegExpFilter.typeMap.DOCUMENT, host, false, null, false); | 207 url, RegExpFilter.typeMap.DOCUMENT, host, false, null, false); |
128 } | 208 } |
129 catch (e) | 209 catch (e) {} |
130 { | 210 return null; |
131 return null; | 211 } |
132 } | 212 |
133 } | 213 function checkData(data, check) |
134 | 214 { |
135 var AdblockPlusApi = | 215 if (!data) return false; |
136 { | 216 const properties = [].concat(check || []); |
137 get filtersLoaded() | 217 return properties.every(function(item) { |
138 { | 218 return item in data; |
139 return !FilterStorage._loading; | 219 }); |
140 }, | 220 } |
141 get adblockPlusEnabled() | 221 |
142 { | 222 function successData(value) |
143 return Prefs.enabled; | 223 { |
144 }, | 224 const data = {}; |
145 set adblockPlusEnabled(adblockPlusEnabled) | 225 data.success = true; |
146 { | 226 if (value != null) data.value = value; |
147 Prefs.enabled = adblockPlusEnabled | 227 return data; |
148 }, | 228 } |
149 get acceptableAdsEnabled() | 229 |
150 { | 230 function errorData(errorMsg) |
151 return FilterStorage.subscriptions.some( | 231 { |
152 (subscription) => subscription.url == Prefs.subscriptions_exceptionsurl); | 232 const data = {}; |
153 }, | 233 if (errorMsg) data.error = errorMsg; |
154 set acceptableAdsEnabled(acceptableAdsEnabled) | 234 return data; |
155 { | 235 } |
156 if (acceptableAdsEnabled != AdblockPlusApi.acceptableAdsEnabled) | 236 |
157 UI.toggleAcceptableAds(); | 237 init(); |
158 }, | |
159 get subscriptionsXml() | |
160 { | |
161 let request = new XMLHttpRequest(); | |
162 // Synchronous requests are deprecated, but using an asynchronous request | |
163 // here (for a static resource that doesn't need to be downloaded anyway) | |
164 // would require a huge code change on the Java side, so we stick to | |
165 // synchronous requests | |
166 request.open("GET", | |
167 "chrome://adblockplus/content/ui/subscriptions.xml", | |
168 false); | |
169 request.send(); | |
170 return request.responseText; | |
171 }, | |
172 isSubscriptionListed: function(url) | |
173 { | |
174 return url in FilterStorage.knownSubscriptions; | |
175 }, | |
176 addSubscription: function(url, title) | |
177 { | |
178 let subscriptionToAdd = Subscription.fromURL(url); | |
179 if (title) | |
180 subscriptionToAdd.title = title; | |
181 FilterStorage.addSubscription(subscriptionToAdd); | |
182 let subscription = FilterStorage.knownSubscriptions[url]; | |
183 if (subscription) | |
184 { | |
185 subscription.disabled = false; | |
186 if (!subscription.lastDownload) | |
187 { | |
188 Synchronizer.execute(subscription); | |
189 } | |
190 } | |
191 if (url == Prefs.subscriptions_exceptionsurl) | |
192 { | |
193 setBoolPref(USER_REMOVED_EXCEPTIONS_SUB_PREF, false); | |
194 } | |
195 else if (hasBlockSubscription()) | |
196 { | |
197 setBoolPref(USER_REMOVED_BLOCK_SUBS_PREF, false); | |
198 } | |
199 }, | |
200 removeSubscription: function(url) | |
201 { | |
202 FilterStorage.removeSubscription(FilterStorage.knownSubscriptions[url]); | |
203 if (url == Prefs.subscriptions_exceptionsurl) | |
204 { | |
205 setBoolPref(USER_REMOVED_EXCEPTIONS_SUB_PREF, true); | |
206 } | |
207 else if (!hasBlockSubscription()) | |
208 { | |
209 setBoolPref(USER_REMOVED_BLOCK_SUBS_PREF, true); | |
210 } | |
211 }, | |
212 getActiveSubscriptions: function() | |
213 { | |
214 let subscriptions = []; | |
215 for (let i = 0; i < FilterStorage.subscriptions.length; i++) | |
216 { | |
217 let subscription = FilterStorage.subscriptions[i]; | |
218 if (!subscription.disabled) | |
219 subscriptions.push({"title": subscription.title, "url": subscription.url
}); | |
220 } | |
221 return subscriptions; | |
222 }, | |
223 get whitelistedWebsites() | |
224 { | |
225 let whitelistedWebsites = []; | |
226 for (let i = 0; i < FilterStorage.subscriptions.length; i++) | |
227 { | |
228 let subscription = FilterStorage.subscriptions[i]; | |
229 if (subscription.url && subscription.url.startsWith("~user~") && subscript
ion.filters) | |
230 { | |
231 for (let j = 0; j < subscription.filters.length; j++) | |
232 { | |
233 let filter = subscription.filters[j]; | |
234 let whitelistMatch = filter.text ? filter.text.match(/^@@\|\|([^/:]+)\
^\$document$/) : null; | |
235 if(whitelistMatch) | |
236 { | |
237 whitelistedWebsites.push({"url": whitelistMatch[1]}) | |
238 } | |
239 } | |
240 } | |
241 } | |
242 return whitelistedWebsites; | |
243 }, | |
244 isWebsiteWhitelisted: function(url, host) | |
245 { | |
246 return !!getWhitelistingFilter(url, host); | |
247 }, | |
248 whitelistWebsite: function(url, host, whitelisted) | |
249 { | |
250 if (whitelisted) | |
251 { | |
252 var filter = Filter.fromText("@@||" + host + "^$document"); | |
253 if (filter.subscriptions.length && filter.disabled) | |
254 { | |
255 filter.disabled = false; | |
256 } | |
257 else | |
258 { | |
259 filter.disabled = false; | |
260 FilterStorage.addFilter(filter); | |
261 } | |
262 } | |
263 else | |
264 { | |
265 // Remove any exception rules applying to this URL | |
266 var filter = getWhitelistingFilter(url, host); | |
267 while (filter) | |
268 { | |
269 FilterStorage.removeFilter(filter); | |
270 if (filter.subscriptions.length) | |
271 { | |
272 filter.disabled = true; | |
273 } | |
274 filter = getWhitelistingFilter(url); | |
275 } | |
276 } | |
277 }, | |
278 initCommunication: function() | |
279 { | |
280 initFilterListeners(); | |
281 | |
282 EventDispatcher.instance.registerListener((event, data, callback) => | |
283 { | |
284 if (!data) | |
285 { | |
286 callback.onError("malformed request"); | |
287 return; | |
288 } | |
289 | |
290 if (!this.filtersLoaded) | |
291 { | |
292 callback.onError("filters not loaded"); | |
293 return; | |
294 } | |
295 | |
296 switch (data["action"]) | |
297 { | |
298 case "getAdblockPlusEnabled": | |
299 callback.onSuccess({"value": this.adblockPlusEnabled}); | |
300 return; | |
301 case "setAdblockPlusEnabled": | |
302 if ("enable" in data) | |
303 { | |
304 this.adblockPlusEnabled = !!data["enable"]; | |
305 callback.onSuccess({}); | |
306 return; | |
307 } | |
308 break; | |
309 case "getAcceptableAdsEnabled": | |
310 callback.onSuccess({"value": this.acceptableAdsEnabled}); | |
311 return; | |
312 case "setAcceptableAdsEnabled": | |
313 if ("enable" in data) | |
314 { | |
315 this.acceptableAdsEnabled = !!data["enable"]; | |
316 callback.onSuccess({}); | |
317 return; | |
318 } | |
319 break; | |
320 case "getSubscriptionsXml": | |
321 callback.onSuccess({"value": this.subscriptionsXml}); | |
322 return; | |
323 case "getActiveSubscriptions": | |
324 callback.onSuccess({"value": this.getActiveSubscriptions()}); | |
325 return; | |
326 case "isSubscriptionListed": | |
327 if ("url" in data) | |
328 { | |
329 callback.onSuccess({"value": this.isSubscriptionListed(data["url"])}
); | |
330 return; | |
331 } | |
332 break; | |
333 case "addSubscription": | |
334 if ("url" in data) | |
335 { | |
336 this.addSubscription(data["url"], data["title"]); | |
337 callback.onSuccess({}); | |
338 return; | |
339 } | |
340 break; | |
341 case "removeSubscription": | |
342 if ("url" in data) | |
343 { | |
344 this.removeSubscription(data["url"]); | |
345 callback.onSuccess({}); | |
346 return; | |
347 } | |
348 break; | |
349 case "getWhitelistedWebsites": | |
350 callback.onSuccess({"value": this.whitelistedWebsites}); | |
351 return; | |
352 case "isWebsiteWhitelisted": | |
353 if ("url" in data && "host" in data) | |
354 { | |
355 callback.onSuccess({"value": this.isWebsiteWhitelisted(data["url"],
data["host"])}); | |
356 return; | |
357 } | |
358 break; | |
359 case "whitelistWebsite": | |
360 if ("url" in data && "host" in data && "whitelisted" in data) | |
361 { | |
362 this.whitelistWebsite(data["url"], data["host"], data["whitelisted"]
); | |
363 callback.onSuccess({}); | |
364 return; | |
365 } | |
366 break; | |
367 } | |
368 callback.onError("malformed request"); | |
369 }, "AdblockPlus:Api"); | |
370 } | |
371 }; | |
OLD | NEW |