mirror of https://github.com/gorhill/uBlock.git
Improve detection of invalid CSS selectors
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/389 Additionally, fix case of using potentially uninitialized variable in preview mode. Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/425
This commit is contained in:
parent
426a6ea9a7
commit
93842a3f9c
|
@ -707,17 +707,17 @@ var filtersFrom = function(x, y) {
|
||||||
const filterToDOMInterface = (function() {
|
const filterToDOMInterface = (function() {
|
||||||
// Net filters: we need to lookup manually -- translating into a foolproof
|
// Net filters: we need to lookup manually -- translating into a foolproof
|
||||||
// CSS selector is just not possible.
|
// CSS selector is just not possible.
|
||||||
var fromNetworkFilter = function(filter) {
|
const fromNetworkFilter = function(filter) {
|
||||||
var out = [];
|
const out = [];
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/945
|
// https://github.com/chrisaljoudi/uBlock/issues/945
|
||||||
// Transform into a regular expression, this allows the user to edit and
|
// Transform into a regular expression, this allows the user to edit and
|
||||||
// insert wildcard(s) into the proposed filter.
|
// insert wildcard(s) into the proposed filter.
|
||||||
var reStr = '';
|
let reStr = '';
|
||||||
if ( filter.length > 1 && filter.charAt(0) === '/' && filter.slice(-1) === '/' ) {
|
if ( filter.length > 1 && filter.charAt(0) === '/' && filter.slice(-1) === '/' ) {
|
||||||
reStr = filter.slice(1, -1);
|
reStr = filter.slice(1, -1);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
var rePrefix = '', reSuffix = '';
|
let rePrefix = '', reSuffix = '';
|
||||||
if ( filter.slice(0, 2) === '||' ) {
|
if ( filter.slice(0, 2) === '||' ) {
|
||||||
filter = filter.replace('||', '');
|
filter = filter.replace('||', '');
|
||||||
} else {
|
} else {
|
||||||
|
@ -734,7 +734,7 @@ const filterToDOMInterface = (function() {
|
||||||
filter.replace(/[.+?${}()|[\]\\]/g, '\\$&').replace(/[\*^]+/g, '.*') +
|
filter.replace(/[.+?${}()|[\]\\]/g, '\\$&').replace(/[\*^]+/g, '.*') +
|
||||||
reSuffix;
|
reSuffix;
|
||||||
}
|
}
|
||||||
var reFilter = null;
|
let reFilter = null;
|
||||||
try {
|
try {
|
||||||
reFilter = new RegExp(reStr);
|
reFilter = new RegExp(reStr);
|
||||||
}
|
}
|
||||||
|
@ -743,18 +743,14 @@ const filterToDOMInterface = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup by tag names.
|
// Lookup by tag names.
|
||||||
var src1stProps = netFilter1stSources;
|
let elems = document.querySelectorAll(
|
||||||
var src2ndProps = netFilter2ndSources;
|
Object.keys(netFilter1stSources).join()
|
||||||
var srcProp, src;
|
);
|
||||||
var elems = document.querySelectorAll(Object.keys(src1stProps).join()),
|
for ( const elem of elems ) {
|
||||||
iElem = elems.length,
|
let srcProp = netFilter1stSources[elem.localName];
|
||||||
elem;
|
let src = elem[srcProp];
|
||||||
while ( iElem-- ) {
|
|
||||||
elem = elems[iElem];
|
|
||||||
srcProp = src1stProps[elem.localName];
|
|
||||||
src = elem[srcProp];
|
|
||||||
if ( typeof src !== 'string' || src.length === 0 ) {
|
if ( typeof src !== 'string' || src.length === 0 ) {
|
||||||
srcProp = src2ndProps[elem.localName];
|
srcProp = netFilter2ndSources[elem.localName];
|
||||||
src = elem[srcProp];
|
src = elem[srcProp];
|
||||||
}
|
}
|
||||||
if ( src && reFilter.test(src) ) {
|
if ( src && reFilter.test(src) ) {
|
||||||
|
@ -768,10 +764,7 @@ const filterToDOMInterface = (function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find matching background image in current set of candidate elements.
|
// Find matching background image in current set of candidate elements.
|
||||||
elems = candidateElements;
|
for ( const elem of candidateElements ) {
|
||||||
iElem = elems.length;
|
|
||||||
while ( iElem-- ) {
|
|
||||||
elem = elems[iElem];
|
|
||||||
if ( reFilter.test(backgroundImageURLFromElement(elem)) ) {
|
if ( reFilter.test(backgroundImageURLFromElement(elem)) ) {
|
||||||
out.push({
|
out.push({
|
||||||
type: 'network',
|
type: 'network',
|
||||||
|
@ -790,9 +783,14 @@ const filterToDOMInterface = (function() {
|
||||||
// ways to compose a valid href to the same effective URL. One idea is to
|
// ways to compose a valid href to the same effective URL. One idea is to
|
||||||
// normalize all a[href] on the page, but for now I will wait and see, as I
|
// normalize all a[href] on the page, but for now I will wait and see, as I
|
||||||
// prefer to refrain from tampering with the page content if I can avoid it.
|
// prefer to refrain from tampering with the page content if I can avoid it.
|
||||||
var fromPlainCosmeticFilter = function(filter) {
|
//
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/389
|
||||||
|
// Test filter using comma-separated list to better detect invalid CSS
|
||||||
|
// selectors.
|
||||||
|
const fromPlainCosmeticFilter = function(filter) {
|
||||||
let elems;
|
let elems;
|
||||||
try {
|
try {
|
||||||
|
document.documentElement.matches(`${filter},\na`);
|
||||||
elems = document.querySelectorAll(filter);
|
elems = document.querySelectorAll(filter);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
@ -808,15 +806,15 @@ const filterToDOMInterface = (function() {
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/1772
|
// https://github.com/gorhill/uBlock/issues/1772
|
||||||
// Handle procedural cosmetic filters.
|
// Handle procedural cosmetic filters.
|
||||||
var fromCompiledCosmeticFilter = function(raw) {
|
const fromCompiledCosmeticFilter = function(raw) {
|
||||||
if ( typeof raw !== 'string' ) { return; }
|
if ( typeof raw !== 'string' ) { return; }
|
||||||
var o;
|
let o;
|
||||||
try {
|
try {
|
||||||
o = JSON.parse(raw);
|
o = JSON.parse(raw);
|
||||||
} catch(ex) {
|
} catch(ex) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var elems;
|
let elems;
|
||||||
if ( o.style ) {
|
if ( o.style ) {
|
||||||
elems = document.querySelectorAll(o.style[0]);
|
elems = document.querySelectorAll(o.style[0]);
|
||||||
lastAction = o.style[0] + ' {' + o.style[1] + '}';
|
lastAction = o.style[0] + ' {' + o.style[1] + '}';
|
||||||
|
@ -824,21 +822,21 @@ const filterToDOMInterface = (function() {
|
||||||
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
|
elems = vAPI.domFilterer.createProceduralFilter(o).exec();
|
||||||
}
|
}
|
||||||
if ( !elems ) { return; }
|
if ( !elems ) { return; }
|
||||||
var out = [];
|
const out = [];
|
||||||
for ( var i = 0, n = elems.length; i < n; i++ ) {
|
for ( const elem of elems ) {
|
||||||
out.push({ type: 'cosmetic', elem: elems[i] });
|
out.push({ type: 'cosmetic', elem });
|
||||||
}
|
}
|
||||||
return out;
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
var lastFilter,
|
let lastFilter,
|
||||||
lastResultset,
|
lastResultset,
|
||||||
lastAction,
|
lastAction,
|
||||||
appliedStyleTag,
|
appliedStyleTag,
|
||||||
applied = false,
|
applied = false,
|
||||||
previewing = false;
|
previewing = false;
|
||||||
|
|
||||||
var queryAll = function(filter, callback) {
|
const queryAll = function(filter, callback) {
|
||||||
filter = filter.trim();
|
filter = filter.trim();
|
||||||
if ( filter === lastFilter ) {
|
if ( filter === lastFilter ) {
|
||||||
callback(lastResultset);
|
callback(lastResultset);
|
||||||
|
@ -859,7 +857,7 @@ const filterToDOMInterface = (function() {
|
||||||
callback(lastResultset);
|
callback(lastResultset);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var selector = filter.slice(2);
|
const selector = filter.slice(2);
|
||||||
lastResultset = fromPlainCosmeticFilter(selector);
|
lastResultset = fromPlainCosmeticFilter(selector);
|
||||||
if ( lastResultset ) {
|
if ( lastResultset ) {
|
||||||
if ( previewing ) { apply(); }
|
if ( previewing ) { apply(); }
|
||||||
|
@ -878,18 +876,13 @@ const filterToDOMInterface = (function() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
var applyHide = function() {
|
const applyHide = function() {
|
||||||
var htmlElem = document.documentElement,
|
const htmlElem = document.documentElement;
|
||||||
items = lastResultset,
|
for ( const item of lastResultset ) {
|
||||||
item, elem, style;
|
const elem = item.elem;
|
||||||
for ( var i = 0, n = items.length; i < n; i++ ) {
|
|
||||||
item = items[i];
|
|
||||||
elem = item.elem;
|
|
||||||
// https://github.com/gorhill/uBlock/issues/1629
|
// https://github.com/gorhill/uBlock/issues/1629
|
||||||
if ( elem === pickerRoot ) {
|
if ( elem === pickerRoot ) { continue; }
|
||||||
continue;
|
const style = elem.style;
|
||||||
}
|
|
||||||
style = elem.style;
|
|
||||||
if (
|
if (
|
||||||
(elem !== htmlElem) &&
|
(elem !== htmlElem) &&
|
||||||
(item.type === 'cosmetic' || item.type === 'network' && item.src !== undefined)
|
(item.type === 'cosmetic' || item.type === 'network' && item.src !== undefined)
|
||||||
|
@ -906,10 +899,9 @@ const filterToDOMInterface = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var unapplyHide = function() {
|
const unapplyHide = function() {
|
||||||
var items = lastResultset, item;
|
if ( lastResultset === undefined ) { return; }
|
||||||
for ( var i = 0, n = items.length; i < n; i++ ) {
|
for ( const item of lastResultset ) {
|
||||||
item = items[i];
|
|
||||||
if ( item.hasOwnProperty('display') ) {
|
if ( item.hasOwnProperty('display') ) {
|
||||||
item.elem.style.setProperty(
|
item.elem.style.setProperty(
|
||||||
'display',
|
'display',
|
||||||
|
@ -929,14 +921,14 @@ const filterToDOMInterface = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var unapplyStyle = function() {
|
const unapplyStyle = function() {
|
||||||
if ( !appliedStyleTag || appliedStyleTag.parentNode === null ) {
|
if ( !appliedStyleTag || appliedStyleTag.parentNode === null ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
appliedStyleTag.parentNode.removeChild(appliedStyleTag);
|
appliedStyleTag.parentNode.removeChild(appliedStyleTag);
|
||||||
};
|
};
|
||||||
|
|
||||||
var applyStyle = function() {
|
const applyStyle = function() {
|
||||||
if ( !appliedStyleTag ) {
|
if ( !appliedStyleTag ) {
|
||||||
appliedStyleTag = document.createElement('style');
|
appliedStyleTag = document.createElement('style');
|
||||||
appliedStyleTag.setAttribute('type', 'text/css');
|
appliedStyleTag.setAttribute('type', 'text/css');
|
||||||
|
@ -947,13 +939,11 @@ const filterToDOMInterface = (function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var apply = function() {
|
const apply = function() {
|
||||||
if ( applied ) {
|
if ( applied ) {
|
||||||
unapply();
|
unapply();
|
||||||
}
|
}
|
||||||
if ( lastResultset === undefined ) {
|
if ( lastResultset === undefined ) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( typeof lastAction === 'string' ) {
|
if ( typeof lastAction === 'string' ) {
|
||||||
applyStyle();
|
applyStyle();
|
||||||
} else {
|
} else {
|
||||||
|
@ -962,10 +952,8 @@ const filterToDOMInterface = (function() {
|
||||||
applied = true;
|
applied = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
var unapply = function() {
|
const unapply = function() {
|
||||||
if ( !applied ) {
|
if ( !applied ) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ( typeof lastAction === 'string' ) {
|
if ( typeof lastAction === 'string' ) {
|
||||||
unapplyStyle();
|
unapplyStyle();
|
||||||
} else {
|
} else {
|
||||||
|
@ -974,13 +962,12 @@ const filterToDOMInterface = (function() {
|
||||||
applied = false;
|
applied = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
var preview = function(filter) {
|
const preview = function(filter) {
|
||||||
previewing = filter !== false;
|
previewing = filter !== false;
|
||||||
if ( previewing ) {
|
if ( previewing ) {
|
||||||
queryAll(filter, function(items) {
|
queryAll(filter, items => {
|
||||||
if ( items !== undefined ) {
|
if ( items === undefined ) { return; }
|
||||||
apply();
|
apply();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
unapply();
|
unapply();
|
||||||
|
@ -998,7 +985,7 @@ const filterToDOMInterface = (function() {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const userFilterFromCandidate = function(callback) {
|
const userFilterFromCandidate = function(callback) {
|
||||||
var v = rawFilterFromTextarea();
|
let v = rawFilterFromTextarea();
|
||||||
filterToDOMInterface.set(v, function(items) {
|
filterToDOMInterface.set(v, function(items) {
|
||||||
if ( !items || items.length === 0 ) {
|
if ( !items || items.length === 0 ) {
|
||||||
callback();
|
callback();
|
||||||
|
@ -1007,7 +994,7 @@ const userFilterFromCandidate = function(callback) {
|
||||||
|
|
||||||
// https://github.com/gorhill/uBlock/issues/738
|
// https://github.com/gorhill/uBlock/issues/738
|
||||||
// Trim dots.
|
// Trim dots.
|
||||||
var hostname = window.location.hostname;
|
let hostname = window.location.hostname;
|
||||||
if ( hostname.slice(-1) === '.' ) {
|
if ( hostname.slice(-1) === '.' ) {
|
||||||
hostname = hostname.slice(0, -1);
|
hostname = hostname.slice(0, -1);
|
||||||
}
|
}
|
||||||
|
@ -1019,14 +1006,14 @@ const userFilterFromCandidate = function(callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume net filter
|
// Assume net filter
|
||||||
var opts = [];
|
const opts = [];
|
||||||
|
|
||||||
// If no domain included in filter, we need domain option
|
// If no domain included in filter, we need domain option
|
||||||
if ( v.lastIndexOf('||', 0) === -1 ) {
|
if ( v.lastIndexOf('||', 0) === -1 ) {
|
||||||
opts.push('domain=' + hostname);
|
opts.push('domain=' + hostname);
|
||||||
}
|
}
|
||||||
|
|
||||||
var item = items[0];
|
const item = items[0];
|
||||||
if ( item.opts ) {
|
if ( item.opts ) {
|
||||||
opts.push(item.opts);
|
opts.push(item.opts);
|
||||||
}
|
}
|
||||||
|
@ -1042,11 +1029,12 @@ const userFilterFromCandidate = function(callback) {
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const onCandidateChanged = (function() {
|
const onCandidateChanged = (function() {
|
||||||
var process = function(items) {
|
const process = function(items) {
|
||||||
var elems = [], valid = items !== undefined;
|
const elems = [];
|
||||||
|
const valid = items !== undefined;
|
||||||
if ( valid ) {
|
if ( valid ) {
|
||||||
for ( var i = 0; i < items.length; i++ ) {
|
for ( const item of items ) {
|
||||||
elems.push(items[i].elem);
|
elems.push(item.elem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pickerBody.querySelector('#resultsetCount').textContent = valid ?
|
pickerBody.querySelector('#resultsetCount').textContent = valid ?
|
||||||
|
|
Loading…
Reference in New Issue