Raymond Hill 2018-12-20 17:29:39 -05:00
parent 04d17a3d84
commit 3730d7d128
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
3 changed files with 141 additions and 90 deletions

View File

@ -83,6 +83,7 @@ html#ublock0-epicker,
padding: 2px !important;
resize: none !important;
width: 100% !important;
word-break: break-all !important;
}
#ublock0-epicker #resultsetCount {
background-color: #aaa !important;

View File

@ -272,104 +272,118 @@ var highlightElements = function(elems, force) {
/******************************************************************************/
// TODO: Investigate why diff'ing these two strings returns an incorrect result:
// |
// /articles/5c1a7aae1854f30006cb26f7/lede/1545239527833-shutterstock_726017572-copy.jpeg?crop=0.8889xw%3A0.9988xh%3B0.1089xw%2C0xh&resize=650%3A*&output-quality=55
// /articles/5c1aaea91854f30006cb2f1e/lede/1545253629235-shutterstock_1063990172-copy.jpeg?crop=0.7749xw%3A1xh%3B0.0391xw%2C0xh&resize=650%3A*&output-quality=55
// |
// This appears to be an issue in the differ, need to investigate it.
const mergeStrings = function(urls) {
if ( urls.length === 0 ) { return ''; }
if (
urls.length === 1 ||
self.diff_match_patch instanceof Function === false
) {
return urls[0];
}
const differ = new self.diff_match_patch();
let merged = urls[0];
for ( let i = 1; i < urls.length; i++ ) {
// The differ works at line granularity: we insert a linefeed after
// each character to trick the differ to work at character granularity.
const diffs = differ.diff_main(
//urls[i].replace(/.(?=.)/g, '$&\n'),
//merged.replace(/.(?=.)/g, '$&\n')
urls[i].split('').join('\n'),
merged.split('').join('\n')
);
const result = [];
for ( const diff of diffs ) {
if ( diff[0] !== 0 ) {
result.push('*');
} else {
result.push(diff[1].charAt(0));
}
}
// 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');
}
return merged;
};
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/1897
// Ignore `data:` URI, they can't be handled by an HTTP observer.
var backgroundImageURLFromElement = function(elem) {
var style = window.getComputedStyle(elem),
bgImg = style.backgroundImage || '',
matches = /^url\((["']?)([^"']+)\1\)$/.exec(bgImg),
url = matches !== null && matches.length === 3 ? matches[2] : '';
const backgroundImageURLFromElement = function(elem) {
const style = window.getComputedStyle(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) : '';
};
/******************************************************************************/
// https://github.com/gorhill/uBlock/issues/1725#issuecomment-226479197
// Limit returned string to 1024 characters.
// Also, return only URLs which will be seen by an HTTP observer.
// Limit returned string to 1024 characters.
// Also, return only URLs which will be seen by an HTTP observer.
var resourceURLFromElement = function(elem) {
var tagName = elem.localName, s;
if (
(s = netFilter1stSources[tagName]) ||
(s = netFilter2ndSources[tagName])
) {
s = elem[s];
if ( typeof s === 'string' && /^https?:\/\//.test(s) ) {
return s.slice(0, 1024);
const resourceURLFromElement = function(elem) {
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+/) ) {
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);
};
/******************************************************************************/
var netFilterFromUnion = (function() {
var reTokenizer = /[^0-9a-z%*]+|[0-9a-z%]+|\*/gi;
var a = document.createElement('a');
const netFilterFromUnion = function(toMergeURL, out) {
const parsedURL = new URL(toMergeURL, document.baseURI);
return function(to, out) {
a.href= to;
to = a.pathname + a.search;
var from = lastNetFilterUnion;
toMergeURL = parsedURL.pathname + parsedURL.search;
// Reset reference filter when dealing with unrelated URLs
if ( from === '' || a.host === '' || a.host !== lastNetFilterHostname ) {
lastNetFilterHostname = a.host;
lastNetFilterUnion = to;
vAPI.messaging.send(
'elementPicker',
{
what: 'elementPickerEprom',
lastNetFilterSession: lastNetFilterSession,
lastNetFilterHostname: lastNetFilterHostname,
lastNetFilterUnion: lastNetFilterUnion
}
);
return;
}
// Related URLs
lastNetFilterHostname = a.host;
var fromTokens = from.match(reTokenizer);
var toTokens = to.match(reTokenizer);
var toCount = toTokens.length, toIndex = 0;
var fromToken, pos;
for ( var fromIndex = 0; fromIndex < fromTokens.length; fromIndex += 1 ) {
fromToken = fromTokens[fromIndex];
if ( fromToken === '*' ) {
continue;
}
pos = toTokens.indexOf(fromToken, toIndex);
if ( pos === -1 ) {
fromTokens[fromIndex] = '*';
continue;
}
if ( pos !== toIndex ) {
fromTokens.splice(fromIndex, 0, '*');
fromIndex += 1;
}
toIndex = pos + 1;
if ( toIndex === toCount ) {
fromTokens = fromTokens.slice(0, fromIndex + 1);
break;
}
}
from = fromTokens.join('').replace(/\*\*+/g, '*');
if ( from !== '/*' && from !== to ) {
var filter = '||' + lastNetFilterHostname + from;
if ( out.indexOf(filter) === -1 ) {
out.push(filter);
}
} else {
from = to;
}
lastNetFilterUnion = from;
// Remember across element picker sessions
// Reset reference filter when dealing with unrelated URLs
if (
lastNetFilterUnion === '' ||
parsedURL.host === '' ||
parsedURL.host !== lastNetFilterHostname
) {
lastNetFilterHostname = parsedURL.host;
lastNetFilterUnion = toMergeURL;
vAPI.messaging.send(
'elementPicker',
{
@ -379,14 +393,40 @@ var netFilterFromUnion = (function() {
lastNetFilterUnion: lastNetFilterUnion
}
);
};
})();
return;
}
// Related URLs
lastNetFilterHostname = parsedURL.host;
let mergedURL = mergeStrings([ toMergeURL, lastNetFilterUnion ]);
if ( mergedURL !== '/*' && mergedURL !== toMergeURL ) {
const filter = '||' + lastNetFilterHostname + mergedURL;
if ( out.indexOf(filter) === -1 ) {
out.push(filter);
}
} else {
mergedURL = toMergeURL;
}
lastNetFilterUnion = mergedURL;
// Remember across element picker sessions
vAPI.messaging.send(
'elementPicker',
{
what: 'elementPickerEprom',
lastNetFilterSession: lastNetFilterSession,
lastNetFilterHostname: lastNetFilterHostname,
lastNetFilterUnion: lastNetFilterUnion
}
);
};
/******************************************************************************/
// Extract the best possible net filter, i.e. as specific as possible.
var netFilterFromElement = function(elem) {
const netFilterFromElement = function(elem) {
if ( elem === null ) { return 0; }
if ( elem.nodeType !== 1 ) { return 0; }
let src = resourceURLFromElement(elem);
@ -429,7 +469,7 @@ var netFilterFromElement = function(elem) {
return candidates.length - len;
};
var netFilter1stSources = {
const netFilter1stSources = {
'audio': 'src',
'embed': 'src',
'iframe': 'src',
@ -438,11 +478,11 @@ var netFilter1stSources = {
'video': 'src'
};
var netFilter2ndSources = {
const netFilter2ndSources = {
'img': 'srcset'
};
var filterTypes = {
const filterTypes = {
'audio': 'media',
'embed': 'object',
'iframe': 'subdocument',
@ -1498,7 +1538,7 @@ var stopPicker = function() {
/******************************************************************************/
var startPicker = function(details) {
const startPicker = function(details) {
pickerRoot.addEventListener('load', stopPicker);
const frameDoc = pickerRoot.contentDocument;
@ -1611,7 +1651,7 @@ var startPicker = function(details) {
/******************************************************************************/
var bootstrapPicker = function() {
const bootstrapPicker = function() {
pickerRoot.removeEventListener('load', bootstrapPicker);
vAPI.shutdown.add(stopPicker);
vAPI.messaging.send(
@ -1626,7 +1666,7 @@ var bootstrapPicker = function() {
pickerRoot = document.createElement('iframe');
pickerRoot.id = vAPI.sessionId;
var pickerCSSStyle = [
const pickerCSSStyle = [
'background: transparent',
'border: 0',
'border-radius: 0',
@ -1649,12 +1689,12 @@ var pickerCSSStyle = [
].join(' !important;');
pickerRoot.style.cssText = pickerCSSStyle;
var pickerCSS1 = [
const pickerCSS1 = [
'#' + pickerRoot.id + ' {',
pickerCSSStyle,
'}'
].join('\n');
var pickerCSS2 = [
const pickerCSS2 = [
'[' + pickerRoot.id + '-clickblind] {',
'pointer-events: none !important;',
'}'

View File

@ -407,6 +407,16 @@ var matchBucket = function(url, hostname, bucket, start) {
this.epickerTarget = targetElement || '';
this.epickerZap = zap || false;
// https://github.com/uBlockOrigin/uBlock-issues/issues/40
// The element picker needs this library
vAPI.tabs.injectScript(
tabId,
{
file: '/lib/diff/swatinem_diff.js',
runAt: 'document_end'
}
);
// https://github.com/uBlockOrigin/uBlock-issues/issues/168
// Force activate the target tab once the element picker has been
// injected.