Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Side by Side Diff: adblockplus.js

Issue 29336753: Issue 3671 - Split out contentBlockerList API (Closed)
Patch Set: Reworked API into ContentBlockerList class and addressed other feedback Created Feb. 22, 2016, 12:21 p.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | Download patch
OLDNEW
(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 // This file has been generated automatically, relevant repositories:
20 // * https://hg.adblockplus.org/adblockplus/
21 // * https://hg.adblockplus.org/jshydra/
22 //
23
24 function Filter(text)
25 {
26 this.text = text;
27 this.subscriptions = [];
28 }
29 exports.Filter = Filter;
30 Filter.prototype = {
31 text: null,
32 subscriptions: null,
33 get type()
34 {
35 throw new Error("Please define filter type in the subclass");
36 },
37 serialize: function(buffer)
38 {
39 buffer.push("[Filter]");
40 buffer.push("text=" + this.text);
41 },
42 toString: function()
43 {
44 return this.text;
45 }
46 };
47 Filter.knownFilters = Object.create(null);
48 Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?: [$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/;
49 Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[ ^,\s]+)?)*)?$/;
50 Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/ ;
51 Filter.csspropertyRegExp = /\[\-abp\-properties=(["'])([^"']+)\1\]/;
52 Filter.fromText = function(text)
53 {
54 if (text in Filter.knownFilters)
55 {
56 return Filter.knownFilters[text];
57 }
58 var ret;
59 var match = text.indexOf("#") >= 0 ? Filter.elemhideRegExp.exec(text) : null;
60 if (match)
61 {
62 ret = ElemHideBase.fromText(text, match[1], !!match[2], match[3], match[4], match[5]);
63 }
64 else if (text[0] == "!")
65 {
66 ret = new CommentFilter(text);
67 }
68 else
69 {
70 ret = RegExpFilter.fromText(text);
71 }
72 Filter.knownFilters[ret.text] = ret;
73 return ret;
74 };
75 Filter.fromObject = function(obj)
76 {
77 var ret = Filter.fromText(obj.text);
78 if (ret instanceof ActiveFilter)
79 {
80 if ("disabled" in obj)
81 {
82 ret._disabled = obj.disabled == "true";
83 }
84 if ("hitCount" in obj)
85 {
86 ret._hitCount = parseInt(obj.hitCount) || 0;
87 }
88 if ("lastHit" in obj)
89 {
90 ret._lastHit = parseInt(obj.lastHit) || 0;
91 }
92 }
93 return ret;
94 };
95 Filter.normalize = function(text)
96 {
97 if (!text)
98 {
99 return text;
100 }
101 text = text.replace(/[^\S ]/g, "");
102 if (/^\s*!/.test(text))
103 {
104 return text.trim();
105 }
106 else if (Filter.elemhideRegExp.test(text))
107 {
108 var _tempVar0 = /^(.*?)(#\@?#?)(.*)$/.exec(text);
109 var domain = _tempVar0[1];
110 var separator = _tempVar0[2];
111 var selector = _tempVar0[3];
112 return domain.replace(/\s/g, "") + separator + selector.trim();
113 }
114 else
115 {
116 return text.replace(/\s/g, "");
117 }
118 };
119 Filter.toRegExp = function(text)
120 {
121 return text.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&"). replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\ x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/ )(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/ , "").replace(/(\.\*)$/, "");
122 };
123
124 function InvalidFilter(text, reason)
125 {
126 Filter.call(this, text);
127 this.reason = reason;
128 }
129 exports.InvalidFilter = InvalidFilter;
130 InvalidFilter.prototype = {
131 __proto__: Filter.prototype,
132 type: "invalid",
133 reason: null,
134 serialize: function(buffer)
135 {}
136 };
137
138 function CommentFilter(text)
139 {
140 Filter.call(this, text);
141 }
142 exports.CommentFilter = CommentFilter;
143 CommentFilter.prototype = {
144 __proto__: Filter.prototype,
145 type: "comment",
146 serialize: function(buffer)
147 {}
148 };
149
150 function ActiveFilter(text, domains)
151 {
152 Filter.call(this, text);
153 this.domainSource = domains;
154 }
155 exports.ActiveFilter = ActiveFilter;
156 ActiveFilter.prototype = {
157 __proto__: Filter.prototype,
158 _disabled: false,
159 _hitCount: 0,
160 _lastHit: 0,
161 get disabled()
162 {
163 return this._disabled;
164 },
165 set disabled(value)
166 {
167 if (value != this._disabled)
168 {
169 var oldValue = this._disabled;
170 this._disabled = value;
171 }
172 return this._disabled;
173 },
174 get hitCount()
175 {
176 return this._hitCount;
177 },
178 set hitCount(value)
179 {
180 if (value != this._hitCount)
181 {
182 var oldValue = this._hitCount;
183 this._hitCount = value;
184 }
185 return this._hitCount;
186 },
187 get lastHit()
188 {
189 return this._lastHit;
190 },
191 set lastHit(value)
192 {
193 if (value != this._lastHit)
194 {
195 var oldValue = this._lastHit;
196 this._lastHit = value;
197 }
198 return this._lastHit;
199 },
200 domainSource: null,
201 domainSeparator: null,
202 ignoreTrailingDot: true,
203 domainSourceIsUpperCase: false,
204 get domains()
205 {
206 var prop = Object.getOwnPropertyDescriptor(this, "domains");
207 if (prop)
208 {
209 return prop.value;
210 }
211 var domains = null;
212 if (this.domainSource)
213 {
214 var source = this.domainSource;
215 if (!this.domainSourceIsUpperCase)
216 {
217 source = source.toUpperCase();
218 }
219 var list = source.split(this.domainSeparator);
220 if (list.length == 1 && list[0][0] != "~")
221 {
222 domains = {
223 __proto__: null,
224 "": false
225 };
226 if (this.ignoreTrailingDot)
227 {
228 list[0] = list[0].replace(/\.+$/, "");
229 }
230 domains[list[0]] = true;
231 }
232 else
233 {
234 var hasIncludes = false;
235 for (var i = 0; i < list.length; i++)
236 {
237 var domain = list[i];
238 if (this.ignoreTrailingDot)
239 {
240 domain = domain.replace(/\.+$/, "");
241 }
242 if (domain == "")
243 {
244 continue;
245 }
246 var include;
247 if (domain[0] == "~")
248 {
249 include = false;
250 domain = domain.substr(1);
251 }
252 else
253 {
254 include = true;
255 hasIncludes = true;
256 }
257 if (!domains)
258 {
259 domains = Object.create(null);
260 }
261 domains[domain] = include;
262 }
263 domains[""] = !hasIncludes;
264 }
265 this.domainSource = null;
266 }
267 Object.defineProperty(this, "domains",
268 {
269 value: domains,
270 enumerable: true
271 });
272 return this.domains;
273 },
274 sitekeys: null,
275 isActiveOnDomain: function(docDomain, sitekey)
276 {
277 if (this.sitekeys && (!sitekey || this.sitekeys.indexOf(sitekey.toUpperCase( )) < 0))
278 {
279 return false;
280 }
281 if (!this.domains)
282 {
283 return true;
284 }
285 if (!docDomain)
286 {
287 return this.domains[""];
288 }
289 if (this.ignoreTrailingDot)
290 {
291 docDomain = docDomain.replace(/\.+$/, "");
292 }
293 docDomain = docDomain.toUpperCase();
294 while (true)
295 {
296 if (docDomain in this.domains)
297 {
298 return this.domains[docDomain];
299 }
300 var nextDot = docDomain.indexOf(".");
301 if (nextDot < 0)
302 {
303 break;
304 }
305 docDomain = docDomain.substr(nextDot + 1);
306 }
307 return this.domains[""];
308 },
309 isActiveOnlyOnDomain: function(docDomain)
310 {
311 if (!docDomain || !this.domains || this.domains[""])
312 {
313 return false;
314 }
315 if (this.ignoreTrailingDot)
316 {
317 docDomain = docDomain.replace(/\.+$/, "");
318 }
319 docDomain = docDomain.toUpperCase();
320 for (var domain in this.domains)
321 {
322 if (this.domains[domain] && domain != docDomain && (domain.length <= docDo main.length || domain.indexOf("." + docDomain) != domain.length - docDomain.leng th - 1))
323 {
324 return false;
325 }
326 }
327 return true;
328 },
329 isGeneric: function()
330 {
331 return !(this.sitekeys && this.sitekeys.length) && (!this.domains || this.do mains[""]);
332 },
333 serialize: function(buffer)
334 {
335 if (this._disabled || this._hitCount || this._lastHit)
336 {
337 Filter.prototype.serialize.call(this, buffer);
338 if (this._disabled)
339 {
340 buffer.push("disabled=true");
341 }
342 if (this._hitCount)
343 {
344 buffer.push("hitCount=" + this._hitCount);
345 }
346 if (this._lastHit)
347 {
348 buffer.push("lastHit=" + this._lastHit);
349 }
350 }
351 }
352 };
353
354 function RegExpFilter(text, regexpSource, contentType, matchCase, domains, third Party, sitekeys)
355 {
356 ActiveFilter.call(this, text, domains, sitekeys);
357 if (contentType != null)
358 {
359 this.contentType = contentType;
360 }
361 if (matchCase)
362 {
363 this.matchCase = matchCase;
364 }
365 if (thirdParty != null)
366 {
367 this.thirdParty = thirdParty;
368 }
369 if (sitekeys != null)
370 {
371 this.sitekeySource = sitekeys;
372 }
373 if (regexpSource.length >= 2 && regexpSource[0] == "/" && regexpSource[regexpS ource.length - 1] == "/")
374 {
375 var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), thi s.matchCase ? "" : "i");
376 Object.defineProperty(this, "regexp",
377 {
378 value: regexp
379 });
380 }
381 else
382 {
383 this.regexpSource = regexpSource;
384 }
385 }
386 exports.RegExpFilter = RegExpFilter;
387 RegExpFilter.prototype = {
388 __proto__: ActiveFilter.prototype,
389 domainSourceIsUpperCase: true,
390 length: 1,
391 domainSeparator: "|",
392 regexpSource: null,
393 get regexp()
394 {
395 var prop = Object.getOwnPropertyDescriptor(this, "regexp");
396 if (prop)
397 {
398 return prop.value;
399 }
400 var source = Filter.toRegExp(this.regexpSource);
401 var regexp = new RegExp(source, this.matchCase ? "" : "i");
402 Object.defineProperty(this, "regexp",
403 {
404 value: regexp
405 });
406 return regexp;
407 },
408 contentType: 2147483647,
409 matchCase: false,
410 thirdParty: null,
411 sitekeySource: null,
412 get sitekeys()
413 {
414 var prop = Object.getOwnPropertyDescriptor(this, "sitekeys");
415 if (prop)
416 {
417 return prop.value;
418 }
419 var sitekeys = null;
420 if (this.sitekeySource)
421 {
422 sitekeys = this.sitekeySource.split("|");
423 this.sitekeySource = null;
424 }
425 Object.defineProperty(this, "sitekeys",
426 {
427 value: sitekeys,
428 enumerable: true
429 });
430 return this.sitekeys;
431 },
432 matches: function(location, typeMask, docDomain, thirdParty, sitekey)
433 {
434 if (this.contentType & typeMask && (this.thirdParty == null || this.thirdPar ty == thirdParty) && this.isActiveOnDomain(docDomain, sitekey) && this.regexp.te st(location))
435 {
436 return true;
437 }
438 return false;
439 }
440 };
441 Object.defineProperty(RegExpFilter.prototype, "0",
442 {
443 get: function()
444 {
445 return this;
446 }
447 });
448 RegExpFilter.fromText = function(text)
449 {
450 var blocking = true;
451 var origText = text;
452 if (text.indexOf("@@") == 0)
453 {
454 blocking = false;
455 text = text.substr(2);
456 }
457 var contentType = null;
458 var matchCase = null;
459 var domains = null;
460 var sitekeys = null;
461 var thirdParty = null;
462 var collapse = null;
463 var options;
464 var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null;
465 if (match)
466 {
467 options = match[1].toUpperCase().split(",");
468 text = match.input.substr(0, match.index);
469 for (var _loopIndex1 = 0; _loopIndex1 < options.length; ++_loopIndex1)
470 {
471 var option = options[_loopIndex1];
472 var value = null;
473 var separatorIndex = option.indexOf("=");
474 if (separatorIndex >= 0)
475 {
476 value = option.substr(separatorIndex + 1);
477 option = option.substr(0, separatorIndex);
478 }
479 option = option.replace(/-/, "_");
480 if (option in RegExpFilter.typeMap)
481 {
482 if (contentType == null)
483 {
484 contentType = 0;
485 }
486 contentType |= RegExpFilter.typeMap[option];
487 }
488 else if (option[0] == "~" && option.substr(1) in RegExpFilter.typeMap)
489 {
490 if (contentType == null)
491 {
492 contentType = RegExpFilter.prototype.contentType;
493 }
494 contentType &= ~RegExpFilter.typeMap[option.substr(1)];
495 }
496 else if (option == "MATCH_CASE")
497 {
498 matchCase = true;
499 }
500 else if (option == "~MATCH_CASE")
501 {
502 matchCase = false;
503 }
504 else if (option == "DOMAIN" && typeof value != "undefined")
505 {
506 domains = value;
507 }
508 else if (option == "THIRD_PARTY")
509 {
510 thirdParty = true;
511 }
512 else if (option == "~THIRD_PARTY")
513 {
514 thirdParty = false;
515 }
516 else if (option == "COLLAPSE")
517 {
518 collapse = true;
519 }
520 else if (option == "~COLLAPSE")
521 {
522 collapse = false;
523 }
524 else if (option == "SITEKEY" && typeof value != "undefined")
525 {
526 sitekeys = value;
527 }
528 else
529 {
530 return new InvalidFilter(origText, "Unknown option " + option.toLowerCas e());
531 }
532 }
533 }
534 try
535 {
536 if (blocking)
537 {
538 return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);
539 }
540 else
541 {
542 return new WhitelistFilter(origText, text, contentType, matchCase, domains , thirdParty, sitekeys);
543 }
544 }
545 catch (e)
546 {
547 return new InvalidFilter(origText, e);
548 }
549 };
550 RegExpFilter.typeMap = {
551 OTHER: 1,
552 SCRIPT: 2,
553 IMAGE: 4,
554 STYLESHEET: 8,
555 OBJECT: 16,
556 SUBDOCUMENT: 32,
557 DOCUMENT: 64,
558 XBL: 1,
559 PING: 1024,
560 XMLHTTPREQUEST: 2048,
561 OBJECT_SUBREQUEST: 4096,
562 DTD: 1,
563 MEDIA: 16384,
564 FONT: 32768,
565 BACKGROUND: 4,
566 POPUP: 268435456,
567 GENERICBLOCK: 536870912,
568 ELEMHIDE: 1073741824,
569 GENERICHIDE: 2147483648
570 };
571 RegExpFilter.prototype.contentType &= ~ (RegExpFilter.typeMap.DOCUMENT | RegExpF ilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP | RegExpFilter.typeMap.GENER ICHIDE | RegExpFilter.typeMap.GENERICBLOCK);
572
573 function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thi rdParty, sitekeys, collapse)
574 {
575 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, sitekeys);
576 this.collapse = collapse;
577 }
578 exports.BlockingFilter = BlockingFilter;
579 BlockingFilter.prototype = {
580 __proto__: RegExpFilter.prototype,
581 type: "blocking",
582 collapse: null
583 };
584
585 function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, th irdParty, sitekeys)
586 {
587 RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, t hirdParty, sitekeys);
588 }
589 exports.WhitelistFilter = WhitelistFilter;
590 WhitelistFilter.prototype = {
591 __proto__: RegExpFilter.prototype,
592 type: "whitelist"
593 };
594
595 function ElemHideBase(text, domains, selector)
596 {
597 ActiveFilter.call(this, text, domains || null);
598 if (domains)
599 {
600 this.selectorDomain = domains.replace(/,~[^,]+/g, "").replace(/^~[^,]+,?/, " ").toLowerCase();
601 }
602 this.selector = selector;
603 }
604 exports.ElemHideBase = ElemHideBase;
605 ElemHideBase.prototype = {
606 __proto__: ActiveFilter.prototype,
607 domainSeparator: ",",
608 ignoreTrailingDot: false,
609 selectorDomain: null,
610 selector: null
611 };
612 ElemHideBase.fromText = function(text, domain, isException, tagName, attrRules, selector)
613 {
614 if (!selector)
615 {
616 if (tagName == "*")
617 {
618 tagName = "";
619 }
620 var id = null;
621 var additional = "";
622 if (attrRules)
623 {
624 attrRules = attrRules.match(/\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\)/g);
625 for (var _loopIndex2 = 0; _loopIndex2 < attrRules.length; ++_loopIndex2)
626 {
627 var rule = attrRules[_loopIndex2];
628 rule = rule.substr(1, rule.length - 2);
629 var separatorPos = rule.indexOf("=");
630 if (separatorPos > 0)
631 {
632 rule = rule.replace(/=/, "=\"") + "\"";
633 additional += "[" + rule + "]";
634 }
635 else
636 {
637 if (id)
638 {
639 return new InvalidFilter(text);
640 }
641 id = rule;
642 }
643 }
644 }
645 if (id)
646 {
647 selector = tagName + "." + id + additional + "," + tagName + "#" + id + ad ditional;
648 }
649 else if (tagName || additional)
650 {
651 selector = tagName + additional;
652 }
653 else
654 {
655 return new InvalidFilter(text);
656 }
657 }
658 if (isException)
659 {
660 return new ElemHideException(text, domain, selector);
661 }
662 var match = Filter.csspropertyRegExp.exec(selector);
663 if (match)
664 {
665 if (!/,[^~][^,.]*\.[^,]/.test("," + domain))
666 {
667 return new InvalidFilter(text);
668 }
669 return new CSSPropertyFilter(text, domain, selector, match[2], selector.subs tr(0, match.index), selector.substr(match.index + match[0].length));
670 }
671 return new ElemHideFilter(text, domain, selector);
672 };
673
674 function ElemHideFilter(text, domains, selector)
675 {
676 ElemHideBase.call(this, text, domains, selector);
677 }
678 exports.ElemHideFilter = ElemHideFilter;
679 ElemHideFilter.prototype = {
680 __proto__: ElemHideBase.prototype,
681 type: "elemhide"
682 };
683
684 function ElemHideException(text, domains, selector)
685 {
686 ElemHideBase.call(this, text, domains, selector);
687 }
688 exports.ElemHideException = ElemHideException;
689 ElemHideException.prototype = {
690 __proto__: ElemHideBase.prototype,
691 type: "elemhideexception"
692 };
693
694 function CSSPropertyFilter(text, domains, selector, regexpSource, selectorPrefix , selectorSuffix)
695 {
696 ElemHideBase.call(this, text, domains, selector);
697 this.regexpSource = regexpSource;
698 this.selectorPrefix = selectorPrefix;
699 this.selectorSuffix = selectorSuffix;
700 }
701 exports.CSSPropertyFilter = CSSPropertyFilter;
702 CSSPropertyFilter.prototype = {
703 __proto__: ElemHideBase.prototype,
704 type: "cssproperty",
705 regexpSource: null,
706 selectorPrefix: null,
707 selectorSuffix: null,
708 get regexpString()
709 {
710 var prop = Object.getOwnPropertyDescriptor(this, "regexpString");
711 if (prop)
712 {
713 return prop.value;
714 }
715 var regexp = Filter.toRegExp(this.regexpSource);
716 Object.defineProperty(this, "regexpString",
717 {
718 value: regexp
719 });
720 return regexp;
721 }
722 };
OLDNEW
« no previous file with comments | « abp2blocklist.js ('k') | lib/contentBlockerList.js » ('j') | lib/contentBlockerList.js » ('J')

Powered by Google App Engine
This is Rietveld