@@ -79,6 +79,9 @@ function safeSelf() {
79
79
if ( `${ args [ 0 ] } ` === '' ) { return ; }
80
80
this . log ( '[uBO]' , ...args ) ;
81
81
} ,
82
+ escapeRegexChars ( s ) {
83
+ return s . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
84
+ } ,
82
85
initPattern ( pattern , options = { } ) {
83
86
if ( pattern === '' ) {
84
87
return { matchAll : true } ;
@@ -99,8 +102,7 @@ function safeSelf() {
99
102
}
100
103
if ( options . flags !== undefined ) {
101
104
return {
102
- re : new this . RegExp ( pattern . replace (
103
- / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ,
105
+ re : new this . RegExp ( this . escapeRegexChars ( pattern ) ,
104
106
options . flags
105
107
) ,
106
108
expect,
@@ -119,7 +121,7 @@ function safeSelf() {
119
121
if ( pattern === '' ) { return / ^ / ; }
120
122
const match = / ^ \/ ( .+ ) \/ ( [ g i m s u ] * ) $ / . exec ( pattern ) ;
121
123
if ( match === null ) {
122
- const reStr = pattern . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g , '\\$&' ) ;
124
+ const reStr = this . escapeRegexChars ( pattern ) ;
123
125
return new RegExp ( verbatim ? `^${ reStr } $` : reStr , flags ) ;
124
126
}
125
127
try {
@@ -835,9 +837,63 @@ function objectFindOwnerFn(
835
837
836
838
/******************************************************************************/
837
839
840
+ builtinScriptlets . push ( {
841
+ name : 'get-all-cookies.fn' ,
842
+ fn : getAllCookiesFn ,
843
+ } ) ;
844
+ function getAllCookiesFn ( ) {
845
+ return document . cookie . split ( / \s * ; \s * / ) . map ( s => {
846
+ const pos = s . indexOf ( '=' ) ;
847
+ if ( pos === 0 ) { return ; }
848
+ if ( pos === - 1 ) { return `${ s . trim ( ) } =` ; }
849
+ const key = s . slice ( 0 , pos ) . trim ( ) ;
850
+ const value = s . slice ( pos + 1 ) . trim ( ) ;
851
+ return { key, value } ;
852
+ } ) . filter ( s => s !== undefined ) ;
853
+ }
854
+
855
+ /******************************************************************************/
856
+
857
+ builtinScriptlets . push ( {
858
+ name : 'get-all-local-storage.fn' ,
859
+ fn : getAllLocalStorageFn ,
860
+ } ) ;
861
+ function getAllLocalStorageFn ( which = 'localStorage' ) {
862
+ const storage = self [ which ] ;
863
+ const out = [ ] ;
864
+ for ( let i = 0 ; i < storage . length ; i ++ ) {
865
+ const key = storage . key ( i ) ;
866
+ const value = storage . getItem ( key ) ;
867
+ return { key, value } ;
868
+ }
869
+ return out ;
870
+ }
871
+
872
+ /******************************************************************************/
873
+
874
+ builtinScriptlets . push ( {
875
+ name : 'get-cookie.fn' ,
876
+ fn : getCookieFn ,
877
+ } ) ;
878
+ function getCookieFn (
879
+ name = ''
880
+ ) {
881
+ for ( const s of document . cookie . split ( / \s * ; \s * / ) ) {
882
+ const pos = s . indexOf ( '=' ) ;
883
+ if ( pos === - 1 ) { continue ; }
884
+ if ( s . slice ( 0 , pos ) !== name ) { continue ; }
885
+ return s . slice ( pos + 1 ) . trim ( ) ;
886
+ }
887
+ }
888
+
889
+ /******************************************************************************/
890
+
838
891
builtinScriptlets . push ( {
839
892
name : 'set-cookie.fn' ,
840
893
fn : setCookieFn ,
894
+ dependencies : [
895
+ 'get-cookie.fn' ,
896
+ ] ,
841
897
} ) ;
842
898
function setCookieFn (
843
899
trusted = false ,
@@ -847,16 +903,7 @@ function setCookieFn(
847
903
path = '' ,
848
904
options = { } ,
849
905
) {
850
- const getCookieValue = name => {
851
- for ( const s of document . cookie . split ( / \s * ; \s * / ) ) {
852
- const pos = s . indexOf ( '=' ) ;
853
- if ( pos === - 1 ) { continue ; }
854
- if ( s . slice ( 0 , pos ) !== name ) { continue ; }
855
- return s . slice ( pos + 1 ) ;
856
- }
857
- } ;
858
-
859
- const cookieBefore = getCookieValue ( name ) ;
906
+ const cookieBefore = getCookieFn ( name ) ;
860
907
if ( cookieBefore !== undefined && options . dontOverwrite ) { return ; }
861
908
if ( cookieBefore === value && options . reload ) { return ; }
862
909
@@ -884,7 +931,7 @@ function setCookieFn(
884
931
} catch ( _ ) {
885
932
}
886
933
887
- if ( options . reload && getCookieValue ( name ) === value ) {
934
+ if ( options . reload && getCookieFn ( name ) === value ) {
888
935
window . location . reload ( ) ;
889
936
}
890
937
}
@@ -4029,6 +4076,9 @@ function trustedSetSessionStorageItem(key = '', value = '') {
4029
4076
builtinScriptlets . push ( {
4030
4077
name : 'trusted-replace-fetch-response.js' ,
4031
4078
requiresTrust : true ,
4079
+ aliases : [
4080
+ 'trusted-rpfr.js' ,
4081
+ ] ,
4032
4082
fn : trustedReplaceFetchResponse ,
4033
4083
dependencies : [
4034
4084
'replace-fetch-response.fn' ,
@@ -4140,23 +4190,67 @@ builtinScriptlets.push({
4140
4190
fn : trustedClickElement ,
4141
4191
world : 'ISOLATED' ,
4142
4192
dependencies : [
4193
+ 'get-all-cookies.fn' ,
4194
+ 'get-all-local-storage.fn' ,
4143
4195
'run-at-html-element.fn' ,
4144
4196
'safe-self.fn' ,
4145
4197
] ,
4146
4198
} ) ;
4147
4199
function trustedClickElement (
4148
4200
selectors = '' ,
4149
- extraMatch = '' , // not yet supported
4201
+ extraMatch = '' ,
4150
4202
delay = ''
4151
4203
) {
4152
- if ( extraMatch !== '' ) { return ; }
4153
-
4154
4204
const safe = safeSelf ( ) ;
4155
4205
const extraArgs = safe . getExtraArgs ( Array . from ( arguments ) , 3 ) ;
4156
4206
const uboLog = extraArgs . log !== undefined
4157
4207
? ( ( ...args ) => { safe . uboLog ( ...args ) ; } )
4158
4208
: ( ( ) => { } ) ;
4159
4209
4210
+ if ( extraMatch !== '' ) {
4211
+ const assertions = extraMatch . split ( ',' ) . map ( s => {
4212
+ const pos1 = s . indexOf ( ':' ) ;
4213
+ const s1 = pos1 !== - 1 ? s . slice ( 0 , pos1 ) : s ;
4214
+ const not = s1 . startsWith ( '!' ) ;
4215
+ const type = not ? s1 . slice ( 1 ) : s1 ;
4216
+ const s2 = pos1 !== - 1 ? s . slice ( pos1 + 1 ) . trim ( ) : '' ;
4217
+ if ( s2 === '' ) { return ; }
4218
+ const out = { not, type } ;
4219
+ const match = / ^ \/ ( .+ ) \/ ( i ? ) $ / . exec ( s2 ) ;
4220
+ if ( match !== null ) {
4221
+ out . re = new RegExp ( match [ 1 ] , match [ 2 ] || undefined ) ;
4222
+ return out ;
4223
+ }
4224
+ const pos2 = s2 . indexOf ( '=' ) ;
4225
+ const key = pos2 !== - 1 ? s2 . slice ( 0 , pos2 ) . trim ( ) : s2 ;
4226
+ const value = pos2 !== - 1 ? s2 . slice ( pos2 + 1 ) . trim ( ) : '' ;
4227
+ out . re = new RegExp ( `^${ this . escapeRegexChars ( key ) } =${ this . escapeRegexChars ( value ) } ` ) ;
4228
+ return out ;
4229
+ } ) . filter ( details => details !== undefined ) ;
4230
+ const allCookies = assertions . some ( o => o . type === 'cookie' )
4231
+ ? getAllCookiesFn ( )
4232
+ : [ ] ;
4233
+ const allStorageItems = assertions . some ( o => o . type === 'localStorage' )
4234
+ ? getAllLocalStorageFn ( )
4235
+ : [ ] ;
4236
+ const hasNeedle = ( haystack , needle ) => {
4237
+ for ( const { key, value } of haystack ) {
4238
+ if ( needle . test ( `${ key } =${ value } ` ) ) { return true ; }
4239
+ }
4240
+ return false ;
4241
+ } ;
4242
+ for ( const { not, type, re } of assertions ) {
4243
+ switch ( type ) {
4244
+ case 'cookie' :
4245
+ if ( hasNeedle ( allCookies , re ) === not ) { return ; }
4246
+ break ;
4247
+ case 'localStorage' :
4248
+ if ( hasNeedle ( allStorageItems , re ) === not ) { return ; }
4249
+ break ;
4250
+ }
4251
+ }
4252
+ }
4253
+
4160
4254
const querySelectorEx = ( selector , context = document ) => {
4161
4255
const pos = selector . indexOf ( ' >>> ' ) ;
4162
4256
if ( pos === - 1 ) { return context . querySelector ( selector ) ; }
0 commit comments