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

Delta Between Two Patch Sets: lib/matcher.js

Issue 29869571: Issue 6741 - Use ES2015 classes in lib/matcher.js (Closed) Base URL: https://hg.adblockplus.org/adblockpluscore/
Left Patch Set: Created Aug. 30, 2018, 3:53 p.m.
Right Patch Set: Address PS4 Comments Created Sept. 7, 2018, 3:48 a.m.
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
« no previous file with change/comment | « no previous file | no next file » | no next file with change/comment »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
LEFTRIGHT
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 "use strict"; 18 "use strict";
19 19
20 /** 20 /**
21 * @fileOverview Matcher class implementing matching addresses against 21 * @fileOverview Matcher class implementing matching addresses against
22 * a list of filters. 22 * a list of filters.
23 */ 23 */
24 24
25 const {Filter, WhitelistFilter} = require("./filterClasses"); 25 const {WhitelistFilter} = require("./filterClasses");
26 26
27 /**
28 * Blacklist/whitelist filter matching
29 */
27 class Matcher 30 class Matcher
28 { 31 {
29 /**
Manish Jethani 2018/08/31 13:01:34 I think this rather sounds like the description of
Jon Sonesen 2018/09/02 16:43:20 Done.
30 * Blacklist/whitelist filter matching
31 */
32 constructor() 32 constructor()
33 { 33 {
34 /** 34 /**
35 * Lookup table for filters by their associated keyword 35 * Lookup table for filters by their associated keyword
36 * @type {Map.<string,(Filter|Filter[])>} 36 * @type {Map.<string,(Filter|Filter[])>}
37 */ 37 */
38 this.filterByKeyword = null; 38 this.filterByKeyword = new Map();
39 39
40 /** 40 /**
41 * Lookup table for keywords by the filter 41 * Lookup table for keywords by the filter
42 * @type {Map.<Filter,string>} 42 * @type {Map.<Filter,string>}
43 */ 43 */
44 this.keywordByFilter = null; 44 this.keywordByFilter = new Map();
45 this.clear();
Manish Jethani 2018/08/31 13:01:35 Let's leave a line before this.clear(). Actually
Jon Sonesen 2018/09/02 16:43:20 Done.
46 } 45 }
47 46
48 /** 47 /**
49 * Removes all known filters 48 * Removes all known filters
50 */ 49 */
51 clear() 50 clear()
52 { 51 {
53 this.filterByKeyword = new Map(); 52 this.filterByKeyword.clear();
Manish Jethani 2018/08/31 13:01:35 Since these are Map objects now, we can just call
Jon Sonesen 2018/09/02 16:43:19 Done.
54 this.keywordByFilter = new Map(); 53 this.keywordByFilter.clear();
55 } 54 }
56 55
57 /** 56 /**
58 * Adds a filter to the matcher 57 * Adds a filter to the matcher
59 * @param {RegExpFilter} filter 58 * @param {RegExpFilter} filter
60 */ 59 */
61 add(filter) 60 add(filter)
62 { 61 {
63 if (this.keywordByFilter.has(filter)) 62 if (this.keywordByFilter.has(filter))
64 return; 63 return;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
98 this.filterByKeyword.set(keyword, list[0]); 97 this.filterByKeyword.set(keyword, list[0]);
99 } 98 }
100 } 99 }
101 100
102 this.keywordByFilter.delete(filter); 101 this.keywordByFilter.delete(filter);
103 } 102 }
104 103
105 /** 104 /**
106 * Chooses a keyword to be associated with the filter 105 * Chooses a keyword to be associated with the filter
107 * @param {Filter} filter 106 * @param {Filter} filter
108 * @return {string} keyword or an empty string if no keyword could be found 107 * @returns {string} keyword or an empty string if no keyword could be found
Manish Jethani 2018/08/31 13:01:34 Let's s/@return/@returns/ here and throughout the
Jon Sonesen 2018/09/02 16:43:20 Done.
109 */ 108 */
110 findKeyword(filter) 109 findKeyword(filter)
111 { 110 {
112 let result = ""; 111 let result = "";
113 let {text} = filter; 112 let {pattern} = filter;
114 if (Filter.regexpRegExp.test(text)) 113 if (pattern == null)
115 return result; 114 return result;
116 115
117 // Remove options 116 let candidates = pattern.toLowerCase().match(
118 let match = Filter.optionsRegExp.exec(text);
119 if (match)
120 text = match.input.substr(0, match.index);
121
122 // Remove whitelist marker
123 if (text[0] == "@" && text[1] == "@")
124 text = text.substr(2);
125
126 let candidates = text.toLowerCase().match(
127 /[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g 117 /[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g
128 ); 118 );
129 if (!candidates) 119 if (!candidates)
130 return result; 120 return result;
131 121
132 let hash = this.filterByKeyword; 122 let hash = this.filterByKeyword;
133 let resultCount = 0xFFFFFF; 123 let resultCount = 0xFFFFFF;
134 let resultLength = 0; 124 let resultLength = 0;
135 for (let i = 0, l = candidates.length; i < l; i++) 125 for (let i = 0, l = candidates.length; i < l; i++)
136 { 126 {
137 let candidate = candidates[i].substr(1); 127 let candidate = candidates[i].substr(1);
138 let filters = hash.get(candidate); 128 let filters = hash.get(candidate);
139 let count = typeof filters != "undefined" ? filters.length : 0; 129 let count = typeof filters != "undefined" ? filters.length : 0;
140 if (count < resultCount || 130 if (count < resultCount ||
141 (count == resultCount && candidate.length > resultLength)) 131 (count == resultCount && candidate.length > resultLength))
142 { 132 {
143 result = candidate; 133 result = candidate;
144 resultCount = count; 134 resultCount = count;
145 resultLength = candidate.length; 135 resultLength = candidate.length;
146 } 136 }
147 } 137 }
148 return result; 138 return result;
149 } 139 }
150 140
151 /** 141 /**
152 * Checks whether a particular filter is being matched against. 142 * Checks whether a particular filter is being matched against.
153 * @param {RegExpFilter} filter 143 * @param {RegExpFilter} filter
154 * @return {boolean} 144 * @returns {boolean}
155 */ 145 */
156 hasFilter(filter) 146 hasFilter(filter)
157 { 147 {
158 return this.keywordByFilter.has(filter); 148 return this.keywordByFilter.has(filter);
159 } 149 }
160 150
161 /** 151 /**
162 * Returns the keyword used for a filter, null for unknown filters. 152 * Returns the keyword used for a filter, <code>null</code>
153 * for unknown filters.
163 * @param {RegExpFilter} filter 154 * @param {RegExpFilter} filter
164 * @return {?string} 155 * @returns {?string}
165 */ 156 */
166 getKeywordForFilter(filter) 157 getKeywordForFilter(filter)
167 { 158 {
168 let keyword = this.keywordByFilter.get(filter); 159 let keyword = this.keywordByFilter.get(filter);
169 return typeof keyword != "undefined" ? keyword : null; 160 return typeof keyword != "undefined" ? keyword : null;
170 } 161 }
171 162
172 /** 163 /**
173 * Checks whether the entries for a particular keyword match a URL 164 * Checks whether the entries for a particular keyword match a URL
174 * @param {string} keyword 165 * @param {string} keyword
175 * @param {string} location 166 * @param {string} location
176 * @param {number} typeMask 167 * @param {number} typeMask
177 * @param {string} docDomain 168 * @param {string} [docDomain]
Manish Jethani 2018/08/31 13:01:34 docDomain, thirdParty, sitekey, and specificOnly a
Jon Sonesen 2018/09/02 16:43:20 Done.
178 * @param {boolean} thirdParty 169 * @param {boolean} [thirdParty]
179 * @param {string} sitekey 170 * @param {string} [sitekey]
180 * @param {boolean} specificOnly 171 * @param {boolean} [specificOnly]
181 * @return {?Filter} 172 * @returns {?Filter}
182 */ 173 */
183 _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey, 174 _checkEntryMatch(keyword, location, typeMask, docDomain, thirdParty, sitekey,
184 specificOnly) 175 specificOnly)
185 { 176 {
186 let list = this.filterByKeyword.get(keyword); 177 let list = this.filterByKeyword.get(keyword);
187 if (typeof list == "undefined") 178 if (typeof list == "undefined")
188 return null; 179 return null;
189 for (let i = 0; i < list.length; i++) 180 for (let i = 0; i < list.length; i++)
190 { 181 {
191 let filter = list[i]; 182 let filter = list[i];
192 183
193 if (specificOnly && filter.isGeneric() && 184 if (specificOnly && filter.isGeneric() &&
194 !(filter instanceof WhitelistFilter)) 185 !(filter instanceof WhitelistFilter))
195 continue; 186 continue;
196 187
197 if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey)) 188 if (filter.matches(location, typeMask, docDomain, thirdParty, sitekey))
198 return filter; 189 return filter;
199 } 190 }
200 return null; 191 return null;
201 } 192 }
202 193
203 /** 194 /**
204 * Tests whether the URL matches any of the known filters 195 * Tests whether the URL matches any of the known filters
205 * @param {string} location 196 * @param {string} location
206 * URL to be tested 197 * URL to be tested
207 * @param {number} typeMask 198 * @param {number} typeMask
208 * bitmask of content / request types to match 199 * bitmask of content / request types to match
209 * @param {string} docDomain 200 * @param {string} [docDomain]
Manish Jethani 2018/08/31 13:01:35 See comment above about optional parameters.
Jon Sonesen 2018/09/02 16:43:20 Done.
210 * domain name of the document that loads the URL 201 * domain name of the document that loads the URL
211 * @param {boolean} thirdParty 202 * @param {boolean} [thirdParty]
212 * should be true if the URL is a third-party request 203 * should be true if the URL is a third-party request
213 * @param {string} sitekey 204 * @param {string} [sitekey]
214 * public key provided by the document 205 * public key provided by the document
215 * @param {boolean} specificOnly 206 * @param {boolean} [specificOnly]
216 * should be true if generic matches should be ignored 207 * should be <code>true</code> if generic matches should be ignored
Manish Jethani 2018/08/31 13:01:35 Let's make this <code>true</code>.
Jon Sonesen 2018/09/02 16:43:20 Done.
217 * @return {?RegExpFilter} 208 * @returns {?RegExpFilter}
218 * matching filter or null 209 * matching filter or <code>null</code>
Manish Jethani 2018/08/31 13:01:34 Let's make this <code>null</code>.
Jon Sonesen 2018/09/02 16:43:19 Done.
219 */ 210 */
220 matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly) 211 matchesAny(location, typeMask, docDomain, thirdParty, sitekey, specificOnly)
221 { 212 {
222 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); 213 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
223 if (candidates === null) 214 if (candidates === null)
224 candidates = []; 215 candidates = [];
225 candidates.push(""); 216 candidates.push("");
226 for (let i = 0, l = candidates.length; i < l; i++) 217 for (let i = 0, l = candidates.length; i < l; i++)
227 { 218 {
228 let result = this._checkEntryMatch(candidates[i], location, typeMask, 219 let result = this._checkEntryMatch(candidates[i], location, typeMask,
229 docDomain, thirdParty, sitekey, 220 docDomain, thirdParty, sitekey,
230 specificOnly); 221 specificOnly);
231 if (result) 222 if (result)
232 return result; 223 return result;
233 } 224 }
234 225
235 return null; 226 return null;
236 } 227 }
237 } 228 }
Manish Jethani 2018/08/31 13:01:34 Let's leave a blank line after the class definitio
Jon Sonesen 2018/09/02 16:43:20 Done.
229
238 exports.Matcher = Matcher; 230 exports.Matcher = Matcher;
239 231
240 232
241 233 /**
234 * Combines a matcher for blocking and exception rules, automatically sorts
235 * rules into two {@link Matcher} instances.
236 */
242 class CombinedMatcher 237 class CombinedMatcher
243 { 238 {
244 /**
245 * Combines a matcher for blocking and exception rules, automatically sorts
Manish Jethani 2018/08/31 13:01:34 Let's move this to above the class definition.
246 * rules into two Matcher instances.
Manish Jethani 2018/08/31 13:01:35 Let's change `Matcher` here to `{@link Matcher}` s
247 * @augments Matcher
Manish Jethani 2018/08/31 13:01:34 Let's remove @arguments here, it's outdated.
248 */
249 constructor() 239 constructor()
250 { 240 {
251 /** 241 /**
252 * Maximal number of matching cache entries to be kept 242 * Maximal number of matching cache entries to be kept
253 * @type {number} 243 * @type {number}
254 */ 244 */
255 this.maxCacheEntries = 1000; 245 this.maxCacheEntries = 1000;
256 246
257 /** 247 /**
258 * Matcher for blocking rules. 248 * Matcher for blocking rules.
259 * @type {Matcher} 249 * @type {Matcher}
260 */ 250 */
261 this.blacklist = new Matcher(); 251 this.blacklist = new Matcher();
262 252
263 /** 253 /**
264 * Matcher for exception rules. 254 * Matcher for exception rules.
265 * @type {Matcher} 255 * @type {Matcher}
266 */ 256 */
267 this.whitelist = new Matcher(); 257 this.whitelist = new Matcher();
268 258
269 /** 259 /**
270 * Lookup table of previous matchesAny results 260 * Lookup table of previous {@link Matcher#matchesAny} results
Manish Jethani 2018/08/31 13:01:35 Let's make this `{@link Matcher#matchesAny}`
Jon Sonesen 2018/09/02 16:43:20 Done.
271 * @type {Map.<string,Filter>} 261 * @type {Map.<string,Filter>}
272 */ 262 */
273 this.resultCache = new Map(); 263 this.resultCache = new Map();
274 } 264 }
275 265
276 /** 266 /**
277 * @see Matcher#clear 267 * @see Matcher#clear
278 */ 268 */
279 clear() 269 clear()
280 { 270 {
(...skipping 26 matching lines...) Expand all
307 this.whitelist.remove(filter); 297 this.whitelist.remove(filter);
308 else 298 else
309 this.blacklist.remove(filter); 299 this.blacklist.remove(filter);
310 300
311 this.resultCache.clear(); 301 this.resultCache.clear();
312 } 302 }
313 303
314 /** 304 /**
315 * @see Matcher#findKeyword 305 * @see Matcher#findKeyword
316 * @param {Filter} filter 306 * @param {Filter} filter
317 * @return {string} keyword 307 * @returns {string} keyword
318 */ 308 */
319 findKeyword(filter) 309 findKeyword(filter)
320 { 310 {
321 if (filter instanceof WhitelistFilter) 311 if (filter instanceof WhitelistFilter)
322 return this.whitelist.findKeyword(filter); 312 return this.whitelist.findKeyword(filter);
323 return this.blacklist.findKeyword(filter); 313 return this.blacklist.findKeyword(filter);
324 } 314 }
325 315
326 /** 316 /**
327 * @see Matcher#hasFilter 317 * @see Matcher#hasFilter
328 * @param {Filter} filter 318 * @param {Filter} filter
329 * @return {boolean} 319 * @returns {boolean}
330 */ 320 */
331 hasFilter(filter) 321 hasFilter(filter)
332 { 322 {
333 if (filter instanceof WhitelistFilter) 323 if (filter instanceof WhitelistFilter)
334 return this.whitelist.hasFilter(filter); 324 return this.whitelist.hasFilter(filter);
335 return this.blacklist.hasFilter(filter); 325 return this.blacklist.hasFilter(filter);
336 } 326 }
337 327
338 /** 328 /**
339 * @see Matcher#getKeywordForFilter 329 * @see Matcher#getKeywordForFilter
340 * @param {Filter} filter 330 * @param {Filter} filter
341 * @return {string} keyword 331 * @returns {string} keyword
342 */ 332 */
343 getKeywordForFilter(filter) 333 getKeywordForFilter(filter)
344 { 334 {
345 if (filter instanceof WhitelistFilter) 335 if (filter instanceof WhitelistFilter)
346 return this.whitelist.getKeywordForFilter(filter); 336 return this.whitelist.getKeywordForFilter(filter);
347 return this.blacklist.getKeywordForFilter(filter); 337 return this.blacklist.getKeywordForFilter(filter);
348 } 338 }
349 339
350 /** 340 /**
351 * Checks whether a particular filter is slow 341 * Checks whether a particular filter is slow
352 * @param {RegExpFilter} filter 342 * @param {RegExpFilter} filter
353 * @return {boolean} 343 * @returns {boolean}
354 */ 344 */
355 isSlowFilter(filter) 345 isSlowFilter(filter)
356 { 346 {
357 let matcher = ( 347 let matcher = (
358 filter instanceof WhitelistFilter ? this.whitelist : this.blacklist 348 filter instanceof WhitelistFilter ? this.whitelist : this.blacklist
359 ); 349 );
360 if (matcher.hasFilter(filter)) 350 if (matcher.hasFilter(filter))
361 return !matcher.getKeywordForFilter(filter); 351 return !matcher.getKeywordForFilter(filter);
362 return !matcher.findKeyword(filter); 352 return !matcher.findKeyword(filter);
363 } 353 }
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
413 result = this.matchesAnyInternal(location, typeMask, docDomain, 403 result = this.matchesAnyInternal(location, typeMask, docDomain,
414 thirdParty, sitekey, specificOnly); 404 thirdParty, sitekey, specificOnly);
415 405
416 if (this.resultCache.size >= CombinedMatcher.maxCacheEntries) 406 if (this.resultCache.size >= CombinedMatcher.maxCacheEntries)
417 this.resultCache.clear(); 407 this.resultCache.clear();
418 408
419 this.resultCache.set(key, result); 409 this.resultCache.set(key, result);
420 410
421 return result; 411 return result;
422 } 412 }
423 } 413 }
Manish Jethani 2018/08/31 13:01:34 Let's leave a blank line here.
Jon Sonesen 2018/09/02 16:43:20 Done.
414
424 exports.CombinedMatcher = CombinedMatcher; 415 exports.CombinedMatcher = CombinedMatcher;
425 416
426 /** 417 /**
427 * Shared CombinedMatcher instance that should usually be used. 418 * Shared {@link CombinedMatcher} instance that should usually be used.
Manish Jethani 2018/08/31 13:01:35 Let's make this `{@link CombinedMatcher}`
Jon Sonesen 2018/09/02 16:43:20 Done.
428 * @type {CombinedMatcher} 419 * @type {CombinedMatcher}
429 */ 420 */
430 exports.defaultMatcher = new CombinedMatcher(); 421 let defaultMatcher = new CombinedMatcher();
422
423 exports.defaultMatcher = defaultMatcher;
LEFTRIGHT
« no previous file | no next file » | Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Toggle Comments ('s')

Powered by Google App Engine
This is Rietveld