| LEFT | RIGHT |
| (no file at all) | |
| 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-2017 eyeo GmbH | 3 * Copyright (C) 2006-2017 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 /** | |
| 19 * @fileOverview Definition of Subscription class and its subclasses. | |
| 20 */ | |
| 21 | |
| 22 "use strict"; | 18 "use strict"; |
| 23 | 19 |
| 24 let {ActiveFilter, BlockingFilter, WhitelistFilter, ElemHideBase} = require("fil
terClasses"); | 20 let compiled = require("compiled"); |
| 25 let {FilterNotifier} = require("filterNotifier"); | 21 for (let cls of ["Subscription", "SpecialSubscription", |
| 26 | 22 "DownloadableSubscription"]) |
| 27 /** | |
| 28 * Abstract base class for filter subscriptions | |
| 29 * | |
| 30 * @param {String} url download location of the subscription | |
| 31 * @param {String} [title] title of the filter subscription | |
| 32 * @constructor | |
| 33 */ | |
| 34 function Subscription(url, title) | |
| 35 { | 23 { |
| 36 this.url = url; | 24 exports[cls] = compiled[cls]; |
| 37 this.filters = []; | |
| 38 if (title) | |
| 39 this._title = title; | |
| 40 Subscription.knownSubscriptions[url] = this; | |
| 41 } | 25 } |
| 42 exports.Subscription = Subscription; | |
| 43 | |
| 44 Subscription.prototype = | |
| 45 { | |
| 46 /** | |
| 47 * Download location of the subscription | |
| 48 * @type String | |
| 49 */ | |
| 50 url: null, | |
| 51 | |
| 52 /** | |
| 53 * Filters contained in the filter subscription | |
| 54 * @type Filter[] | |
| 55 */ | |
| 56 filters: null, | |
| 57 | |
| 58 _title: null, | |
| 59 _fixedTitle: false, | |
| 60 _disabled: false, | |
| 61 | |
| 62 /** | |
| 63 * Title of the filter subscription | |
| 64 * @type String | |
| 65 */ | |
| 66 get title() | |
| 67 { | |
| 68 return this._title; | |
| 69 }, | |
| 70 set title(value) | |
| 71 { | |
| 72 if (value != this._title) | |
| 73 { | |
| 74 let oldValue = this._title; | |
| 75 this._title = value; | |
| 76 FilterNotifier.triggerListeners("subscription.title", this, value, oldValu
e); | |
| 77 } | |
| 78 return this._title; | |
| 79 }, | |
| 80 | |
| 81 /** | |
| 82 * Determines whether the title should be editable | |
| 83 * @type Boolean | |
| 84 */ | |
| 85 get fixedTitle() | |
| 86 { | |
| 87 return this._fixedTitle; | |
| 88 }, | |
| 89 set fixedTitle(value) | |
| 90 { | |
| 91 if (value != this._fixedTitle) | |
| 92 { | |
| 93 let oldValue = this._fixedTitle; | |
| 94 this._fixedTitle = value; | |
| 95 FilterNotifier.triggerListeners("subscription.fixedTitle", this, value, ol
dValue); | |
| 96 } | |
| 97 return this._fixedTitle; | |
| 98 }, | |
| 99 | |
| 100 /** | |
| 101 * Defines whether the filters in the subscription should be disabled | |
| 102 * @type Boolean | |
| 103 */ | |
| 104 get disabled() | |
| 105 { | |
| 106 return this._disabled; | |
| 107 }, | |
| 108 set disabled(value) | |
| 109 { | |
| 110 if (value != this._disabled) | |
| 111 { | |
| 112 let oldValue = this._disabled; | |
| 113 this._disabled = value; | |
| 114 FilterNotifier.triggerListeners("subscription.disabled", this, value, oldV
alue); | |
| 115 } | |
| 116 return this._disabled; | |
| 117 }, | |
| 118 | |
| 119 /** | |
| 120 * Serializes the subscription to an array of strings for writing out on the d
isk. | |
| 121 * @param {string[]} buffer buffer to push the serialization results into | |
| 122 */ | |
| 123 serialize: function(buffer) | |
| 124 { | |
| 125 buffer.push("[Subscription]"); | |
| 126 buffer.push("url=" + this.url); | |
| 127 if (this._title) | |
| 128 buffer.push("title=" + this._title); | |
| 129 if (this._fixedTitle) | |
| 130 buffer.push("fixedTitle=true"); | |
| 131 if (this._disabled) | |
| 132 buffer.push("disabled=true"); | |
| 133 }, | |
| 134 | |
| 135 serializeFilters: function(buffer) | |
| 136 { | |
| 137 for (let filter of this.filters) | |
| 138 buffer.push(filter.text.replace(/\[/g, "\\[")); | |
| 139 }, | |
| 140 | |
| 141 toString: function() | |
| 142 { | |
| 143 let buffer = []; | |
| 144 this.serialize(buffer); | |
| 145 return buffer.join("\n"); | |
| 146 } | |
| 147 }; | |
| 148 | |
| 149 /** | |
| 150 * Cache for known filter subscriptions, maps URL to subscription objects. | |
| 151 * @type Object | |
| 152 */ | |
| 153 Subscription.knownSubscriptions = Object.create(null); | |
| 154 | |
| 155 /** | |
| 156 * Returns a subscription from its URL, creates a new one if necessary. | |
| 157 * @param {String} url URL of the subscription | |
| 158 * @return {Subscription} subscription or null if the subscription couldn't be c
reated | |
| 159 */ | |
| 160 Subscription.fromURL = function(url) | |
| 161 { | |
| 162 if (url in Subscription.knownSubscriptions) | |
| 163 return Subscription.knownSubscriptions[url]; | |
| 164 | |
| 165 if (url[0] != "~") | |
| 166 return new DownloadableSubscription(url, null); | |
| 167 else | |
| 168 return new SpecialSubscription(url); | |
| 169 }; | |
| 170 | |
| 171 /** | |
| 172 * Deserializes a subscription | |
| 173 * | |
| 174 * @param {Object} obj map of serialized properties and their values | |
| 175 * @return {Subscription} subscription or null if the subscription couldn't be c
reated | |
| 176 */ | |
| 177 Subscription.fromObject = function(obj) | |
| 178 { | |
| 179 let result; | |
| 180 if (obj.url[0] != "~") | |
| 181 { | |
| 182 // URL is valid - this is a downloadable subscription | |
| 183 result = new DownloadableSubscription(obj.url, obj.title); | |
| 184 if ("downloadStatus" in obj) | |
| 185 result._downloadStatus = obj.downloadStatus; | |
| 186 if ("lastSuccess" in obj) | |
| 187 result.lastSuccess = parseInt(obj.lastSuccess, 10) || 0; | |
| 188 if ("lastCheck" in obj) | |
| 189 result._lastCheck = parseInt(obj.lastCheck, 10) || 0; | |
| 190 if ("expires" in obj) | |
| 191 result.expires = parseInt(obj.expires, 10) || 0; | |
| 192 if ("softExpiration" in obj) | |
| 193 result.softExpiration = parseInt(obj.softExpiration, 10) || 0; | |
| 194 if ("errors" in obj) | |
| 195 result._errors = parseInt(obj.errors, 10) || 0; | |
| 196 if ("version" in obj) | |
| 197 result.version = parseInt(obj.version, 10) || 0; | |
| 198 if ("requiredVersion" in obj) | |
| 199 result.requiredVersion = obj.requiredVersion; | |
| 200 if ("homepage" in obj) | |
| 201 result._homepage = obj.homepage; | |
| 202 if ("lastDownload" in obj) | |
| 203 result._lastDownload = parseInt(obj.lastDownload, 10) || 0; | |
| 204 if ("downloadCount" in obj) | |
| 205 result.downloadCount = parseInt(obj.downloadCount, 10) || 0; | |
| 206 } | |
| 207 else | |
| 208 { | |
| 209 result = new SpecialSubscription(obj.url, obj.title); | |
| 210 if ("defaults" in obj) | |
| 211 result.defaults = obj.defaults.split(" "); | |
| 212 } | |
| 213 if ("fixedTitle" in obj) | |
| 214 result._fixedTitle = (obj.fixedTitle == "true"); | |
| 215 if ("disabled" in obj) | |
| 216 result._disabled = (obj.disabled == "true"); | |
| 217 | |
| 218 return result; | |
| 219 }; | |
| 220 | |
| 221 /** | |
| 222 * Class for special filter subscriptions (user's filters) | |
| 223 * @param {String} url see Subscription() | |
| 224 * @param {String} [title] see Subscription() | |
| 225 * @constructor | |
| 226 * @augments Subscription | |
| 227 */ | |
| 228 function SpecialSubscription(url, title) | |
| 229 { | |
| 230 Subscription.call(this, url, title); | |
| 231 } | |
| 232 exports.SpecialSubscription = SpecialSubscription; | |
| 233 | |
| 234 SpecialSubscription.prototype = | |
| 235 { | |
| 236 __proto__: Subscription.prototype, | |
| 237 | |
| 238 /** | |
| 239 * Filter types that should be added to this subscription by default | |
| 240 * (entries should correspond to keys in SpecialSubscription.defaultsMap). | |
| 241 * @type string[] | |
| 242 */ | |
| 243 defaults: null, | |
| 244 | |
| 245 /** | |
| 246 * Tests whether a filter should be added to this group by default | |
| 247 * @param {Filter} filter filter to be tested | |
| 248 * @return {Boolean} | |
| 249 */ | |
| 250 isDefaultFor: function(filter) | |
| 251 { | |
| 252 if (this.defaults && this.defaults.length) | |
| 253 { | |
| 254 for (let type of this.defaults) | |
| 255 { | |
| 256 if (filter instanceof SpecialSubscription.defaultsMap[type]) | |
| 257 return true; | |
| 258 if (!(filter instanceof ActiveFilter) && type == "blacklist") | |
| 259 return true; | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 return false; | |
| 264 }, | |
| 265 | |
| 266 /** | |
| 267 * See Subscription.serialize() | |
| 268 */ | |
| 269 serialize: function(buffer) | |
| 270 { | |
| 271 Subscription.prototype.serialize.call(this, buffer); | |
| 272 if (this.defaults && this.defaults.length) | |
| 273 buffer.push("defaults=" + this.defaults.filter((type) => type in SpecialSu
bscription.defaultsMap).join(" ")); | |
| 274 if (this._lastDownload) | |
| 275 buffer.push("lastDownload=" + this._lastDownload); | |
| 276 } | |
| 277 }; | |
| 278 | |
| 279 SpecialSubscription.defaultsMap = { | |
| 280 __proto__: null, | |
| 281 "whitelist": WhitelistFilter, | |
| 282 "blocking": BlockingFilter, | |
| 283 "elemhide": ElemHideBase | |
| 284 }; | |
| 285 | |
| 286 /** | |
| 287 * Creates a new user-defined filter group. | |
| 288 * @param {String} [title] title of the new filter group | |
| 289 * @result {SpecialSubscription} | |
| 290 */ | |
| 291 SpecialSubscription.create = function(title) | |
| 292 { | |
| 293 let url; | |
| 294 do | |
| 295 { | |
| 296 url = "~user~" + Math.round(Math.random()*1000000); | |
| 297 } while (url in Subscription.knownSubscriptions); | |
| 298 return new SpecialSubscription(url, title); | |
| 299 }; | |
| 300 | |
| 301 /** | |
| 302 * Creates a new user-defined filter group and adds the given filter to it. | |
| 303 * This group will act as the default group for this filter type. | |
| 304 */ | |
| 305 SpecialSubscription.createForFilter = function(/**Filter*/ filter) /**SpecialSub
scription*/ | |
| 306 { | |
| 307 let subscription = SpecialSubscription.create(); | |
| 308 subscription.filters.push(filter); | |
| 309 for (let type in SpecialSubscription.defaultsMap) | |
| 310 { | |
| 311 if (filter instanceof SpecialSubscription.defaultsMap[type]) | |
| 312 subscription.defaults = [type]; | |
| 313 } | |
| 314 if (!subscription.defaults) | |
| 315 subscription.defaults = ["blocking"]; | |
| 316 return subscription; | |
| 317 }; | |
| 318 | |
| 319 /** | |
| 320 * Abstract base class for regular filter subscriptions (both internally and ext
ernally updated) | |
| 321 * @param {String} url see Subscription() | |
| 322 * @param {String} [title] see Subscription() | |
| 323 * @constructor | |
| 324 * @augments Subscription | |
| 325 */ | |
| 326 function RegularSubscription(url, title) | |
| 327 { | |
| 328 Subscription.call(this, url, title || url); | |
| 329 } | |
| 330 exports.RegularSubscription = RegularSubscription; | |
| 331 | |
| 332 RegularSubscription.prototype = | |
| 333 { | |
| 334 __proto__: Subscription.prototype, | |
| 335 | |
| 336 _homepage: null, | |
| 337 _lastDownload: 0, | |
| 338 | |
| 339 /** | |
| 340 * Filter subscription homepage if known | |
| 341 * @type String | |
| 342 */ | |
| 343 get homepage() | |
| 344 { | |
| 345 return this._homepage; | |
| 346 }, | |
| 347 set homepage(value) | |
| 348 { | |
| 349 if (value != this._homepage) | |
| 350 { | |
| 351 let oldValue = this._homepage; | |
| 352 this._homepage = value; | |
| 353 FilterNotifier.triggerListeners("subscription.homepage", this, value, oldV
alue); | |
| 354 } | |
| 355 return this._homepage; | |
| 356 }, | |
| 357 | |
| 358 /** | |
| 359 * Time of the last subscription download (in seconds since the beginning of t
he epoch) | |
| 360 * @type Number | |
| 361 */ | |
| 362 get lastDownload() | |
| 363 { | |
| 364 return this._lastDownload; | |
| 365 }, | |
| 366 set lastDownload(value) | |
| 367 { | |
| 368 if (value != this._lastDownload) | |
| 369 { | |
| 370 let oldValue = this._lastDownload; | |
| 371 this._lastDownload = value; | |
| 372 FilterNotifier.triggerListeners("subscription.lastDownload", this, value,
oldValue); | |
| 373 } | |
| 374 return this._lastDownload; | |
| 375 }, | |
| 376 | |
| 377 /** | |
| 378 * See Subscription.serialize() | |
| 379 */ | |
| 380 serialize: function(buffer) | |
| 381 { | |
| 382 Subscription.prototype.serialize.call(this, buffer); | |
| 383 if (this._homepage) | |
| 384 buffer.push("homepage=" + this._homepage); | |
| 385 if (this._lastDownload) | |
| 386 buffer.push("lastDownload=" + this._lastDownload); | |
| 387 } | |
| 388 }; | |
| 389 | |
| 390 /** | |
| 391 * Class for filter subscriptions updated externally (by other extension) | |
| 392 * @param {String} url see Subscription() | |
| 393 * @param {String} [title] see Subscription() | |
| 394 * @constructor | |
| 395 * @augments RegularSubscription | |
| 396 */ | |
| 397 function ExternalSubscription(url, title) | |
| 398 { | |
| 399 RegularSubscription.call(this, url, title); | |
| 400 } | |
| 401 exports.ExternalSubscription = ExternalSubscription; | |
| 402 | |
| 403 ExternalSubscription.prototype = | |
| 404 { | |
| 405 __proto__: RegularSubscription.prototype, | |
| 406 | |
| 407 /** | |
| 408 * See Subscription.serialize() | |
| 409 */ | |
| 410 serialize: function(buffer) | |
| 411 { | |
| 412 throw new Error("Unexpected call, external subscriptions should not be seria
lized"); | |
| 413 } | |
| 414 }; | |
| 415 | |
| 416 /** | |
| 417 * Class for filter subscriptions updated externally (by other extension) | |
| 418 * @param {String} url see Subscription() | |
| 419 * @param {String} [title] see Subscription() | |
| 420 * @constructor | |
| 421 * @augments RegularSubscription | |
| 422 */ | |
| 423 function DownloadableSubscription(url, title) | |
| 424 { | |
| 425 RegularSubscription.call(this, url, title); | |
| 426 } | |
| 427 exports.DownloadableSubscription = DownloadableSubscription; | |
| 428 | |
| 429 DownloadableSubscription.prototype = | |
| 430 { | |
| 431 __proto__: RegularSubscription.prototype, | |
| 432 | |
| 433 _downloadStatus: null, | |
| 434 _lastCheck: 0, | |
| 435 _errors: 0, | |
| 436 | |
| 437 /** | |
| 438 * Status of the last download (ID of a string) | |
| 439 * @type String | |
| 440 */ | |
| 441 get downloadStatus() | |
| 442 { | |
| 443 return this._downloadStatus; | |
| 444 }, | |
| 445 set downloadStatus(value) | |
| 446 { | |
| 447 let oldValue = this._downloadStatus; | |
| 448 this._downloadStatus = value; | |
| 449 FilterNotifier.triggerListeners("subscription.downloadStatus", this, value,
oldValue); | |
| 450 return this._downloadStatus; | |
| 451 }, | |
| 452 | |
| 453 /** | |
| 454 * Time of the last successful download (in seconds since the beginning of the | |
| 455 * epoch). | |
| 456 */ | |
| 457 lastSuccess: 0, | |
| 458 | |
| 459 /** | |
| 460 * Time when the subscription was considered for an update last time (in secon
ds | |
| 461 * since the beginning of the epoch). This will be used to increase softExpira
tion | |
| 462 * if the user doesn't use Adblock Plus for some time. | |
| 463 * @type Number | |
| 464 */ | |
| 465 get lastCheck() | |
| 466 { | |
| 467 return this._lastCheck; | |
| 468 }, | |
| 469 set lastCheck(value) | |
| 470 { | |
| 471 if (value != this._lastCheck) | |
| 472 { | |
| 473 let oldValue = this._lastCheck; | |
| 474 this._lastCheck = value; | |
| 475 FilterNotifier.triggerListeners("subscription.lastCheck", this, value, old
Value); | |
| 476 } | |
| 477 return this._lastCheck; | |
| 478 }, | |
| 479 | |
| 480 /** | |
| 481 * Hard expiration time of the filter subscription (in seconds since the begin
ning of the epoch) | |
| 482 * @type Number | |
| 483 */ | |
| 484 expires: 0, | |
| 485 | |
| 486 /** | |
| 487 * Soft expiration time of the filter subscription (in seconds since the begin
ning of the epoch) | |
| 488 * @type Number | |
| 489 */ | |
| 490 softExpiration: 0, | |
| 491 | |
| 492 /** | |
| 493 * Number of download failures since last success | |
| 494 * @type Number | |
| 495 */ | |
| 496 get errors() | |
| 497 { | |
| 498 return this._errors; | |
| 499 }, | |
| 500 set errors(value) | |
| 501 { | |
| 502 if (value != this._errors) | |
| 503 { | |
| 504 let oldValue = this._errors; | |
| 505 this._errors = value; | |
| 506 FilterNotifier.triggerListeners("subscription.errors", this, value, oldVal
ue); | |
| 507 } | |
| 508 return this._errors; | |
| 509 }, | |
| 510 | |
| 511 /** | |
| 512 * Version of the subscription data retrieved on last successful download | |
| 513 * @type Number | |
| 514 */ | |
| 515 version: 0, | |
| 516 | |
| 517 /** | |
| 518 * Minimal Adblock Plus version required for this subscription | |
| 519 * @type String | |
| 520 */ | |
| 521 requiredVersion: null, | |
| 522 | |
| 523 /** | |
| 524 * Number indicating how often the object was downloaded. | |
| 525 * @type Number | |
| 526 */ | |
| 527 downloadCount: 0, | |
| 528 | |
| 529 /** | |
| 530 * See Subscription.serialize() | |
| 531 */ | |
| 532 serialize: function(buffer) | |
| 533 { | |
| 534 RegularSubscription.prototype.serialize.call(this, buffer); | |
| 535 if (this.downloadStatus) | |
| 536 buffer.push("downloadStatus=" + this.downloadStatus); | |
| 537 if (this.lastSuccess) | |
| 538 buffer.push("lastSuccess=" + this.lastSuccess); | |
| 539 if (this.lastCheck) | |
| 540 buffer.push("lastCheck=" + this.lastCheck); | |
| 541 if (this.expires) | |
| 542 buffer.push("expires=" + this.expires); | |
| 543 if (this.softExpiration) | |
| 544 buffer.push("softExpiration=" + this.softExpiration); | |
| 545 if (this.errors) | |
| 546 buffer.push("errors=" + this.errors); | |
| 547 if (this.version) | |
| 548 buffer.push("version=" + this.version); | |
| 549 if (this.requiredVersion) | |
| 550 buffer.push("requiredVersion=" + this.requiredVersion); | |
| 551 if (this.downloadCount) | |
| 552 buffer.push("downloadCount=" + this.downloadCount); | |
| 553 } | |
| 554 }; | |
| LEFT | RIGHT |