Skip to content

Commit

Permalink
add trsuted-replace-fetch-response scriptlet
Browse files Browse the repository at this point in the history
  • Loading branch information
stanislav-atr committed Oct 24, 2022
1 parent 932ff82 commit f96716d
Showing 1 changed file with 172 additions and 0 deletions.
172 changes: 172 additions & 0 deletions src/scriptlets/trusted-replace-fetch-response.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import {
hit,
getFetchData,
objectToString,
parseMatchProps,
validateParsedData,
getMatchPropsData,
noopPromiseResolve,
getWildcardSymbol,
// following helpers should be imported and injected
// because they are used by helpers above
toRegExp,
isValidStrPattern,
escapeRegExp,
isEmptyObject,
getRequestData,
getObjectEntries,
getObjectFromEntries,
} from '../helpers/index';

/* eslint-disable max-len */
/**
* @scriptlet trusted-replace-fetch-response
*
* @description // FIXME
* Prevents `fetch` calls if **all** given parameters match
*
* Related UBO scriptlet:
* https://github.com/gorhill/uBlock/wiki/Resources-Library#no-fetch-ifjs-
*
* **Syntax**
* ```
* example.org#%#//scriptlet('prevent-fetch'[, propsToMatch[, responseBody]])
* ```
*
* - `propsToMatch` - optional, string of space-separated properties to match; possible props:
* - string or regular expression for matching the URL passed to fetch call; empty string, wildcard `*` or invalid regular expression will match all fetch calls
* - colon-separated pairs `name:value` where
* - `name` is [`init` option name](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#parameters)
* - `value` is string or regular expression for matching the value of the option passed to fetch call; invalid regular expression will cause any value matching
* - responseBody - optional, string for defining response body value, defaults to `emptyObj`. Possible values:
* - `emptyObj` - empty object
* - `emptyArr` - empty array
* > Usage with no arguments will log fetch calls to browser console;
* which is useful for debugging but permitted for production filter lists.
*
* **Examples**
* 1. Log all fetch calls
* ```
* example.org#%#//scriptlet('prevent-fetch')
* ```
*
* 2. Prevent all fetch calls
* ```
* example.org#%#//scriptlet('prevent-fetch', '*')
* OR
* example.org#%#//scriptlet('prevent-fetch', '')
* ```
*
* 3. Prevent fetch call for specific url
* ```
* example.org#%#//scriptlet('prevent-fetch', '/url\\.part/')
* ```
*
* 4. Prevent fetch call for specific request method
* ```
* example.org#%#//scriptlet('prevent-fetch', 'method:HEAD')
* ```
*
* 5. Prevent fetch call for specific url and request method
* ```
* example.org#%#//scriptlet('prevent-fetch', '/specified_url_part/ method:/HEAD|GET/')
* ```
*
* 6. Prevent fetch call and specify response body value
* ```
* ! Specify response body for fetch call to a specific url
* example.org#%#//scriptlet('prevent-fetch', '/specified_url_part/ method:/HEAD|GET/', 'emptyArr')
*
* ! Specify response body for all fetch calls
* example.org#%#//scriptlet('prevent-fetch', '', 'emptyArr')
* ```
*/
/* eslint-enable max-len */
export function trustedReplaceFetchResponse(source, pattern = '', replacement = '', propsToMatch = '') {
// do nothing if browser does not support fetch or Proxy (e.g. Internet Explorer)
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy
if (typeof fetch === 'undefined'
|| typeof Proxy === 'undefined'
|| typeof Response === 'undefined') {
return;
}

if (typeof pattern === 'undefined' || typeof replacement === 'undefined') {
return;
}

// eslint-disable-next-line no-console
const log = console.log.bind(console);
const nativeFetch = window.fetch;

let shouldPrevent = false;
let fetchData;

const handlerWrapper = (target, thisArg, args) => {
fetchData = getFetchData(args);

if (typeof propsToMatch === 'undefined') {
// log if no propsToMatch given
const logMessage = `log: fetch( ${objectToString(fetchData)} )`;
log(source, logMessage);
} else if (propsToMatch === '' || propsToMatch === getWildcardSymbol()) {
// prevent all fetch calls
shouldPrevent = true;
} else {
const parsedData = parseMatchProps(propsToMatch);
if (!validateParsedData(parsedData)) {
// eslint-disable-next-line no-console
console.log(`Invalid parameter: ${propsToMatch}`);
shouldPrevent = false;
} else {
const matchData = getMatchPropsData(parsedData);
// prevent only if all props match
shouldPrevent = Object.keys(matchData)
.every((matchKey) => {
const matchValue = matchData[matchKey];
return Object.prototype.hasOwnProperty.call(fetchData, matchKey)
&& matchValue.test(fetchData[matchKey]);
});
}
}

if (shouldPrevent) {
// REPLACE CONTENT HERE


hit(source);
}

return Reflect.apply(target, thisArg, args);
};

const fetchHandler = {
apply: handlerWrapper,
};

fetch = new Proxy(fetch, fetchHandler); // eslint-disable-line no-global-assign
}

trustedReplaceFetchResponse.names = [
'trusted-replace-fetch-response',

];

trustedReplaceFetchResponse.injections = [
hit,
getFetchData,
objectToString,
parseMatchProps,
validateParsedData,
getMatchPropsData,
noopPromiseResolve,
getWildcardSymbol,
toRegExp,
isValidStrPattern,
escapeRegExp,
isEmptyObject,
getRequestData,
getObjectEntries,
getObjectFromEntries,
];

0 comments on commit f96716d

Please sign in to comment.