@@ -79,6 +79,9 @@ function safeSelf() {
7979            if  (  `${ args [ 0 ] }  `  ===  ''  )  {  return ;  } 
8080            this . log ( '[uBO]' ,  ...args ) ; 
8181        } , 
82+         escapeRegexChars ( s )  { 
83+             return  s . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g,  '\\$&' ) ; 
84+         } , 
8285        initPattern ( pattern ,  options  =  { } )  { 
8386            if  (  pattern  ===  ''  )  { 
8487                return  {  matchAll : true  } ; 
@@ -99,8 +102,7 @@ function safeSelf() {
99102            } 
100103            if  (  options . flags  !==  undefined  )  { 
101104                return  { 
102-                     re : new  this . RegExp ( pattern . replace ( 
103-                         / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g,  '\\$&' ) , 
105+                     re : new  this . RegExp ( this . escapeRegexChars ( pattern ) , 
104106                        options . flags 
105107                    ) , 
106108                    expect, 
@@ -119,7 +121,7 @@ function safeSelf() {
119121            if  (  pattern  ===  ''  )  {  return  / ^ / ;  } 
120122            const  match  =  / ^ \/ ( .+ ) \/ ( [ g i m s u ] * ) $ / . exec ( pattern ) ; 
121123            if  (  match  ===  null  )  { 
122-                 const  reStr  =  pattern . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g ,   '\\$&' ) ; 
124+                 const  reStr  =  this . escapeRegexChars ( pattern ) ; 
123125                return  new  RegExp ( verbatim  ? `^${ reStr }  $`  : reStr ,  flags ) ; 
124126            } 
125127            try  { 
@@ -835,9 +837,63 @@ function objectFindOwnerFn(
835837
836838/******************************************************************************/ 
837839
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+ 
838891builtinScriptlets . push ( { 
839892    name : 'set-cookie.fn' , 
840893    fn : setCookieFn , 
894+     dependencies : [ 
895+         'get-cookie.fn' , 
896+     ] , 
841897} ) ; 
842898function  setCookieFn ( 
843899    trusted  =  false , 
@@ -847,16 +903,7 @@ function setCookieFn(
847903    path  =  '' , 
848904    options  =  { } , 
849905)  { 
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 ) ; 
860907    if  (  cookieBefore  !==  undefined  &&  options . dontOverwrite  )  {  return ;  } 
861908    if  (  cookieBefore  ===  value  &&  options . reload  )  {  return ;  } 
862909
@@ -884,7 +931,7 @@ function setCookieFn(
884931    }  catch ( _ )  { 
885932    } 
886933
887-     if  (  options . reload  &&  getCookieValue ( name )  ===  value  )  { 
934+     if  (  options . reload  &&  getCookieFn ( name )  ===  value  )  { 
888935        window . location . reload ( ) ; 
889936    } 
890937} 
@@ -4029,6 +4076,9 @@ function trustedSetSessionStorageItem(key = '', value = '') {
40294076builtinScriptlets . push ( { 
40304077    name : 'trusted-replace-fetch-response.js' , 
40314078    requiresTrust : true , 
4079+     aliases : [ 
4080+         'trusted-rpfr.js' , 
4081+     ] , 
40324082    fn : trustedReplaceFetchResponse , 
40334083    dependencies : [ 
40344084        'replace-fetch-response.fn' , 
@@ -4140,23 +4190,67 @@ builtinScriptlets.push({
41404190    fn : trustedClickElement , 
41414191    world : 'ISOLATED' , 
41424192    dependencies : [ 
4193+         'get-all-cookies.fn' , 
4194+         'get-all-local-storage.fn' , 
41434195        'run-at-html-element.fn' , 
41444196        'safe-self.fn' , 
41454197    ] , 
41464198} ) ; 
41474199function  trustedClickElement ( 
41484200    selectors  =  '' , 
4149-     extraMatch  =  '' ,   // not yet supported 
4201+     extraMatch  =  '' , 
41504202    delay  =  '' 
41514203)  { 
4152-     if  (  extraMatch  !==  ''  )  {  return ;  } 
4153- 
41544204    const  safe  =  safeSelf ( ) ; 
41554205    const  extraArgs  =  safe . getExtraArgs ( Array . from ( arguments ) ,  3 ) ; 
41564206    const  uboLog  =  extraArgs . log  !==  undefined 
41574207        ? ( ( ...args )  =>  {  safe . uboLog ( ...args ) ;  } ) 
41584208        : ( (  )  =>  {  } ) ; 
41594209
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+ 
41604254    const  querySelectorEx  =  ( selector ,  context  =  document )  =>  { 
41614255        const  pos  =  selector . indexOf ( ' >>> ' ) ; 
41624256        if  (  pos  ===  - 1  )  {  return  context . querySelector ( selector ) ;  } 
0 commit comments