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 |
(...skipping 19 matching lines...) Expand all Loading... |
30 */ | 30 */ |
31 function Matcher() | 31 function Matcher() |
32 { | 32 { |
33 this.clear(); | 33 this.clear(); |
34 } | 34 } |
35 exports.Matcher = Matcher; | 35 exports.Matcher = Matcher; |
36 | 36 |
37 Matcher.prototype = { | 37 Matcher.prototype = { |
38 /** | 38 /** |
39 * Lookup table for filters by their associated keyword | 39 * Lookup table for filters by their associated keyword |
40 * @type {Object} | 40 * @type {Map.<string,Filter>} |
41 */ | 41 */ |
42 filterByKeyword: null, | 42 filterByKeyword: null, |
43 | 43 |
44 /** | 44 /** |
45 * Lookup table for keywords by the filter text | 45 * Lookup table for keywords by the filter |
46 * @type {Object} | 46 * @type {Map.<Filter,string>} |
47 */ | 47 */ |
48 keywordByFilter: null, | 48 keywordByFilter: null, |
49 | 49 |
50 /** | 50 /** |
51 * Removes all known filters | 51 * Removes all known filters |
52 */ | 52 */ |
53 clear() | 53 clear() |
54 { | 54 { |
55 this.filterByKeyword = Object.create(null); | 55 this.filterByKeyword = new Map(); |
56 this.keywordByFilter = Object.create(null); | 56 this.keywordByFilter = new Map(); |
57 }, | 57 }, |
58 | 58 |
59 /** | 59 /** |
60 * Adds a filter to the matcher | 60 * Adds a filter to the matcher |
61 * @param {RegExpFilter} filter | 61 * @param {RegExpFilter} filter |
62 */ | 62 */ |
63 add(filter) | 63 add(filter) |
64 { | 64 { |
65 if (filter.text in this.keywordByFilter) | 65 if (this.keywordByFilter.has(filter)) |
66 return; | 66 return; |
67 | 67 |
68 // Look for a suitable keyword | 68 // Look for a suitable keyword |
69 let keyword = this.findKeyword(filter); | 69 let keyword = this.findKeyword(filter); |
70 let oldEntry = this.filterByKeyword[keyword]; | 70 let oldEntry = this.filterByKeyword.get(keyword); |
71 if (typeof oldEntry == "undefined") | 71 if (typeof oldEntry == "undefined") |
72 this.filterByKeyword[keyword] = filter; | 72 this.filterByKeyword.set(keyword, filter); |
73 else if (oldEntry.length == 1) | 73 else if (oldEntry.length == 1) |
74 this.filterByKeyword[keyword] = [oldEntry, filter]; | 74 this.filterByKeyword.set(keyword, [oldEntry, filter]); |
75 else | 75 else |
76 oldEntry.push(filter); | 76 oldEntry.push(filter); |
77 this.keywordByFilter[filter.text] = keyword; | 77 this.keywordByFilter.set(filter, keyword); |
78 }, | 78 }, |
79 | 79 |
80 /** | 80 /** |
81 * Removes a filter from the matcher | 81 * Removes a filter from the matcher |
82 * @param {RegExpFilter} filter | 82 * @param {RegExpFilter} filter |
83 */ | 83 */ |
84 remove(filter) | 84 remove(filter) |
85 { | 85 { |
86 if (!(filter.text in this.keywordByFilter)) | 86 let keyword = this.keywordByFilter.get(filter); |
| 87 if (typeof keyword == "undefined") |
87 return; | 88 return; |
88 | 89 |
89 let keyword = this.keywordByFilter[filter.text]; | 90 let list = this.filterByKeyword.get(keyword); |
90 let list = this.filterByKeyword[keyword]; | |
91 if (list.length <= 1) | 91 if (list.length <= 1) |
92 delete this.filterByKeyword[keyword]; | 92 this.filterByKeyword.delete(keyword); |
93 else | 93 else |
94 { | 94 { |
95 let index = list.indexOf(filter); | 95 let index = list.indexOf(filter); |
96 if (index >= 0) | 96 if (index >= 0) |
97 { | 97 { |
98 list.splice(index, 1); | 98 list.splice(index, 1); |
99 if (list.length == 1) | 99 if (list.length == 1) |
100 this.filterByKeyword[keyword] = list[0]; | 100 this.filterByKeyword.set(keyword, list[0]); |
101 } | 101 } |
102 } | 102 } |
103 | 103 |
104 delete this.keywordByFilter[filter.text]; | 104 this.keywordByFilter.delete(filter); |
105 }, | 105 }, |
106 | 106 |
107 /** | 107 /** |
108 * Chooses a keyword to be associated with the filter | 108 * Chooses a keyword to be associated with the filter |
109 * @param {Filter} filter | 109 * @param {Filter} filter |
110 * @return {string} keyword or an empty string if no keyword could be found | 110 * @return {string} keyword or an empty string if no keyword could be found |
111 */ | 111 */ |
112 findKeyword(filter) | 112 findKeyword(filter) |
113 { | 113 { |
114 let result = ""; | 114 let result = ""; |
(...skipping 15 matching lines...) Expand all Loading... |
130 ); | 130 ); |
131 if (!candidates) | 131 if (!candidates) |
132 return result; | 132 return result; |
133 | 133 |
134 let hash = this.filterByKeyword; | 134 let hash = this.filterByKeyword; |
135 let resultCount = 0xFFFFFF; | 135 let resultCount = 0xFFFFFF; |
136 let resultLength = 0; | 136 let resultLength = 0; |
137 for (let i = 0, l = candidates.length; i < l; i++) | 137 for (let i = 0, l = candidates.length; i < l; i++) |
138 { | 138 { |
139 let candidate = candidates[i].substr(1); | 139 let candidate = candidates[i].substr(1); |
140 let count = (candidate in hash ? hash[candidate].length : 0); | 140 let filters = hash.get(candidate); |
| 141 let count = typeof filters != "undefined" ? filters.length : 0; |
141 if (count < resultCount || | 142 if (count < resultCount || |
142 (count == resultCount && candidate.length > resultLength)) | 143 (count == resultCount && candidate.length > resultLength)) |
143 { | 144 { |
144 result = candidate; | 145 result = candidate; |
145 resultCount = count; | 146 resultCount = count; |
146 resultLength = candidate.length; | 147 resultLength = candidate.length; |
147 } | 148 } |
148 } | 149 } |
149 return result; | 150 return result; |
150 }, | 151 }, |
151 | 152 |
152 /** | 153 /** |
153 * Checks whether a particular filter is being matched against. | 154 * Checks whether a particular filter is being matched against. |
154 * @param {RegExpFilter} filter | 155 * @param {RegExpFilter} filter |
155 * @return {boolean} | 156 * @return {boolean} |
156 */ | 157 */ |
157 hasFilter(filter) | 158 hasFilter(filter) |
158 { | 159 { |
159 return (filter.text in this.keywordByFilter); | 160 return this.keywordByFilter.has(filter); |
160 }, | 161 }, |
161 | 162 |
162 /** | 163 /** |
163 * Returns the keyword used for a filter, null for unknown filters. | 164 * Returns the keyword used for a filter, null for unknown filters. |
164 * @param {RegExpFilter} filter | 165 * @param {RegExpFilter} filter |
165 * @return {string} | 166 * @return {?string} |
166 */ | 167 */ |
167 getKeywordForFilter(filter) | 168 getKeywordForFilter(filter) |
168 { | 169 { |
169 if (filter.text in this.keywordByFilter) | 170 let keyword = this.keywordByFilter.get(filter); |
170 return this.keywordByFilter[filter.text]; | 171 return typeof keyword != "undefined" ? keyword : null; |
171 return null; | |
172 }, | 172 }, |
173 | 173 |
174 /** | 174 /** |
175 * Checks whether the entries for a particular keyword match a URL | 175 * Checks whether the entries for a particular keyword match a URL |
176 * @param {string} keyword | 176 * @param {string} keyword |
177 * @param {string} location | 177 * @param {string} location |
178 * @param {number} typeMask | 178 * @param {number} typeMask |
179 * @param {string} docDomain | 179 * @param {string} docDomain |
180 * @param {boolean} thirdParty | 180 * @param {boolean} thirdParty |
181 * @param {string} sitekey | 181 * @param {string} sitekey |
182 * @param {boolean} specificOnly | 182 * @param {boolean} specificOnly |
183 * @return {?Filter} | 183 * @return {?Filter} |
184 */ | 184 */ |
185 _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, | 185 _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, |
186 specificOnly) | 186 specificOnly) |
187 { | 187 { |
188 let list = this.filterByKeyword[keyword]; | 188 let list = this.filterByKeyword.get(keyword); |
| 189 if (typeof list == "undefined") |
| 190 return null; |
189 for (let i = 0; i < list.length; i++) | 191 for (let i = 0; i < list.length; i++) |
190 { | 192 { |
191 let filter = list[i]; | 193 let filter = list[i]; |
192 | 194 |
193 if (specificOnly && filter.isGeneric() && | 195 if (specificOnly && filter.isGeneric() && |
194 !(filter instanceof WhitelistFilter)) | 196 !(filter instanceof WhitelistFilter)) |
195 continue; | 197 continue; |
196 | 198 |
197 if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey)) | 199 if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey)) |
198 return filter; | 200 return filter; |
(...skipping 19 matching lines...) Expand all Loading... |
218 * matching filter or null | 220 * matching filter or null |
219 */ | 221 */ |
220 matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) | 222 matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) |
221 { | 223 { |
222 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | 224 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
223 if (candidates === null) | 225 if (candidates === null) |
224 candidates = []; | 226 candidates = []; |
225 candidates.push(""); | 227 candidates.push(""); |
226 for (let i = 0, l = candidates.length; i < l; i++) | 228 for (let i = 0, l = candidates.length; i < l; i++) |
227 { | 229 { |
228 let substr = candidates[i]; | 230 let result = this._checkEntryMatch(candidates[i], location, typeMask, |
229 if (substr in this.filterByKeyword) | 231 docDomain, thirdParty, sitekey, |
230 { | 232 specificOnly); |
231 let result = this._checkEntryMatch(substr, location, typeMask, | 233 if (result) |
232 docDomain, thirdParty, sitekey, | 234 return result; |
233 specificOnly); | |
234 if (result) | |
235 return result; | |
236 } | |
237 } | 235 } |
238 | 236 |
239 return null; | 237 return null; |
240 } | 238 } |
241 }; | 239 }; |
242 | 240 |
243 /** | 241 /** |
244 * Combines a matcher for blocking and exception rules, automatically sorts | 242 * Combines a matcher for blocking and exception rules, automatically sorts |
245 * rules into two Matcher instances. | 243 * rules into two Matcher instances. |
246 * @constructor | 244 * @constructor |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 { | 393 { |
396 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | 394 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
397 if (candidates === null) | 395 if (candidates === null) |
398 candidates = []; | 396 candidates = []; |
399 candidates.push(""); | 397 candidates.push(""); |
400 | 398 |
401 let blacklistHit = null; | 399 let blacklistHit = null; |
402 for (let i = 0, l = candidates.length; i < l; i++) | 400 for (let i = 0, l = candidates.length; i < l; i++) |
403 { | 401 { |
404 let substr = candidates[i]; | 402 let substr = candidates[i]; |
405 if (substr in this.whitelist.filterByKeyword) | 403 let result = this.whitelist._checkEntryMatch( |
406 { | 404 substr, location, typeMask, docDomain, thirdParty, sitekey |
407 let result = this.whitelist._checkEntryMatch( | 405 ); |
408 substr, location, typeMask, docDomain, thirdParty, sitekey | 406 if (result) |
409 ); | 407 return result; |
410 if (result) | 408 if (blacklistHit === null) |
411 return result; | |
412 } | |
413 if (substr in this.blacklist.filterByKeyword && blacklistHit === null) | |
414 { | 409 { |
415 blacklistHit = this.blacklist._checkEntryMatch( | 410 blacklistHit = this.blacklist._checkEntryMatch( |
416 substr, location, typeMask, docDomain, thirdParty, sitekey, | 411 substr, location, typeMask, docDomain, thirdParty, sitekey, |
417 specificOnly | 412 specificOnly |
418 ); | 413 ); |
419 } | 414 } |
420 } | 415 } |
421 return blacklistHit; | 416 return blacklistHit; |
422 }, | 417 }, |
423 | 418 |
(...skipping 22 matching lines...) Expand all Loading... |
446 | 441 |
447 return result; | 442 return result; |
448 } | 443 } |
449 }; | 444 }; |
450 | 445 |
451 /** | 446 /** |
452 * Shared CombinedMatcher instance that should usually be used. | 447 * Shared CombinedMatcher instance that should usually be used. |
453 * @type {CombinedMatcher} | 448 * @type {CombinedMatcher} |
454 */ | 449 */ |
455 exports.defaultMatcher = new CombinedMatcher(); | 450 exports.defaultMatcher = new CombinedMatcher(); |
OLD | NEW |