@@ -1291,8 +1291,8 @@ function jsonPruneFetchResponse(
12911291 rawNeedlePaths ,
12921292 { matchAll : true } ,
12931293 extraArgs
1294- ) ;
1295- if ( typeof objAfter !== 'object' ) { return responseBefore ; }
1294+ ) ;
1295+ if ( typeof objAfter !== 'object' ) { return responseBefore ; }
12961296 const responseAfter = Response . json ( objAfter , {
12971297 status : responseBefore . status ,
12981298 statusText : responseBefore . statusText ,
@@ -1321,6 +1321,113 @@ function jsonPruneFetchResponse(
13211321
13221322/******************************************************************************/
13231323
1324+ builtinScriptlets . push ( {
1325+ name : 'json-prune-xhr-response.js' ,
1326+ fn : jsonPruneXhrResponse ,
1327+ dependencies : [
1328+ 'match-object-properties.fn' ,
1329+ 'object-prune.fn' ,
1330+ 'parse-properties-to-match.fn' ,
1331+ 'safe-self.fn' ,
1332+ 'should-log.fn' ,
1333+ ] ,
1334+ } ) ;
1335+ function jsonPruneXhrResponse (
1336+ rawPrunePaths = '' ,
1337+ rawNeedlePaths = ''
1338+ ) {
1339+ const safe = safeSelf ( ) ;
1340+ const xhrInstances = new WeakMap ( ) ;
1341+ const extraArgs = safe . getExtraArgs ( Array . from ( arguments ) , 2 ) ;
1342+ const logLevel = shouldLog ( { log : rawPrunePaths === '' || extraArgs . log , } ) ;
1343+ const log = logLevel ? ( ( ...args ) => { safe . uboLog ( ...args ) ; } ) : ( ( ) => { } ) ;
1344+ const propNeedles = parsePropertiesToMatch ( extraArgs . propsToMatch , 'url' ) ;
1345+ self . XMLHttpRequest = class extends self . XMLHttpRequest {
1346+ open ( method , url , ...args ) {
1347+ const outerXhr = this ;
1348+ const xhrDetails = { method, url, args : [ ...args ] } ;
1349+ let outcome = 'match' ;
1350+ if ( propNeedles . size !== 0 ) {
1351+ if ( matchObjectProperties ( propNeedles , { method, url } ) === false ) {
1352+ outcome = 'nomatch' ;
1353+ }
1354+ }
1355+ if ( outcome === logLevel || outcome === 'all' ) {
1356+ log ( `xhr.open(${ method } , ${ url } , ${ args . join ( ', ' ) } )` ) ;
1357+ }
1358+ if ( outcome === 'match' ) {
1359+ xhrInstances . set ( outerXhr , xhrDetails ) ;
1360+ }
1361+ return super . open ( method , url , ...args ) ;
1362+ }
1363+ send ( ...args ) {
1364+ const outerXhr = this ;
1365+ const xhrDetails = xhrInstances . get ( outerXhr ) ;
1366+ if ( xhrDetails === undefined ) {
1367+ return super . send ( ...args ) ;
1368+ }
1369+ switch ( outerXhr . responseType ) {
1370+ case '' :
1371+ case 'json' :
1372+ case 'text' :
1373+ break ;
1374+ default :
1375+ return super . send ( ...args ) ;
1376+ }
1377+ const innerXhr = new safe . XMLHttpRequest ( ) ;
1378+ innerXhr . open ( xhrDetails . method , xhrDetails . url , ...xhrDetails . args ) ;
1379+ innerXhr . responseType = outerXhr . responseType ;
1380+ innerXhr . onloadend = function ( ) {
1381+ let objBefore ;
1382+ switch ( outerXhr . responseType ) {
1383+ case '' :
1384+ case 'text' :
1385+ try {
1386+ objBefore = safe . jsonParse ( innerXhr . responseText ) ;
1387+ } catch ( ex ) {
1388+ }
1389+ break ;
1390+ case 'json' :
1391+ objBefore = innerXhr . response ;
1392+ break ;
1393+ default :
1394+ break ;
1395+ }
1396+ let objAfter ;
1397+ if ( typeof objBefore === 'object' ) {
1398+ objAfter = objectPrune (
1399+ objBefore ,
1400+ rawPrunePaths ,
1401+ rawNeedlePaths ,
1402+ { matchAll : true } ,
1403+ extraArgs
1404+ ) ;
1405+ }
1406+ let response = objAfter || objBefore ;
1407+ let responseText = '' ;
1408+ if ( typeof response === 'object' && outerXhr . responseType !== 'json' ) {
1409+ response = responseText = safe . jsonStringify ( response ) ;
1410+ }
1411+ Object . defineProperties ( outerXhr , {
1412+ readyState : { value : 4 } ,
1413+ response : { value : response } ,
1414+ responseText : { value : responseText } ,
1415+ responseXML : { value : null } ,
1416+ responseURL : { value : innerXhr . url } ,
1417+ status : { value : innerXhr . status } ,
1418+ statusText : { value : innerXhr . statusText } ,
1419+ } ) ;
1420+ outerXhr . dispatchEvent ( new Event ( 'readystatechange' ) ) ;
1421+ outerXhr . dispatchEvent ( new Event ( 'load' ) ) ;
1422+ outerXhr . dispatchEvent ( new Event ( 'loadend' ) ) ;
1423+ } ;
1424+ innerXhr . send ( ...args ) ;
1425+ }
1426+ } ;
1427+ }
1428+
1429+ /******************************************************************************/
1430+
13241431// There is still code out there which uses `eval` in lieu of `JSON.parse`.
13251432
13261433builtinScriptlets . push ( {
@@ -3622,3 +3729,102 @@ function trustedReplaceFetchResponse(
36223729}
36233730
36243731/******************************************************************************/
3732+
3733+ builtinScriptlets . push ( {
3734+ name : 'trusted-replace-xhr-response.js' ,
3735+ fn : trustedReplaceXhrResponse ,
3736+ dependencies : [
3737+ 'match-object-properties.fn' ,
3738+ 'parse-properties-to-match.fn' ,
3739+ 'safe-self.fn' ,
3740+ 'should-log.fn' ,
3741+ ] ,
3742+ } ) ;
3743+ function trustedReplaceXhrResponse (
3744+ pattern = '' ,
3745+ replacement = '' ,
3746+ propsToMatch = ''
3747+ ) {
3748+ const safe = safeSelf ( ) ;
3749+ const xhrInstances = new WeakMap ( ) ;
3750+ const extraArgs = safe . getExtraArgs ( Array . from ( arguments ) , 3 ) ;
3751+ const logLevel = shouldLog ( {
3752+ log : pattern === '' && 'all' || extraArgs . log ,
3753+ } ) ;
3754+ const log = logLevel ? ( ( ...args ) => { safe . uboLog ( ...args ) ; } ) : ( ( ) => { } ) ;
3755+ if ( pattern === '*' ) { pattern = '.*' ; }
3756+ const rePattern = safe . patternToRegex ( pattern ) ;
3757+ const propNeedles = parsePropertiesToMatch ( propsToMatch , 'url' ) ;
3758+ self . XMLHttpRequest = class extends self . XMLHttpRequest {
3759+ open ( method , url , ...args ) {
3760+ const outerXhr = this ;
3761+ const xhrDetails = { method, url, args : [ ...args ] } ;
3762+ let outcome = 'match' ;
3763+ if ( propNeedles . size !== 0 ) {
3764+ if ( matchObjectProperties ( propNeedles , { method, url } ) === false ) {
3765+ outcome = 'nomatch' ;
3766+ }
3767+ }
3768+ if ( outcome === logLevel || outcome === 'all' ) {
3769+ log ( `xhr.open(${ method } , ${ url } , ${ args . join ( ', ' ) } )` ) ;
3770+ }
3771+ if ( outcome === 'match' ) {
3772+ xhrInstances . set ( outerXhr , xhrDetails ) ;
3773+ }
3774+ return super . open ( method , url , ...args ) ;
3775+ }
3776+ send ( ...args ) {
3777+ const outerXhr = this ;
3778+ const xhrDetails = xhrInstances . get ( outerXhr ) ;
3779+ if ( xhrDetails === undefined ) {
3780+ return super . send ( ...args ) ;
3781+ }
3782+ switch ( outerXhr . responseType ) {
3783+ case '' :
3784+ case 'json' :
3785+ case 'text' :
3786+ break ;
3787+ default :
3788+ return super . send ( ...args ) ;
3789+ }
3790+ const innerXhr = new safe . XMLHttpRequest ( ) ;
3791+ innerXhr . open ( xhrDetails . method , xhrDetails . url , ...xhrDetails . args ) ;
3792+ innerXhr . responseType = outerXhr . responseType ;
3793+ innerXhr . onloadend = function ( ) {
3794+ const textBefore = innerXhr . responseText ;
3795+ const textAfter = textBefore . replace ( rePattern , replacement ) ;
3796+ const outcome = textAfter !== textBefore ? 'match' : 'nomatch' ;
3797+ if ( outcome === logLevel || logLevel === 'all' ) {
3798+ log (
3799+ `trusted-replace-xhr-response (${ outcome } )` ,
3800+ `\n\tpattern: ${ pattern } ` ,
3801+ `\n\treplacement: ${ replacement } ` ,
3802+ ) ;
3803+ }
3804+ let response = innerXhr . responseText ;
3805+ if ( outerXhr . responseType === 'json' ) {
3806+ try {
3807+ response = safe . jsonParse ( innerXhr . responseText ) ;
3808+ } catch ( ex ) {
3809+ response = innerXhr . response ;
3810+ }
3811+ }
3812+ Object . defineProperties ( outerXhr , {
3813+ readyState : { value : 4 } ,
3814+ response : { value : response } ,
3815+ responseText : { value : textAfter } ,
3816+ responseXML : { value : null } ,
3817+ responseURL : { value : innerXhr . url } ,
3818+ status : { value : innerXhr . status } ,
3819+ statusText : { value : innerXhr . statusText } ,
3820+ } ) ;
3821+ outerXhr . dispatchEvent ( new Event ( 'readystatechange' ) ) ;
3822+ outerXhr . dispatchEvent ( new Event ( 'load' ) ) ;
3823+ outerXhr . dispatchEvent ( new Event ( 'loadend' ) ) ;
3824+ } ;
3825+ innerXhr . send ( ...args ) ;
3826+ }
3827+ } ;
3828+ }
3829+
3830+ /******************************************************************************/
0 commit comments