From 16727d68c8c73245f78ab247f9ec86ac63edfd30 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 5 Jul 2020 08:44:14 -0400 Subject: [PATCH] Fix parsing of srcset attribute in element picker Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/1071 Additionally, match unconditionally against `srcset` attribute when trying to find matching elements in the page. For example, sometimes an img element may set both `src` and `srcset` properties, they should not be deemed mutually exclusive. --- src/js/scriptlets/element-picker.js | 67 +++++++++++++++++++---------- 1 file changed, 45 insertions(+), 22 deletions(-) diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index 068e31101..ef6acd6c3 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -259,20 +259,41 @@ const resourceURLsFromElement = function(elem) { urls.push(trimFragmentFromURL(s.slice(0, 1024))); } } - if ( typeof elem.srcset === 'string' && elem.srcset !== '' ) { - for ( let s of elem.srcset.split(',') ) { - s = s.trim(); - const pos = s.indexOf(' '); - if ( pos !== -1 ) { s = s.slice(0, pos); } - const parsedURL = new URL(s, document.baseURI); - if ( parsedURL.pathname.length > 1 ) { - urls.push(trimFragmentFromURL(parsedURL.href)); - } - } - } + resourceURLsFromSrcset(elem, urls); return urls; }; +// https://html.spec.whatwg.org/multipage/images.html#parsing-a-srcset-attribute +// https://github.com/uBlockOrigin/uBlock-issues/issues/1071 +const resourceURLsFromSrcset = function(elem, out) { + let srcset = elem.srcset; + if ( typeof srcset !== 'string' && srcset === '' ) { return; } + for(;;) { + // trim whitespace + srcset = srcset.trim(); + if ( srcset.length === 0 ) { break; } + // abort in case of leading comma + if ( /^,/.test(srcset) ) { break; } + // collect and consume all non-whitespace characters + let match = /^\S+/.exec(srcset); + if ( match === null ) { break; } + srcset = srcset.slice(match.index + match[0].length); + let url = match[0]; + // consume descriptor, if any + if ( /,$/.test(url) ) { + url = url.replace(/,$/, ''); + if ( /,$/.test(url) ) { break; } + } else { + match = /^[^,]*(?:\(.+?\))?[^,]*(?:,|$)/.exec(srcset); + if ( match === null ) { break; } + srcset = srcset.slice(match.index + match[0].length); + } + const parsedURL = new URL(url, document.baseURI); + if ( parsedURL.pathname.length === 0 ) { continue; } + out.push(trimFragmentFromURL(parsedURL.href)); + } +}; + /******************************************************************************/ const netFilterFromUnion = function(patternIn, out) { @@ -658,18 +679,20 @@ const filterToDOMInterface = (( ) => { Object.keys(netFilter1stSources).join() ); for ( const elem of elems ) { - let srcProp = netFilter1stSources[elem.localName]; - let src = elem[srcProp]; - if ( typeof src !== 'string' || src.length === 0 ) { - if ( - typeof elem.srcset === 'string' && - elem.srcset !== '' && - typeof elem.currentSrc === 'string' - ) { - src = elem.currentSrc; - } + const srcProp = netFilter1stSources[elem.localName]; + const src = elem[srcProp]; + if ( typeof src === 'string' && reFilter.test(src) ) { + out.push({ + type: 'network', + elem: elem, + src: srcProp, + opts: filterTypes[elem.localName], + }); } - if ( src && reFilter.test(src) ) { + if ( + typeof elem.currentSrc === 'string' && + reFilter.test(elem.currentSrc) + ) { out.push({ type: 'network', elem: elem,