LEFT | RIGHT |
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-2017 eyeo GmbH | 3 * Copyright (C) 2006-2017 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 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 | 246 |
247 // For rules containing only a hostname we know that we're matching against | 247 // For rules containing only a hostname we know that we're matching against |
248 // a lowercase string unless the matchCase option was passed. | 248 // a lowercase string unless the matchCase option was passed. |
249 if (parsed.canSafelyMatchAsLowercase && !filter.matchCase) | 249 if (parsed.canSafelyMatchAsLowercase && !filter.matchCase) |
250 trigger["url-filter"] = trigger["url-filter"].toLowerCase(); | 250 trigger["url-filter"] = trigger["url-filter"].toLowerCase(); |
251 | 251 |
252 if (parsed.canSafelyMatchAsLowercase || filter.matchCase) | 252 if (parsed.canSafelyMatchAsLowercase || filter.matchCase) |
253 trigger["url-filter-is-case-sensitive"] = true; | 253 trigger["url-filter-is-case-sensitive"] = true; |
254 | 254 |
255 let included = []; | 255 let included = []; |
256 let excluded = exceptionDomains || []; | 256 let excluded = []; |
257 | 257 |
258 parseDomains(filter.domains, included, excluded); | 258 parseDomains(filter.domains, included, excluded); |
| 259 |
| 260 if (exceptionDomains) |
| 261 excluded = excluded.concat(exceptionDomains); |
259 | 262 |
260 if (withResourceTypes) | 263 if (withResourceTypes) |
261 { | 264 { |
262 trigger["resource-type"] = getResourceTypes(filter); | 265 trigger["resource-type"] = getResourceTypes(filter); |
263 | 266 |
264 if (trigger["resource-type"].length == 0) | 267 if (trigger["resource-type"].length == 0) |
265 return; | 268 return; |
266 } | 269 } |
267 | 270 |
268 if (filter.thirdParty != null) | 271 if (filter.thirdParty != null) |
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
344 { | 347 { |
345 newSelector.push(selector.substring(i, pos.start)); | 348 newSelector.push(selector.substring(i, pos.start)); |
346 newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']'); | 349 newSelector.push('[id=', selector.substring(pos.start + 1, pos.end), ']'); |
347 i = pos.end; | 350 i = pos.end; |
348 } | 351 } |
349 newSelector.push(selector.substring(i)); | 352 newSelector.push(selector.substring(i)); |
350 | 353 |
351 return newSelector.join(""); | 354 return newSelector.join(""); |
352 } | 355 } |
353 | 356 |
| 357 function addCSSRules(rules, selectors, matchDomain) |
| 358 { |
| 359 while (selectors.length) |
| 360 { |
| 361 let selector = selectors.splice(0, selectorLimit).join(", "); |
| 362 |
| 363 // As of Safari 9.0 element IDs are matched as lowercase. We work around |
| 364 // this by converting to the attribute format [id="elementID"] |
| 365 selector = convertIDSelectorsToAttributeSelectors(selector); |
| 366 |
| 367 rules.push({ |
| 368 trigger: {"url-filter": matchDomain, |
| 369 "url-filter-is-case-sensitive": true}, |
| 370 action: {type: "css-display-none", |
| 371 selector: selector} |
| 372 }); |
| 373 } |
| 374 } |
| 375 |
354 let ContentBlockerList = | 376 let ContentBlockerList = |
355 /** | 377 /** |
356 * Create a new Adblock Plus filter to content blocker list converter | 378 * Create a new Adblock Plus filter to content blocker list converter |
357 * | 379 * |
358 * @constructor | 380 * @constructor |
359 */ | 381 */ |
360 exports.ContentBlockerList = function () | 382 exports.ContentBlockerList = function () |
361 { | 383 { |
362 this.requestFilters = []; | 384 this.requestFilters = []; |
363 this.requestExceptions = []; | 385 this.requestExceptions = []; |
364 this.elemhideFilters = []; | 386 this.elemhideFilters = []; |
365 this.elemhideExceptions = []; | 387 this.elemhideExceptions = []; |
366 this.genericblockExceptions = []; | 388 this.genericblockExceptions = []; |
| 389 this.generichideExceptions = []; |
367 this.elemhideSelectorExceptions = new Map(); | 390 this.elemhideSelectorExceptions = new Map(); |
368 }; | 391 }; |
369 | 392 |
370 /** | 393 /** |
371 * Add Adblock Plus filter to be converted | 394 * Add Adblock Plus filter to be converted |
372 * | 395 * |
373 * @param {Filter} filter Filter to convert | 396 * @param {Filter} filter Filter to convert |
374 */ | 397 */ |
375 ContentBlockerList.prototype.addFilter = function(filter) | 398 ContentBlockerList.prototype.addFilter = function(filter) |
376 { | 399 { |
377 if (filter.sitekeys) | 400 if (filter.sitekeys) |
378 return; | 401 return; |
379 if (filter instanceof filterClasses.RegExpFilter && | 402 if (filter instanceof filterClasses.RegExpFilter && |
380 filter.regexpSource == null) | 403 filter.regexpSource == null) |
381 return; | 404 return; |
382 | 405 |
383 if (filter instanceof filterClasses.BlockingFilter) | 406 if (filter instanceof filterClasses.BlockingFilter) |
384 this.requestFilters.push(filter); | 407 this.requestFilters.push(filter); |
385 | 408 |
386 if (filter instanceof filterClasses.WhitelistFilter) | 409 if (filter instanceof filterClasses.WhitelistFilter) |
387 { | 410 { |
388 if (filter.contentType & (typeMap.DOCUMENT | whitelistableRequestTypes)) | 411 if (filter.contentType & (typeMap.DOCUMENT | whitelistableRequestTypes)) |
389 this.requestExceptions.push(filter); | 412 this.requestExceptions.push(filter); |
390 | 413 |
391 if (filter.contentType & typeMap.ELEMHIDE) | |
392 this.elemhideExceptions.push(filter); | |
393 | |
394 if (filter.contentType & typeMap.GENERICBLOCK) | 414 if (filter.contentType & typeMap.GENERICBLOCK) |
395 this.genericblockExceptions.push(filter); | 415 this.genericblockExceptions.push(filter); |
| 416 |
| 417 if (filter.contentType & typeMap.ELEMHIDE) |
| 418 this.elemhideExceptions.push(filter); |
| 419 else if (filter.contentType & typeMap.GENERICHIDE) |
| 420 this.generichideExceptions.push(filter); |
396 } | 421 } |
397 | 422 |
398 if (filter instanceof filterClasses.ElemHideFilter) | 423 if (filter instanceof filterClasses.ElemHideFilter) |
399 this.elemhideFilters.push(filter); | 424 this.elemhideFilters.push(filter); |
400 | 425 |
401 if (filter instanceof filterClasses.ElemHideException) | 426 if (filter instanceof filterClasses.ElemHideException) |
402 { | 427 { |
403 let domains = this.elemhideSelectorExceptions[filter.selector]; | 428 let domains = this.elemhideSelectorExceptions[filter.selector]; |
404 if (!domains) | 429 if (!domains) |
405 domains = this.elemhideSelectorExceptions[filter.selector] = []; | 430 domains = this.elemhideSelectorExceptions[filter.selector] = []; |
406 | 431 |
407 parseDomains(filter.domains, domains, []); | 432 parseDomains(filter.domains, domains, []); |
408 } | 433 } |
409 }; | 434 }; |
410 | 435 |
411 /** | 436 /** |
412 * Generate content blocker list for all filters that were added | 437 * Generate content blocker list for all filters that were added |
413 * | 438 * |
414 * @returns {Filter} filter Filter to convert | 439 * @returns {Filter} filter Filter to convert |
415 */ | 440 */ |
416 ContentBlockerList.prototype.generateRules = function(filter) | 441 ContentBlockerList.prototype.generateRules = function(filter) |
417 { | 442 { |
418 let rules = []; | 443 let rules = []; |
419 | 444 |
| 445 let genericSelectors = []; |
420 let groupedElemhideFilters = new Map(); | 446 let groupedElemhideFilters = new Map(); |
| 447 |
421 for (let filter of this.elemhideFilters) | 448 for (let filter of this.elemhideFilters) |
422 { | 449 { |
423 let result = convertElemHideFilter(filter, this.elemhideSelectorExceptions); | 450 let result = convertElemHideFilter(filter, this.elemhideSelectorExceptions); |
424 if (!result) | 451 if (!result) |
425 continue; | 452 continue; |
426 | 453 |
427 if (result.matchDomains.length == 0) | 454 if (result.matchDomains.length == 0) |
428 result.matchDomains = ["^https?://"]; | |
429 | |
430 for (let matchDomain of result.matchDomains) | |
431 { | 455 { |
432 let group = groupedElemhideFilters.get(matchDomain) || []; | 456 genericSelectors.push(result.selector); |
433 group.push(result.selector); | |
434 groupedElemhideFilters.set(matchDomain, group); | |
435 } | 457 } |
436 } | 458 else |
| 459 { |
| 460 for (let matchDomain of result.matchDomains) |
| 461 { |
| 462 let group = groupedElemhideFilters.get(matchDomain) || []; |
| 463 group.push(result.selector); |
| 464 groupedElemhideFilters.set(matchDomain, group); |
| 465 } |
| 466 } |
| 467 } |
| 468 |
| 469 addCSSRules(rules, genericSelectors, "^https?://"); |
| 470 |
| 471 // Right after the generic element hiding filters, add the exceptions that |
| 472 // should apply only to those filters. |
| 473 for (let filter of this.generichideExceptions) |
| 474 convertFilterAddRules(rules, filter, "ignore-previous-rules", false); |
437 | 475 |
438 groupedElemhideFilters.forEach((selectors, matchDomain) => | 476 groupedElemhideFilters.forEach((selectors, matchDomain) => |
439 { | 477 { |
440 while (selectors.length) | 478 addCSSRules(rules, selectors, matchDomain); |
441 { | |
442 let selector = selectors.splice(0, selectorLimit).join(", "); | |
443 | |
444 // As of Safari 9.0 element IDs are matched as lowercase. We work around | |
445 // this by converting to the attribute format [id="elementID"] | |
446 selector = convertIDSelectorsToAttributeSelectors(selector); | |
447 | |
448 rules.push({ | |
449 trigger: {"url-filter": matchDomain, | |
450 "url-filter-is-case-sensitive": true}, | |
451 action: {type: "css-display-none", | |
452 selector: selector} | |
453 }); | |
454 } | |
455 }); | 479 }); |
456 | 480 |
457 for (let filter of this.elemhideExceptions) | 481 for (let filter of this.elemhideExceptions) |
458 convertFilterAddRules(rules, filter, "ignore-previous-rules", false); | 482 convertFilterAddRules(rules, filter, "ignore-previous-rules", false); |
459 | 483 |
460 let requestFilterExceptionDomains = []; | 484 let requestFilterExceptionDomains = []; |
461 for (let filter of this.genericblockExceptions) | 485 for (let filter of this.genericblockExceptions) |
462 { | 486 { |
463 let parsed = parseFilterRegexpSource(filter.regexpSource); | 487 let parsed = parseFilterRegexpSource(filter.regexpSource); |
464 if (parsed.hostname) | 488 if (parsed.hostname) |
465 requestFilterExceptionDomains.push(parsed.hostname); | 489 requestFilterExceptionDomains.push(parsed.hostname); |
466 } | 490 } |
467 | 491 |
468 for (let filter of this.requestFilters) | 492 for (let filter of this.requestFilters) |
469 { | 493 { |
470 convertFilterAddRules(rules, filter, "block", true, | 494 convertFilterAddRules(rules, filter, "block", true, |
471 requestFilterExceptionDomains); | 495 requestFilterExceptionDomains); |
472 } | 496 } |
473 | 497 |
474 for (let filter of this.requestExceptions) | 498 for (let filter of this.requestExceptions) |
475 convertFilterAddRules(rules, filter, "ignore-previous-rules", true); | 499 convertFilterAddRules(rules, filter, "ignore-previous-rules", true); |
476 | 500 |
477 return rules.filter(rule => !hasNonASCI(rule)); | 501 return rules.filter(rule => !hasNonASCI(rule)); |
478 }; | 502 }; |
LEFT | RIGHT |