| 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 |