OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <http://adblockplus.org/>, | 2 * This file is part of Adblock Plus <http://adblockplus.org/>, |
3 * Copyright (C) 2006-2014 Eyeo GmbH | 3 * Copyright (C) 2006-2014 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 147 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
158 { | 158 { |
159 if (filter.text in this.keywordByFilter) | 159 if (filter.text in this.keywordByFilter) |
160 return this.keywordByFilter[filter.text]; | 160 return this.keywordByFilter[filter.text]; |
161 else | 161 else |
162 return null; | 162 return null; |
163 }, | 163 }, |
164 | 164 |
165 /** | 165 /** |
166 * Checks whether the entries for a particular keyword match a URL | 166 * Checks whether the entries for a particular keyword match a URL |
167 */ | 167 */ |
168 _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdPar
ty) | 168 _checkEntryMatch: function(keyword, location, contentType, docDomain, thirdPar
ty, sitekey) |
169 { | 169 { |
170 let list = this.filterByKeyword[keyword]; | 170 let list = this.filterByKeyword[keyword]; |
171 for (let i = 0; i < list.length; i++) | 171 for (let i = 0; i < list.length; i++) |
172 { | 172 { |
173 let filter = list[i]; | 173 let filter = list[i]; |
174 if (filter.matches(location, contentType, docDomain, thirdParty)) | 174 if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) |
175 return filter; | 175 return filter; |
176 } | 176 } |
177 return null; | 177 return null; |
178 }, | 178 }, |
179 | 179 |
180 /** | 180 /** |
181 * Tests whether the URL matches any of the known filters | 181 * Tests whether the URL matches any of the known filters |
182 * @param {String} location URL to be tested | 182 * @param {String} location URL to be tested |
183 * @param {String} contentType content type identifier of the URL | 183 * @param {String} contentType content type identifier of the URL |
184 * @param {String} docDomain domain name of the document that loads the URL | 184 * @param {String} docDomain domain name of the document that loads the URL |
185 * @param {Boolean} thirdParty should be true if the URL is a third-party requ
est | 185 * @param {Boolean} thirdParty should be true if the URL is a third-party requ
est |
| 186 * @param {String} sitekey public key provided by the document |
186 * @return {RegExpFilter} matching filter or null | 187 * @return {RegExpFilter} matching filter or null |
187 */ | 188 */ |
188 matchesAny: function(location, contentType, docDomain, thirdParty) | 189 matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) |
189 { | 190 { |
190 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | 191 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
191 if (candidates === null) | 192 if (candidates === null) |
192 candidates = []; | 193 candidates = []; |
193 candidates.push(""); | 194 candidates.push(""); |
194 for (let i = 0, l = candidates.length; i < l; i++) | 195 for (let i = 0, l = candidates.length; i < l; i++) |
195 { | 196 { |
196 let substr = candidates[i]; | 197 let substr = candidates[i]; |
197 if (substr in this.filterByKeyword) | 198 if (substr in this.filterByKeyword) |
198 { | 199 { |
199 let result = this._checkEntryMatch(substr, location, contentType, docDom
ain, thirdParty); | 200 let result = this._checkEntryMatch(substr, location, contentType, docDom
ain, thirdParty, sitekey); |
200 if (result) | 201 if (result) |
201 return result; | 202 return result; |
202 } | 203 } |
203 } | 204 } |
204 | 205 |
205 return null; | 206 return null; |
206 } | 207 } |
207 }; | 208 }; |
208 | 209 |
209 /** | 210 /** |
210 * Combines a matcher for blocking and exception rules, automatically sorts | 211 * Combines a matcher for blocking and exception rules, automatically sorts |
211 * rules into two Matcher instances. | 212 * rules into two Matcher instances. |
212 * @constructor | 213 * @constructor |
213 */ | 214 */ |
214 function CombinedMatcher() | 215 function CombinedMatcher() |
215 { | 216 { |
216 this.blacklist = new Matcher(); | 217 this.blacklist = new Matcher(); |
217 this.whitelist = new Matcher(); | 218 this.whitelist = new Matcher(); |
218 this.keys = Object.create(null); | |
219 this.resultCache = Object.create(null); | 219 this.resultCache = Object.create(null); |
220 } | 220 } |
221 exports.CombinedMatcher = CombinedMatcher; | 221 exports.CombinedMatcher = CombinedMatcher; |
222 | 222 |
223 /** | 223 /** |
224 * Maximal number of matching cache entries to be kept | 224 * Maximal number of matching cache entries to be kept |
225 * @type Number | 225 * @type Number |
226 */ | 226 */ |
227 CombinedMatcher.maxCacheEntries = 1000; | 227 CombinedMatcher.maxCacheEntries = 1000; |
228 | 228 |
229 CombinedMatcher.prototype = | 229 CombinedMatcher.prototype = |
230 { | 230 { |
231 /** | 231 /** |
232 * Matcher for blocking rules. | 232 * Matcher for blocking rules. |
233 * @type Matcher | 233 * @type Matcher |
234 */ | 234 */ |
235 blacklist: null, | 235 blacklist: null, |
236 | 236 |
237 /** | 237 /** |
238 * Matcher for exception rules. | 238 * Matcher for exception rules. |
239 * @type Matcher | 239 * @type Matcher |
240 */ | 240 */ |
241 whitelist: null, | 241 whitelist: null, |
242 | 242 |
243 /** | 243 /** |
244 * Exception rules that are limited by public keys, mapped by the correspondin
g keys. | |
245 * @type Object | |
246 */ | |
247 keys: null, | |
248 | |
249 /** | |
250 * Lookup table of previous matchesAny results | 244 * Lookup table of previous matchesAny results |
251 * @type Object | 245 * @type Object |
252 */ | 246 */ |
253 resultCache: null, | 247 resultCache: null, |
254 | 248 |
255 /** | 249 /** |
256 * Number of entries in resultCache | 250 * Number of entries in resultCache |
257 * @type Number | 251 * @type Number |
258 */ | 252 */ |
259 cacheEntries: 0, | 253 cacheEntries: 0, |
260 | 254 |
261 /** | 255 /** |
262 * @see Matcher#clear | 256 * @see Matcher#clear |
263 */ | 257 */ |
264 clear: function() | 258 clear: function() |
265 { | 259 { |
266 this.blacklist.clear(); | 260 this.blacklist.clear(); |
267 this.whitelist.clear(); | 261 this.whitelist.clear(); |
268 this.keys = Object.create(null); | |
269 this.resultCache = Object.create(null); | 262 this.resultCache = Object.create(null); |
270 this.cacheEntries = 0; | 263 this.cacheEntries = 0; |
271 }, | 264 }, |
272 | 265 |
273 /** | 266 /** |
274 * @see Matcher#add | 267 * @see Matcher#add |
275 */ | 268 */ |
276 add: function(filter) | 269 add: function(filter) |
277 { | 270 { |
278 if (filter instanceof WhitelistFilter) | 271 if (filter instanceof WhitelistFilter) |
279 { | 272 this.whitelist.add(filter); |
280 if (filter.siteKeys) | |
281 { | |
282 for (let i = 0; i < filter.siteKeys.length; i++) | |
283 this.keys[filter.siteKeys[i]] = filter.text; | |
284 } | |
285 else | |
286 this.whitelist.add(filter); | |
287 } | |
288 else | 273 else |
289 this.blacklist.add(filter); | 274 this.blacklist.add(filter); |
290 | 275 |
291 if (this.cacheEntries > 0) | 276 if (this.cacheEntries > 0) |
292 { | 277 { |
293 this.resultCache = Object.create(null); | 278 this.resultCache = Object.create(null); |
294 this.cacheEntries = 0; | 279 this.cacheEntries = 0; |
295 } | 280 } |
296 }, | 281 }, |
297 | 282 |
298 /** | 283 /** |
299 * @see Matcher#remove | 284 * @see Matcher#remove |
300 */ | 285 */ |
301 remove: function(filter) | 286 remove: function(filter) |
302 { | 287 { |
303 if (filter instanceof WhitelistFilter) | 288 if (filter instanceof WhitelistFilter) |
304 { | 289 this.whitelist.remove(filter); |
305 if (filter.siteKeys) | |
306 { | |
307 for (let i = 0; i < filter.siteKeys.length; i++) | |
308 delete this.keys[filter.siteKeys[i]]; | |
309 } | |
310 else | |
311 this.whitelist.remove(filter); | |
312 } | |
313 else | 290 else |
314 this.blacklist.remove(filter); | 291 this.blacklist.remove(filter); |
315 | 292 |
316 if (this.cacheEntries > 0) | 293 if (this.cacheEntries > 0) |
317 { | 294 { |
318 this.resultCache = Object.create(null); | 295 this.resultCache = Object.create(null); |
319 this.cacheEntries = 0; | 296 this.cacheEntries = 0; |
320 } | 297 } |
321 }, | 298 }, |
322 | 299 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 return !matcher.getKeywordForFilter(filter); | 340 return !matcher.getKeywordForFilter(filter); |
364 else | 341 else |
365 return !matcher.findKeyword(filter); | 342 return !matcher.findKeyword(filter); |
366 }, | 343 }, |
367 | 344 |
368 /** | 345 /** |
369 * Optimized filter matching testing both whitelist and blacklist matchers | 346 * Optimized filter matching testing both whitelist and blacklist matchers |
370 * simultaneously. For parameters see Matcher.matchesAny(). | 347 * simultaneously. For parameters see Matcher.matchesAny(). |
371 * @see Matcher#matchesAny | 348 * @see Matcher#matchesAny |
372 */ | 349 */ |
373 matchesAnyInternal: function(location, contentType, docDomain, thirdParty) | 350 matchesAnyInternal: function(location, contentType, docDomain, thirdParty, sit
ekey) |
374 { | 351 { |
375 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | 352 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
376 if (candidates === null) | 353 if (candidates === null) |
377 candidates = []; | 354 candidates = []; |
378 candidates.push(""); | 355 candidates.push(""); |
379 | 356 |
380 let blacklistHit = null; | 357 let blacklistHit = null; |
381 for (let i = 0, l = candidates.length; i < l; i++) | 358 for (let i = 0, l = candidates.length; i < l; i++) |
382 { | 359 { |
383 let substr = candidates[i]; | 360 let substr = candidates[i]; |
384 if (substr in this.whitelist.filterByKeyword) | 361 if (substr in this.whitelist.filterByKeyword) |
385 { | 362 { |
386 let result = this.whitelist._checkEntryMatch(substr, location, contentTy
pe, docDomain, thirdParty); | 363 let result = this.whitelist._checkEntryMatch(substr, location, contentTy
pe, docDomain, thirdParty, sitekey); |
387 if (result) | 364 if (result) |
388 return result; | 365 return result; |
389 } | 366 } |
390 if (substr in this.blacklist.filterByKeyword && blacklistHit === null) | 367 if (substr in this.blacklist.filterByKeyword && blacklistHit === null) |
391 blacklistHit = this.blacklist._checkEntryMatch(substr, location, content
Type, docDomain, thirdParty); | 368 blacklistHit = this.blacklist._checkEntryMatch(substr, location, content
Type, docDomain, thirdParty, sitekey); |
392 } | 369 } |
393 return blacklistHit; | 370 return blacklistHit; |
394 }, | 371 }, |
395 | 372 |
396 /** | 373 /** |
397 * @see Matcher#matchesAny | 374 * @see Matcher#matchesAny |
398 */ | 375 */ |
399 matchesAny: function(location, contentType, docDomain, thirdParty) | 376 matchesAny: function(location, contentType, docDomain, thirdParty, sitekey) |
400 { | 377 { |
401 let key = location + " " + contentType + " " + docDomain + " " + thirdParty; | 378 let key = location + " " + contentType + " " + docDomain + " " + thirdParty
+ " " + sitekey; |
402 if (key in this.resultCache) | 379 if (key in this.resultCache) |
403 return this.resultCache[key]; | 380 return this.resultCache[key]; |
404 | 381 |
405 let result = this.matchesAnyInternal(location, contentType, docDomain, third
Party); | 382 let result = this.matchesAnyInternal(location, contentType, docDomain, third
Party, sitekey); |
406 | 383 |
407 if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) | 384 if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) |
408 { | 385 { |
409 this.resultCache = Object.create(null); | 386 this.resultCache = Object.create(null); |
410 this.cacheEntries = 0; | 387 this.cacheEntries = 0; |
411 } | 388 } |
412 | 389 |
413 this.resultCache[key] = result; | 390 this.resultCache[key] = result; |
414 this.cacheEntries++; | 391 this.cacheEntries++; |
415 | 392 |
416 return result; | 393 return result; |
417 }, | |
418 | |
419 /** | |
420 * Looks up whether any filters match the given website key. | |
421 */ | |
422 matchesByKey: function(/**String*/ location, /**String*/ key, /**String*/ docD
omain) | |
423 { | |
424 key = key.toUpperCase(); | |
425 if (key in this.keys) | |
426 { | |
427 let filter = Filter.knownFilters[this.keys[key]]; | |
428 if (filter && filter.matches(location, "DOCUMENT", docDomain, false)) | |
429 return filter; | |
430 else | |
431 return null; | |
432 } | |
433 else | |
434 return null; | |
435 } | 394 } |
436 } | 395 } |
437 | 396 |
438 /** | 397 /** |
439 * Shared CombinedMatcher instance that should usually be used. | 398 * Shared CombinedMatcher instance that should usually be used. |
440 * @type CombinedMatcher | 399 * @type CombinedMatcher |
441 */ | 400 */ |
442 let defaultMatcher = exports.defaultMatcher = new CombinedMatcher(); | 401 let defaultMatcher = exports.defaultMatcher = new CombinedMatcher(); |
OLD | NEW |