mirror of
https://github.com/gorhill/uBlock.git
synced 2024-10-06 09:37:12 +02:00
Add support for extraMatch
in trusted-click-element
scriptlet
Related issue: https://github.com/uBlockOrigin/uAssets/issues/20744#issuecomment-1900710708 Reference documentation: https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-trusted-scriptlets.md#-%EF%B8%8F-trusted-click-element Except that in uBO's implementation, if a regex is given as value to match, it will be tested against an assembled "key=value" string.
This commit is contained in:
parent
728799dab9
commit
45e62c939f
@ -79,6 +79,9 @@ function safeSelf() {
|
|||||||
if ( `${args[0]}` === '' ) { return; }
|
if ( `${args[0]}` === '' ) { return; }
|
||||||
this.log('[uBO]', ...args);
|
this.log('[uBO]', ...args);
|
||||||
},
|
},
|
||||||
|
escapeRegexChars(s) {
|
||||||
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
},
|
||||||
initPattern(pattern, options = {}) {
|
initPattern(pattern, options = {}) {
|
||||||
if ( pattern === '' ) {
|
if ( pattern === '' ) {
|
||||||
return { matchAll: true };
|
return { matchAll: true };
|
||||||
@ -99,8 +102,7 @@ function safeSelf() {
|
|||||||
}
|
}
|
||||||
if ( options.flags !== undefined ) {
|
if ( options.flags !== undefined ) {
|
||||||
return {
|
return {
|
||||||
re: new this.RegExp(pattern.replace(
|
re: new this.RegExp(this.escapeRegexChars(pattern),
|
||||||
/[.*+?^${}()|[\]\\]/g, '\\$&'),
|
|
||||||
options.flags
|
options.flags
|
||||||
),
|
),
|
||||||
expect,
|
expect,
|
||||||
@ -119,7 +121,7 @@ function safeSelf() {
|
|||||||
if ( pattern === '' ) { return /^/; }
|
if ( pattern === '' ) { return /^/; }
|
||||||
const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
|
const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
|
||||||
if ( match === null ) {
|
if ( match === null ) {
|
||||||
const reStr = pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
const reStr = this.escapeRegexChars(pattern);
|
||||||
return new RegExp(verbatim ? `^${reStr}$` : reStr, flags);
|
return new RegExp(verbatim ? `^${reStr}$` : reStr, flags);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -835,9 +837,63 @@ function objectFindOwnerFn(
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'get-all-cookies.fn',
|
||||||
|
fn: getAllCookiesFn,
|
||||||
|
});
|
||||||
|
function getAllCookiesFn() {
|
||||||
|
return document.cookie.split(/\s*;\s*/).map(s => {
|
||||||
|
const pos = s.indexOf('=');
|
||||||
|
if ( pos === 0 ) { return; }
|
||||||
|
if ( pos === -1 ) { return `${s.trim()}=`; }
|
||||||
|
const key = s.slice(0, pos).trim();
|
||||||
|
const value = s.slice(pos+1).trim();
|
||||||
|
return { key, value };
|
||||||
|
}).filter(s => s !== undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'get-all-local-storage.fn',
|
||||||
|
fn: getAllLocalStorageFn,
|
||||||
|
});
|
||||||
|
function getAllLocalStorageFn(which = 'localStorage') {
|
||||||
|
const storage = self[which];
|
||||||
|
const out = [];
|
||||||
|
for ( let i = 0; i < storage.length; i++ ) {
|
||||||
|
const key = storage.key(i);
|
||||||
|
const value = storage.getItem(key);
|
||||||
|
return { key, value };
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
builtinScriptlets.push({
|
||||||
|
name: 'get-cookie.fn',
|
||||||
|
fn: getCookieFn,
|
||||||
|
});
|
||||||
|
function getCookieFn(
|
||||||
|
name = ''
|
||||||
|
) {
|
||||||
|
for ( const s of document.cookie.split(/\s*;\s*/) ) {
|
||||||
|
const pos = s.indexOf('=');
|
||||||
|
if ( pos === -1 ) { continue; }
|
||||||
|
if ( s.slice(0, pos) !== name ) { continue; }
|
||||||
|
return s.slice(pos+1).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
builtinScriptlets.push({
|
builtinScriptlets.push({
|
||||||
name: 'set-cookie.fn',
|
name: 'set-cookie.fn',
|
||||||
fn: setCookieFn,
|
fn: setCookieFn,
|
||||||
|
dependencies: [
|
||||||
|
'get-cookie.fn',
|
||||||
|
],
|
||||||
});
|
});
|
||||||
function setCookieFn(
|
function setCookieFn(
|
||||||
trusted = false,
|
trusted = false,
|
||||||
@ -847,16 +903,7 @@ function setCookieFn(
|
|||||||
path = '',
|
path = '',
|
||||||
options = {},
|
options = {},
|
||||||
) {
|
) {
|
||||||
const getCookieValue = name => {
|
const cookieBefore = getCookieFn(name);
|
||||||
for ( const s of document.cookie.split(/\s*;\s*/) ) {
|
|
||||||
const pos = s.indexOf('=');
|
|
||||||
if ( pos === -1 ) { continue; }
|
|
||||||
if ( s.slice(0, pos) !== name ) { continue; }
|
|
||||||
return s.slice(pos+1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const cookieBefore = getCookieValue(name);
|
|
||||||
if ( cookieBefore !== undefined && options.dontOverwrite ) { return; }
|
if ( cookieBefore !== undefined && options.dontOverwrite ) { return; }
|
||||||
if ( cookieBefore === value && options.reload ) { return; }
|
if ( cookieBefore === value && options.reload ) { return; }
|
||||||
|
|
||||||
@ -884,7 +931,7 @@ function setCookieFn(
|
|||||||
} catch(_) {
|
} catch(_) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( options.reload && getCookieValue(name) === value ) {
|
if ( options.reload && getCookieFn(name) === value ) {
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4029,6 +4076,9 @@ function trustedSetSessionStorageItem(key = '', value = '') {
|
|||||||
builtinScriptlets.push({
|
builtinScriptlets.push({
|
||||||
name: 'trusted-replace-fetch-response.js',
|
name: 'trusted-replace-fetch-response.js',
|
||||||
requiresTrust: true,
|
requiresTrust: true,
|
||||||
|
aliases: [
|
||||||
|
'trusted-rpfr.js',
|
||||||
|
],
|
||||||
fn: trustedReplaceFetchResponse,
|
fn: trustedReplaceFetchResponse,
|
||||||
dependencies: [
|
dependencies: [
|
||||||
'replace-fetch-response.fn',
|
'replace-fetch-response.fn',
|
||||||
@ -4140,23 +4190,67 @@ builtinScriptlets.push({
|
|||||||
fn: trustedClickElement,
|
fn: trustedClickElement,
|
||||||
world: 'ISOLATED',
|
world: 'ISOLATED',
|
||||||
dependencies: [
|
dependencies: [
|
||||||
|
'get-all-cookies.fn',
|
||||||
|
'get-all-local-storage.fn',
|
||||||
'run-at-html-element.fn',
|
'run-at-html-element.fn',
|
||||||
'safe-self.fn',
|
'safe-self.fn',
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
function trustedClickElement(
|
function trustedClickElement(
|
||||||
selectors = '',
|
selectors = '',
|
||||||
extraMatch = '', // not yet supported
|
extraMatch = '',
|
||||||
delay = ''
|
delay = ''
|
||||||
) {
|
) {
|
||||||
if ( extraMatch !== '' ) { return; }
|
|
||||||
|
|
||||||
const safe = safeSelf();
|
const safe = safeSelf();
|
||||||
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
|
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
|
||||||
const uboLog = extraArgs.log !== undefined
|
const uboLog = extraArgs.log !== undefined
|
||||||
? ((...args) => { safe.uboLog(...args); })
|
? ((...args) => { safe.uboLog(...args); })
|
||||||
: (( ) => { });
|
: (( ) => { });
|
||||||
|
|
||||||
|
if ( extraMatch !== '' ) {
|
||||||
|
const assertions = extraMatch.split(',').map(s => {
|
||||||
|
const pos1 = s.indexOf(':');
|
||||||
|
const s1 = pos1 !== -1 ? s.slice(0, pos1) : s;
|
||||||
|
const not = s1.startsWith('!');
|
||||||
|
const type = not ? s1.slice(1) : s1;
|
||||||
|
const s2 = pos1 !== -1 ? s.slice(pos1+1).trim() : '';
|
||||||
|
if ( s2 === '' ) { return; }
|
||||||
|
const out = { not, type };
|
||||||
|
const match = /^\/(.+)\/(i?)$/.exec(s2);
|
||||||
|
if ( match !== null ) {
|
||||||
|
out.re = new RegExp(match[1], match[2] || undefined);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
const pos2 = s2.indexOf('=');
|
||||||
|
const key = pos2 !== -1 ? s2.slice(0, pos2).trim() : s2;
|
||||||
|
const value = pos2 !== -1 ? s2.slice(pos2+1).trim() : '';
|
||||||
|
out.re = new RegExp(`^${this.escapeRegexChars(key)}=${this.escapeRegexChars(value)}`);
|
||||||
|
return out;
|
||||||
|
}).filter(details => details !== undefined);
|
||||||
|
const allCookies = assertions.some(o => o.type === 'cookie')
|
||||||
|
? getAllCookiesFn()
|
||||||
|
: [];
|
||||||
|
const allStorageItems = assertions.some(o => o.type === 'localStorage')
|
||||||
|
? getAllLocalStorageFn()
|
||||||
|
: [];
|
||||||
|
const hasNeedle = (haystack, needle) => {
|
||||||
|
for ( const { key, value } of haystack ) {
|
||||||
|
if ( needle.test(`${key}=${value}`) ) { return true; }
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
for ( const { not, type, re } of assertions ) {
|
||||||
|
switch ( type ) {
|
||||||
|
case 'cookie':
|
||||||
|
if ( hasNeedle(allCookies, re) === not ) { return; }
|
||||||
|
break;
|
||||||
|
case 'localStorage':
|
||||||
|
if ( hasNeedle(allStorageItems, re) === not ) { return; }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const querySelectorEx = (selector, context = document) => {
|
const querySelectorEx = (selector, context = document) => {
|
||||||
const pos = selector.indexOf(' >>> ');
|
const pos = selector.indexOf(' >>> ');
|
||||||
if ( pos === -1 ) { return context.querySelector(selector); }
|
if ( pos === -1 ) { return context.querySelector(selector); }
|
||||||
|
Loading…
Reference in New Issue
Block a user