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, docLocation, 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, docLocati
on, 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} docLocation location the document that loads the URL |
| 187 * @param {String} siteKey public key provided by the document |
186 * @return {RegExpFilter} matching filter or null | 188 * @return {RegExpFilter} matching filter or null |
187 */ | 189 */ |
188 matchesAny: function(location, contentType, docDomain, thirdParty) | 190 matchesAny: function(location, contentType, docDomain, thirdParty, docLocation
, siteKey) |
189 { | 191 { |
190 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | 192 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
191 if (candidates === null) | 193 if (candidates === null) |
192 candidates = []; | 194 candidates = []; |
193 candidates.push(""); | 195 candidates.push(""); |
194 for (let i = 0, l = candidates.length; i < l; i++) | 196 for (let i = 0, l = candidates.length; i < l; i++) |
195 { | 197 { |
196 let substr = candidates[i]; | 198 let substr = candidates[i]; |
197 if (substr in this.filterByKeyword) | 199 if (substr in this.filterByKeyword) |
198 { | 200 { |
199 let result = this._checkEntryMatch(substr, location, contentType, docDom
ain, thirdParty); | 201 let result = this._checkEntryMatch(substr, location, contentType, docDom
ain, thirdParty, docLocation, siteKey); |
200 if (result) | 202 if (result) |
201 return result; | 203 return result; |
202 } | 204 } |
203 } | 205 } |
204 | 206 |
205 return null; | 207 return null; |
206 } | 208 } |
207 }; | 209 }; |
208 | 210 |
209 /** | 211 /** |
210 * Combines a matcher for blocking and exception rules, automatically sorts | 212 * Combines a matcher for blocking and exception rules, automatically sorts |
211 * rules into two Matcher instances. | 213 * rules into two Matcher instances. |
212 * @constructor | 214 * @constructor |
213 */ | 215 */ |
214 function CombinedMatcher() | 216 function CombinedMatcher() |
215 { | 217 { |
216 this.blacklist = new Matcher(); | 218 this.blacklist = new Matcher(); |
217 this.whitelist = new Matcher(); | 219 this.whitelist = new Matcher(); |
218 this.keys = Object.create(null); | |
219 this.resultCache = Object.create(null); | 220 this.resultCache = Object.create(null); |
220 } | 221 } |
221 exports.CombinedMatcher = CombinedMatcher; | 222 exports.CombinedMatcher = CombinedMatcher; |
222 | 223 |
223 /** | 224 /** |
224 * Maximal number of matching cache entries to be kept | 225 * Maximal number of matching cache entries to be kept |
225 * @type Number | 226 * @type Number |
226 */ | 227 */ |
227 CombinedMatcher.maxCacheEntries = 1000; | 228 CombinedMatcher.maxCacheEntries = 1000; |
228 | 229 |
229 CombinedMatcher.prototype = | 230 CombinedMatcher.prototype = |
230 { | 231 { |
231 /** | 232 /** |
232 * Matcher for blocking rules. | 233 * Matcher for blocking rules. |
233 * @type Matcher | 234 * @type Matcher |
234 */ | 235 */ |
235 blacklist: null, | 236 blacklist: null, |
236 | 237 |
237 /** | 238 /** |
238 * Matcher for exception rules. | 239 * Matcher for exception rules. |
239 * @type Matcher | 240 * @type Matcher |
240 */ | 241 */ |
241 whitelist: null, | 242 whitelist: null, |
242 | 243 |
243 /** | 244 /** |
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 | 245 * Lookup table of previous matchesAny results |
251 * @type Object | 246 * @type Object |
252 */ | 247 */ |
253 resultCache: null, | 248 resultCache: null, |
254 | 249 |
255 /** | 250 /** |
256 * Number of entries in resultCache | 251 * Number of entries in resultCache |
257 * @type Number | 252 * @type Number |
258 */ | 253 */ |
259 cacheEntries: 0, | 254 cacheEntries: 0, |
260 | 255 |
261 /** | 256 /** |
262 * @see Matcher#clear | 257 * @see Matcher#clear |
263 */ | 258 */ |
264 clear: function() | 259 clear: function() |
265 { | 260 { |
266 this.blacklist.clear(); | 261 this.blacklist.clear(); |
267 this.whitelist.clear(); | 262 this.whitelist.clear(); |
268 this.keys = Object.create(null); | |
269 this.resultCache = Object.create(null); | 263 this.resultCache = Object.create(null); |
270 this.cacheEntries = 0; | 264 this.cacheEntries = 0; |
271 }, | 265 }, |
272 | 266 |
273 /** | 267 /** |
274 * @see Matcher#add | 268 * @see Matcher#add |
275 */ | 269 */ |
276 add: function(filter) | 270 add: function(filter) |
277 { | 271 { |
278 if (filter instanceof WhitelistFilter) | 272 if (filter instanceof WhitelistFilter) |
279 { | 273 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 | 274 else |
289 this.blacklist.add(filter); | 275 this.blacklist.add(filter); |
290 | 276 |
291 if (this.cacheEntries > 0) | 277 if (this.cacheEntries > 0) |
292 { | 278 { |
293 this.resultCache = Object.create(null); | 279 this.resultCache = Object.create(null); |
294 this.cacheEntries = 0; | 280 this.cacheEntries = 0; |
295 } | 281 } |
296 }, | 282 }, |
297 | 283 |
298 /** | 284 /** |
299 * @see Matcher#remove | 285 * @see Matcher#remove |
300 */ | 286 */ |
301 remove: function(filter) | 287 remove: function(filter) |
302 { | 288 { |
303 if (filter instanceof WhitelistFilter) | 289 if (filter instanceof WhitelistFilter) |
304 { | 290 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 | 291 else |
314 this.blacklist.remove(filter); | 292 this.blacklist.remove(filter); |
315 | 293 |
316 if (this.cacheEntries > 0) | 294 if (this.cacheEntries > 0) |
317 { | 295 { |
318 this.resultCache = Object.create(null); | 296 this.resultCache = Object.create(null); |
319 this.cacheEntries = 0; | 297 this.cacheEntries = 0; |
320 } | 298 } |
321 }, | 299 }, |
322 | 300 |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 return !matcher.getKeywordForFilter(filter); | 341 return !matcher.getKeywordForFilter(filter); |
364 else | 342 else |
365 return !matcher.findKeyword(filter); | 343 return !matcher.findKeyword(filter); |
366 }, | 344 }, |
367 | 345 |
368 /** | 346 /** |
369 * Optimized filter matching testing both whitelist and blacklist matchers | 347 * Optimized filter matching testing both whitelist and blacklist matchers |
370 * simultaneously. For parameters see Matcher.matchesAny(). | 348 * simultaneously. For parameters see Matcher.matchesAny(). |
371 * @see Matcher#matchesAny | 349 * @see Matcher#matchesAny |
372 */ | 350 */ |
373 matchesAnyInternal: function(location, contentType, docDomain, thirdParty) | 351 matchesAnyInternal: function(location, contentType, docDomain, thirdParty, doc
Location, siteKey) |
374 { | 352 { |
375 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); | 353 let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g); |
376 if (candidates === null) | 354 if (candidates === null) |
377 candidates = []; | 355 candidates = []; |
378 candidates.push(""); | 356 candidates.push(""); |
379 | 357 |
380 let blacklistHit = null; | 358 let blacklistHit = null; |
381 for (let i = 0, l = candidates.length; i < l; i++) | 359 for (let i = 0, l = candidates.length; i < l; i++) |
382 { | 360 { |
383 let substr = candidates[i]; | 361 let substr = candidates[i]; |
384 if (substr in this.whitelist.filterByKeyword) | 362 if (substr in this.whitelist.filterByKeyword) |
385 { | 363 { |
386 let result = this.whitelist._checkEntryMatch(substr, location, contentTy
pe, docDomain, thirdParty); | 364 let result = this.whitelist._checkEntryMatch(substr, location, contentTy
pe, docDomain, thirdParty, docLocation, siteKey); |
387 if (result) | 365 if (result) |
388 return result; | 366 return result; |
389 } | 367 } |
390 if (substr in this.blacklist.filterByKeyword && blacklistHit === null) | 368 if (substr in this.blacklist.filterByKeyword && blacklistHit === null) |
391 blacklistHit = this.blacklist._checkEntryMatch(substr, location, content
Type, docDomain, thirdParty); | 369 blacklistHit = this.blacklist._checkEntryMatch(substr, location, content
Type, docDomain, thirdParty, docLocation, siteKey); |
392 } | 370 } |
393 return blacklistHit; | 371 return blacklistHit; |
394 }, | 372 }, |
395 | 373 |
396 /** | 374 /** |
397 * @see Matcher#matchesAny | 375 * @see Matcher#matchesAny |
398 */ | 376 */ |
399 matchesAny: function(location, contentType, docDomain, thirdParty) | 377 matchesAny: function(location, contentType, docDomain, thirdParty, docLocation
, siteKey) |
400 { | 378 { |
401 let key = location + " " + contentType + " " + docDomain + " " + thirdParty; | 379 let key = location + " " + contentType + " " + docDomain + " " + thirdParty
+ " " + docLocation + " " + siteKey; |
402 if (key in this.resultCache) | 380 if (key in this.resultCache) |
403 return this.resultCache[key]; | 381 return this.resultCache[key]; |
404 | 382 |
405 let result = this.matchesAnyInternal(location, contentType, docDomain, third
Party); | 383 let result = this.matchesAnyInternal(location, contentType, docDomain, third
Party, docLocation, siteKey); |
406 | 384 |
407 if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) | 385 if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) |
408 { | 386 { |
409 this.resultCache = Object.create(null); | 387 this.resultCache = Object.create(null); |
410 this.cacheEntries = 0; | 388 this.cacheEntries = 0; |
411 } | 389 } |
412 | 390 |
413 this.resultCache[key] = result; | 391 this.resultCache[key] = result; |
414 this.cacheEntries++; | 392 this.cacheEntries++; |
415 | 393 |
416 return result; | 394 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 } | 395 } |
436 } | 396 } |
437 | 397 |
438 /** | 398 /** |
439 * Shared CombinedMatcher instance that should usually be used. | 399 * Shared CombinedMatcher instance that should usually be used. |
440 * @type CombinedMatcher | 400 * @type CombinedMatcher |
441 */ | 401 */ |
442 let defaultMatcher = exports.defaultMatcher = new CombinedMatcher(); | 402 let defaultMatcher = exports.defaultMatcher = new CombinedMatcher(); |
OLD | NEW |