mirror of
https://github.com/gorhill/uBlock.git
synced 2024-09-18 17:02:27 +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:
parent
ca80d2826b
commit
bc700e691c
@ -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: there’s 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 {
|
} else {
|
||||||
result.push(diff[1].replace(/\n+/g, ''));
|
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;
|
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
|
// https://github.com/gorhill/uBlock/issues/1897
|
||||||
// Ignore `data:` URI, they can't be handled by an HTTP observer.
|
// 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 bgImg = style.backgroundImage || '';
|
||||||
const matches = /^url\((["']?)([^"']+)\1\)$/.exec(bgImg);
|
const matches = /^url\((["']?)([^"']+)\1\)$/.exec(bgImg);
|
||||||
const url = matches !== null && matches.length === 3 ? matches[2] : '';
|
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.
|
// Limit returned string to 1024 characters.
|
||||||
// Also, return only URLs which will be seen by an HTTP observer.
|
// 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 tagName = elem.localName;
|
||||||
const prop = netFilter1stSources[tagName];
|
const prop = netFilter1stSources[tagName];
|
||||||
if ( prop ) {
|
if ( prop === undefined ) {
|
||||||
let src = '';
|
const url = backgroundImageURLFromElement(elem);
|
||||||
{
|
if ( url !== '' ) { urls.push(url); }
|
||||||
let s = elem[prop];
|
return urls;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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 netFilterFromUnion = function(patternIn, out) {
|
||||||
const parsedURL = new URL(toMergeURL, document.baseURI);
|
|
||||||
|
|
||||||
toMergeURL = parsedURL.pathname + parsedURL.search;
|
|
||||||
|
|
||||||
// Reset reference filter when dealing with unrelated URLs
|
// Reset reference filter when dealing with unrelated URLs
|
||||||
|
const currentHostname = self.location.hostname;
|
||||||
if (
|
if (
|
||||||
lastNetFilterUnion === '' ||
|
lastNetFilterUnion === '' ||
|
||||||
parsedURL.host === '' ||
|
currentHostname === '' ||
|
||||||
parsedURL.host !== lastNetFilterHostname
|
currentHostname !== lastNetFilterHostname
|
||||||
) {
|
) {
|
||||||
lastNetFilterHostname = parsedURL.host;
|
lastNetFilterHostname = currentHostname;
|
||||||
lastNetFilterUnion = toMergeURL;
|
lastNetFilterUnion = patternIn;
|
||||||
vAPI.messaging.send('elementPicker', {
|
vAPI.messaging.send('elementPicker', {
|
||||||
what: 'elementPickerEprom',
|
what: 'elementPickerEprom',
|
||||||
lastNetFilterSession: lastNetFilterSession,
|
lastNetFilterSession,
|
||||||
lastNetFilterHostname: lastNetFilterHostname,
|
lastNetFilterHostname,
|
||||||
lastNetFilterUnion: lastNetFilterUnion,
|
lastNetFilterUnion,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Related URLs
|
// Related URLs
|
||||||
lastNetFilterHostname = parsedURL.host;
|
lastNetFilterHostname = currentHostname;
|
||||||
|
let patternOut = mergeStrings([ patternIn, lastNetFilterUnion ]);
|
||||||
let mergedURL = mergeStrings([ toMergeURL, lastNetFilterUnion ]);
|
if ( patternOut !== '/*' && patternOut !== patternIn ) {
|
||||||
if ( mergedURL !== '/*' && mergedURL !== toMergeURL ) {
|
const filter = `||${patternOut}`;
|
||||||
const filter = '||' + lastNetFilterHostname + mergedURL;
|
|
||||||
if ( out.indexOf(filter) === -1 ) {
|
if ( out.indexOf(filter) === -1 ) {
|
||||||
out.push(filter);
|
out.push(filter);
|
||||||
}
|
}
|
||||||
} else {
|
lastNetFilterUnion = patternOut;
|
||||||
mergedURL = toMergeURL;
|
|
||||||
}
|
}
|
||||||
lastNetFilterUnion = mergedURL;
|
|
||||||
|
|
||||||
// Remember across element picker sessions
|
// Remember across element picker sessions
|
||||||
vAPI.messaging.send('elementPicker', {
|
vAPI.messaging.send('elementPicker', {
|
||||||
what: 'elementPickerEprom',
|
what: 'elementPickerEprom',
|
||||||
lastNetFilterSession: lastNetFilterSession,
|
lastNetFilterSession,
|
||||||
lastNetFilterHostname: lastNetFilterHostname,
|
lastNetFilterHostname,
|
||||||
lastNetFilterUnion: lastNetFilterUnion,
|
lastNetFilterUnion,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -410,8 +321,8 @@ const netFilterFromUnion = function(toMergeURL, out) {
|
|||||||
const netFilterFromElement = function(elem) {
|
const netFilterFromElement = function(elem) {
|
||||||
if ( elem === null ) { return 0; }
|
if ( elem === null ) { return 0; }
|
||||||
if ( elem.nodeType !== 1 ) { return 0; }
|
if ( elem.nodeType !== 1 ) { return 0; }
|
||||||
let src = resourceURLFromElement(elem);
|
const urls = resourceURLsFromElement(elem);
|
||||||
if ( src === '' ) { return 0; }
|
if ( urls.length === 0 ) { return 0; }
|
||||||
|
|
||||||
if ( candidateElements.indexOf(elem) === -1 ) {
|
if ( candidateElements.indexOf(elem) === -1 ) {
|
||||||
candidateElements.push(elem);
|
candidateElements.push(elem);
|
||||||
@ -420,13 +331,11 @@ const netFilterFromElement = function(elem) {
|
|||||||
const candidates = netFilterCandidates;
|
const candidates = netFilterCandidates;
|
||||||
const len = candidates.length;
|
const len = candidates.length;
|
||||||
|
|
||||||
// Remove fragment
|
for ( let i = 0; i < urls.length; i++ ) {
|
||||||
let pos = src.indexOf('#');
|
urls[i] = urls[i].replace(/^https?:\/\//, '');
|
||||||
if ( pos !== -1 ) {
|
|
||||||
src = src.slice(0, pos);
|
|
||||||
}
|
}
|
||||||
|
const pattern = mergeStrings(urls);
|
||||||
|
|
||||||
const filter = src.replace(/^https?:\/\//, '||');
|
|
||||||
|
|
||||||
if ( bestCandidateFilter === null ) {
|
if ( bestCandidateFilter === null ) {
|
||||||
bestCandidateFilter = {
|
bestCandidateFilter = {
|
||||||
@ -436,16 +345,16 @@ const netFilterFromElement = function(elem) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
candidates.push(filter);
|
candidates.push(`||${pattern}`);
|
||||||
|
|
||||||
// Suggest a less narrow filter if possible
|
// Suggest a less narrow filter if possible
|
||||||
pos = filter.indexOf('?');
|
const pos = pattern.indexOf('?');
|
||||||
if ( pos !== -1 ) {
|
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.
|
// Suggest a filter which is a result of combining more than one URL.
|
||||||
netFilterFromUnion(src, candidates);
|
netFilterFromUnion(pattern, candidates);
|
||||||
|
|
||||||
return candidates.length - len;
|
return candidates.length - len;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user