1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-09-03 01:29:39 +02:00

Fix srcset handling in element picker

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/923

Use current page's hostname instead of that of
image URLs to decide whether to reset pattern
union with previous picker sessions.

The fixed issue arose from the fact that the
page uses URLs from different origins in a
single srcset attribute.
This commit is contained in:
Raymond Hill 2020-03-15 08:45:17 -04:00
parent ca80d2826b
commit bc700e691c
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2

View File

@ -26,94 +26,6 @@
/******************************************************************************/
/******************************************************************************/
/*! http://mths.be/cssescape v0.2.1 by @mathias | MIT license */
;(function(root) {
if (!root.CSS) {
root.CSS = {};
}
var CSS = root.CSS;
var InvalidCharacterError = function(message) {
this.message = message;
};
InvalidCharacterError.prototype = new Error();
InvalidCharacterError.prototype.name = 'InvalidCharacterError';
if (!CSS.escape) {
// http://dev.w3.org/csswg/cssom/#serialize-an-identifier
CSS.escape = function(value) {
var string = String(value);
var length = string.length;
var index = -1;
var codeUnit;
var result = '';
var firstCodeUnit = string.charCodeAt(0);
while (++index < length) {
codeUnit = string.charCodeAt(index);
// Note: theres no need to special-case astral symbols, surrogate
// pairs, or lone surrogates.
// If the character is NULL (U+0000), then throw an
// `InvalidCharacterError` exception and terminate these steps.
if (codeUnit === 0x0000) {
throw new InvalidCharacterError(
'Invalid character: the input contains U+0000.'
);
}
if (
// If the character is in the range [\1-\1F] (U+0001 to U+001F) or is
// U+007F, […]
(codeUnit >= 0x0001 && codeUnit <= 0x001F) || codeUnit === 0x007F ||
// If the character is the first character and is in the range [0-9]
// (U+0030 to U+0039), […]
(index === 0 && codeUnit >= 0x0030 && codeUnit <= 0x0039) ||
// If the character is the second character and is in the range [0-9]
// (U+0030 to U+0039) and the first character is a `-` (U+002D), […]
(
index === 1 &&
codeUnit >= 0x0030 && codeUnit <= 0x0039 &&
firstCodeUnit === 0x002D
)
) {
// http://dev.w3.org/csswg/cssom/#escape-a-character-as-code-point
result += '\\' + codeUnit.toString(16) + ' ';
continue;
}
// If the character is not handled by one of the above rules and is
// greater than or equal to U+0080, is `-` (U+002D) or `_` (U+005F), or
// is in one of the ranges [0-9] (U+0030 to U+0039), [A-Z] (U+0041 to
// U+005A), or [a-z] (U+0061 to U+007A), […]
if (
codeUnit >= 0x0080 ||
codeUnit === 0x002D ||
codeUnit === 0x005F ||
codeUnit >= 0x0030 && codeUnit <= 0x0039 ||
codeUnit >= 0x0041 && codeUnit <= 0x005A ||
codeUnit >= 0x0061 && codeUnit <= 0x007A
) {
// the character itself
result += string.charAt(index);
continue;
}
// Otherwise, the escaped character.
// http://dev.w3.org/csswg/cssom/#escape-a-character
result += '\\' + string.charAt(index);
}
return result;
};
}
}(self));
/******************************************************************************/
/******************************************************************************/
(( ) => {
/******************************************************************************/
@ -291,20 +203,28 @@ const mergeStrings = function(urls) {
} else {
result.push(diff[1].replace(/\n+/g, ''));
}
merged = result.join('');
}
// Keep usage of wildcards to a sane level, too many of them can cause
// high overhead filters
merged =
result.join('')
.replace(/\*+$/, '')
.replace(/\*{2,}/g, '*')
.replace(/([^*]{1,2}\*)(?:[^*]{1,2}\*)+/g, '$1');
}
// Keep usage of wildcards to a sane level, too many of them can cause
// high overhead filters
merged = merged.replace(/^\*+$/, '')
.replace(/\*{2,}/g, '*')
.replace(/([^*]{1,3}\*)(?:[^*]{1,3}\*)+/g, '$1');
return merged;
};
/******************************************************************************/
// Remove fragment part from a URL.
const trimFragmentFromURL = function(url) {
const pos = url.indexOf('#');
return pos !== -1 ? url.slice(0, pos) : url;
};
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/1897
// Ignore `data:` URI, they can't be handled by an HTTP observer.
@ -313,7 +233,9 @@ const backgroundImageURLFromElement = function(elem) {
const bgImg = style.backgroundImage || '';
const matches = /^url\((["']?)([^"']+)\1\)$/.exec(bgImg);
const url = matches !== null && matches.length === 3 ? matches[2] : '';
return url.lastIndexOf('data:', 0) === -1 ? url.slice(0, 1024) : '';
return url.lastIndexOf('data:', 0) === -1
? trimFragmentFromURL(url.slice(0, 1024))
: '';
};
/******************************************************************************/
@ -322,84 +244,73 @@ const backgroundImageURLFromElement = function(elem) {
// Limit returned string to 1024 characters.
// Also, return only URLs which will be seen by an HTTP observer.
const resourceURLFromElement = function(elem) {
const resourceURLsFromElement = function(elem) {
const urls = [];
const tagName = elem.localName;
const prop = netFilter1stSources[tagName];
if ( prop ) {
let src = '';
{
let s = elem[prop];
if ( typeof s === 'string' && /^https?:\/\//.test(s) ) {
src = s.slice(0, 1024);
}
}
if ( typeof elem.srcset === 'string' && elem.srcset !== '' ) {
const ss = [];
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 ) {
ss.push(parsedURL.href);
}
}
if ( ss.length !== 0 ) {
if ( src !== '' ) {
ss.push(src);
}
src = mergeStrings(ss);
}
}
return src;
if ( prop === undefined ) {
const url = backgroundImageURLFromElement(elem);
if ( url !== '' ) { urls.push(url); }
return urls;
}
return backgroundImageURLFromElement(elem);
{
const s = elem[prop];
if ( typeof s === 'string' && /^https?:\/\//.test(s) ) {
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));
}
}
}
return urls;
};
/******************************************************************************/
const netFilterFromUnion = function(toMergeURL, out) {
const parsedURL = new URL(toMergeURL, document.baseURI);
toMergeURL = parsedURL.pathname + parsedURL.search;
const netFilterFromUnion = function(patternIn, out) {
// Reset reference filter when dealing with unrelated URLs
const currentHostname = self.location.hostname;
if (
lastNetFilterUnion === '' ||
parsedURL.host === '' ||
parsedURL.host !== lastNetFilterHostname
currentHostname === '' ||
currentHostname !== lastNetFilterHostname
) {
lastNetFilterHostname = parsedURL.host;
lastNetFilterUnion = toMergeURL;
lastNetFilterHostname = currentHostname;
lastNetFilterUnion = patternIn;
vAPI.messaging.send('elementPicker', {
what: 'elementPickerEprom',
lastNetFilterSession: lastNetFilterSession,
lastNetFilterHostname: lastNetFilterHostname,
lastNetFilterUnion: lastNetFilterUnion,
lastNetFilterSession,
lastNetFilterHostname,
lastNetFilterUnion,
});
return;
}
// Related URLs
lastNetFilterHostname = parsedURL.host;
let mergedURL = mergeStrings([ toMergeURL, lastNetFilterUnion ]);
if ( mergedURL !== '/*' && mergedURL !== toMergeURL ) {
const filter = '||' + lastNetFilterHostname + mergedURL;
lastNetFilterHostname = currentHostname;
let patternOut = mergeStrings([ patternIn, lastNetFilterUnion ]);
if ( patternOut !== '/*' && patternOut !== patternIn ) {
const filter = `||${patternOut}`;
if ( out.indexOf(filter) === -1 ) {
out.push(filter);
}
} else {
mergedURL = toMergeURL;
lastNetFilterUnion = patternOut;
}
lastNetFilterUnion = mergedURL;
// Remember across element picker sessions
vAPI.messaging.send('elementPicker', {
what: 'elementPickerEprom',
lastNetFilterSession: lastNetFilterSession,
lastNetFilterHostname: lastNetFilterHostname,
lastNetFilterUnion: lastNetFilterUnion,
lastNetFilterSession,
lastNetFilterHostname,
lastNetFilterUnion,
});
};
@ -410,8 +321,8 @@ const netFilterFromUnion = function(toMergeURL, out) {
const netFilterFromElement = function(elem) {
if ( elem === null ) { return 0; }
if ( elem.nodeType !== 1 ) { return 0; }
let src = resourceURLFromElement(elem);
if ( src === '' ) { return 0; }
const urls = resourceURLsFromElement(elem);
if ( urls.length === 0 ) { return 0; }
if ( candidateElements.indexOf(elem) === -1 ) {
candidateElements.push(elem);
@ -420,13 +331,11 @@ const netFilterFromElement = function(elem) {
const candidates = netFilterCandidates;
const len = candidates.length;
// Remove fragment
let pos = src.indexOf('#');
if ( pos !== -1 ) {
src = src.slice(0, pos);
for ( let i = 0; i < urls.length; i++ ) {
urls[i] = urls[i].replace(/^https?:\/\//, '');
}
const pattern = mergeStrings(urls);
const filter = src.replace(/^https?:\/\//, '||');
if ( bestCandidateFilter === null ) {
bestCandidateFilter = {
@ -436,16 +345,16 @@ const netFilterFromElement = function(elem) {
};
}
candidates.push(filter);
candidates.push(`||${pattern}`);
// Suggest a less narrow filter if possible
pos = filter.indexOf('?');
const pos = pattern.indexOf('?');
if ( pos !== -1 ) {
candidates.push(filter.slice(0, pos));
candidates.push(`||${pattern.slice(0, pos)}`);
}
// Suggest a filter which is a result of combining more than one URL.
netFilterFromUnion(src, candidates);
netFilterFromUnion(pattern, candidates);
return candidates.length - len;
};