1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-07 03:12:33 +01:00
Raymond Hill 2018-04-29 09:07:12 -04:00
parent f79c6ced20
commit 674c3c6079
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -462,27 +462,21 @@ var filterTypes = {
// Extract the best possible cosmetic filter, i.e. as specific as possible. // Extract the best possible cosmetic filter, i.e. as specific as possible.
// https://github.com/gorhill/uBlock/issues/1725 // https://github.com/gorhill/uBlock/issues/1725
// Also take into account the `src` attribute for `img` elements -- and limit // Also take into account the `src` attribute for `img` elements -- and limit
// the value to the 1024 first characters. // the value to the 1024 first characters.
var cosmeticFilterFromElement = function(elem) { var cosmeticFilterFromElement = function(elem) {
if ( elem === null ) { if ( elem === null ) { return 0; }
return 0; if ( elem.nodeType !== 1 ) { return 0; }
}
if ( elem.nodeType !== 1 ) {
return 0;
}
if ( candidateElements.indexOf(elem) === -1 ) { if ( candidateElements.indexOf(elem) === -1 ) {
candidateElements.push(elem); candidateElements.push(elem);
} }
var tagName = elem.localName; let selector = '';
var selector = '';
var v, i;
// Id // Id
v = typeof elem.id === 'string' && CSS.escape(elem.id); let v = typeof elem.id === 'string' && CSS.escape(elem.id);
if ( v ) { if ( v ) {
selector = '#' + v; selector = '#' + v;
} }
@ -490,18 +484,20 @@ var cosmeticFilterFromElement = function(elem) {
// Class(es) // Class(es)
v = elem.classList; v = elem.classList;
if ( v ) { if ( v ) {
i = v.length || 0; let i = v.length || 0;
while ( i-- ) { while ( i-- ) {
selector += '.' + CSS.escape(v.item(i)); selector += '.' + CSS.escape(v.item(i));
} }
} }
// Tag name // Tag name
let tagName = elem.localName;
// Use attributes if still no selector found.
// https://github.com/gorhill/uBlock/issues/1901 // https://github.com/gorhill/uBlock/issues/1901
// Trim attribute value, this may help in case of malformed HTML. // Trim attribute value, this may help in case of malformed HTML.
if ( selector === '' ) { if ( selector === '' ) {
selector = tagName; let attributes = [], attr;
var attributes = [], attr;
switch ( tagName ) { switch ( tagName ) {
case 'a': case 'a':
v = elem.getAttribute('href'); v = elem.getAttribute('href');
@ -529,13 +525,11 @@ var cosmeticFilterFromElement = function(elem) {
break; break;
} }
while ( (attr = attributes.pop()) ) { while ( (attr = attributes.pop()) ) {
if ( attr.v.length === 0 ) { if ( attr.v.length === 0 ) { continue; }
continue;
}
v = elem.getAttribute(attr.k); v = elem.getAttribute(attr.k);
if ( attr.v === v ) { if ( attr.v === v ) {
selector += '[' + attr.k + '="' + attr.v + '"]'; selector += '[' + attr.k + '="' + attr.v + '"]';
} else if ( v.lastIndexOf(attr.v, 0) === 0 ) { } else if ( v.startsWith(attr.v) ) {
selector += '[' + attr.k + '^="' + attr.v + '"]'; selector += '[' + attr.k + '^="' + attr.v + '"]';
} else { } else {
selector += '[' + attr.k + '*="' + attr.v + '"]'; selector += '[' + attr.k + '*="' + attr.v + '"]';
@ -543,19 +537,31 @@ var cosmeticFilterFromElement = function(elem) {
} }
} }
// https://github.com/uBlockOrigin/uBlock-issues/issues/17
// If selector is ambiguous at this point, add the element name to
// further narrow it down.
let parentNode = elem.parentNode;
if (
selector === '' ||
safeQuerySelectorAll(parentNode, cssScope + selector).length > 1
) {
selector = tagName + selector;
}
// https://github.com/chrisaljoudi/uBlock/issues/637 // https://github.com/chrisaljoudi/uBlock/issues/637
// If the selector is still ambiguous at this point, further narrow using // If the selector is still ambiguous at this point, further narrow using
// `nth-of-type`. It is preferable to use `nth-of-type` as opposed to // `nth-of-type`. It is preferable to use `nth-of-type` as opposed to
// `nth-child`, as `nth-of-type` is less volatile. // `nth-child`, as `nth-of-type` is less volatile.
var parentNode = elem.parentNode;
if ( safeQuerySelectorAll(parentNode, cssScope + selector).length > 1 ) { if ( safeQuerySelectorAll(parentNode, cssScope + selector).length > 1 ) {
i = 1; let i = 1;
while ( elem.previousSibling !== null ) { while ( elem.previousSibling !== null ) {
elem = elem.previousSibling; elem = elem.previousSibling;
if ( typeof elem.localName !== 'string' || elem.localName !== tagName ) { if (
continue; typeof elem.localName === 'string' &&
elem.localName === tagName
) {
i++;
} }
i++;
} }
selector += ':nth-of-type(' + i + ')'; selector += ':nth-of-type(' + i + ')';
} }
@ -582,7 +588,7 @@ var filtersFrom = function(x, y) {
candidateElements.length = 0; candidateElements.length = 0;
// We need at least one element. // We need at least one element.
var first = null; let first = null;
if ( typeof x === 'number' ) { if ( typeof x === 'number' ) {
first = elementFromPoint(x, y); first = elementFromPoint(x, y);
} else if ( x instanceof HTMLElement ) { } else if ( x instanceof HTMLElement ) {
@ -596,23 +602,29 @@ var filtersFrom = function(x, y) {
} }
// Cosmetic filter candidates from ancestors. // Cosmetic filter candidates from ancestors.
var elem = first; let elem = first;
while ( elem && elem !== document.body ) { while ( elem && elem !== document.body ) {
cosmeticFilterFromElement(elem); cosmeticFilterFromElement(elem);
elem = elem.parentNode; elem = elem.parentNode;
} }
// The body tag is needed as anchor only when the immediate child // The body tag is needed as anchor only when the immediate child
// uses`nth-of-type`. // uses `nth-of-type`.
var i = cosmeticFilterCandidates.length; let i = cosmeticFilterCandidates.length;
if ( i !== 0 && cosmeticFilterCandidates[i-1].indexOf(':nth-of-type(') !== -1 ) { if ( i !== 0 ) {
cosmeticFilterCandidates.push('##body'); let selector = cosmeticFilterCandidates[i-1];
if (
selector.indexOf(':nth-of-type(') !== -1 &&
safeQuerySelectorAll(document.body, selector).length > 1
) {
cosmeticFilterCandidates.push('##body');
}
} }
// https://github.com/gorhill/uBlock/issues/1545 // https://github.com/gorhill/uBlock/issues/1545
// Network filter candidates from all other elements found at point (x, y). // Network filter candidates from all other elements found at point (x, y).
if ( typeof x === 'number' ) { if ( typeof x === 'number' ) {
var attrName = pickerRoot.id + '-clickblind'; let attrName = pickerRoot.id + '-clickblind';
var previous; let previous;
elem = first; elem = first;
while ( elem !== null ) { while ( elem !== null ) {
previous = elem; previous = elem;
@ -623,7 +635,7 @@ var filtersFrom = function(x, y) {
} }
netFilterFromElement(elem); netFilterFromElement(elem);
} }
var elems = document.querySelectorAll('[' + attrName + ']'); let elems = document.querySelectorAll('[' + attrName + ']');
i = elems.length; i = elems.length;
while ( i-- ) { while ( i-- ) {
elems[i].removeAttribute(attrName); elems[i].removeAttribute(attrName);
@ -1014,35 +1026,34 @@ var onCandidateChanged = (function() {
/******************************************************************************/ /******************************************************************************/
var candidateFromFilterChoice = function(filterChoice) { var candidateFromFilterChoice = function(filterChoice) {
var slot = filterChoice.slot; let slot = filterChoice.slot;
var filters = filterChoice.filters; let filters = filterChoice.filters;
var filter = filters[slot]; let filter = filters[slot];
if ( filter === undefined ) { if ( filter === undefined ) { return ''; }
return '';
}
// For net filters there no such thing as a path // For net filters there no such thing as a path
if ( filter.lastIndexOf('##', 0) !== 0 ) { if ( filter.startsWith('##') === false ) { return filter; }
return filter;
}
// At this point, we have a cosmetic filter // At this point, we have a cosmetic filter
// Modifier means "target broadly". Hence: // Modifier means "target broadly". Hence:
// - Do not compute exact path. // - Do not compute exact path.
// - Discard narrowing directives. // - Discard narrowing directives.
// - Remove the id if one or more classes exist
// TODO: should remove tag name too? ¯\_(ツ)_/¯
if ( filterChoice.modifier ) { if ( filterChoice.modifier ) {
filter = filter.replace(/:nth-of-type\(\d+\)/, ''); filter = filter.replace(/:nth-of-type\(\d+\)/, '');
// Remove the id if one or more classes exist. if ( filter.indexOf('.') !== -1 ) {
if ( filter.charAt(2) === '#' && filter.indexOf('.') !== -1 ) { if ( filter.charAt(2) === '#' ) {
filter = filter.replace(/#[^#.]+/, ''); filter = filter.replace(/#[^#.]+/, '');
}
} }
return filter; return filter;
} }
// Return path: the target element, then all siblings prepended // Return path: the target element, then all siblings prepended
var selector = '', joiner = ''; let selector = '', joiner = '';
for ( ; slot < filters.length; slot++ ) { for ( ; slot < filters.length; slot++ ) {
filter = filters[slot]; filter = filters[slot];
// Remove all classes when an id exists. // Remove all classes when an id exists.
@ -1051,19 +1062,17 @@ var candidateFromFilterChoice = function(filterChoice) {
} }
selector = filter.slice(2) + joiner + selector; selector = filter.slice(2) + joiner + selector;
// Stop at any element with an id: these are unique in a web page // Stop at any element with an id: these are unique in a web page
if ( filter.lastIndexOf('###', 0) === 0 ) { if ( filter.startsWith('###') ) { break; }
break;
}
// Stop if current selector matches only one element on the page // Stop if current selector matches only one element on the page
if ( document.querySelectorAll(selector).length === 1 ) { if ( document.querySelectorAll(selector).length === 1 ) { break; }
break;
}
joiner = ' > '; joiner = ' > ';
} }
// https://github.com/gorhill/uBlock/issues/2519 // https://github.com/gorhill/uBlock/issues/2519
// https://github.com/uBlockOrigin/uBlock-issues/issues/17
if ( if (
slot === filters.length && slot === filters.length &&
selector.startsWith('body > ') === false &&
document.querySelectorAll(selector).length > 1 document.querySelectorAll(selector).length > 1
) { ) {
selector = 'body > ' + selector; selector = 'body > ' + selector;