Skip to content

Commit

Permalink
add replacement logic & improve description
Browse files Browse the repository at this point in the history
  • Loading branch information
stanislav-atr committed Oct 24, 2022
1 parent f96716d commit acef717
Showing 1 changed file with 80 additions and 37 deletions.
117 changes: 80 additions & 37 deletions src/scriptlets/trusted-replace-fetch-response.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,63 +22,55 @@ import {
/**
* @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-
* @description
* Replaces response text content of `fetch` requests if **all** given parameters match.
*
* **Syntax**
* ```
* example.org#%#//scriptlet('prevent-fetch'[, propsToMatch[, responseBody]])
* example.org#%#//scriptlet('trusted-replace-fetch-response'[, pattern, replacement[, propsToMatch]])
* ```
*
* - pattern - optional, argument for matching contents of responseText that should be replaced. If set, `replacement` is required;
* possible values:
* - '*' to match all text content
* - string
* - regular expression
* - replacement — optional, should be set if `pattern` is set. String to replace matched content with. Empty string to remove content.
* - `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.
* which is useful for debugging but only allowed for production filter lists.
*
* > Scriptlet does nothing if response body can't be converted to text.
*
* **Examples**
* 1. Log all fetch calls
* ```
* example.org#%#//scriptlet('prevent-fetch')
* ```
*
* 2. Prevent all fetch calls
* 2. Replace response text content of fetch requests with specific url
* ```
* example.org#%#//scriptlet('prevent-fetch', '*')
* OR
* example.org#%#//scriptlet('prevent-fetch', '')
* example.org#%#//scriptlet('trusted-replace-fetch-response', 'adb_detect:true', 'adb_detect:false', 'example.org')
* example.org#%#//scriptlet('trusted-replace-fetch-response', '/#EXT-X-VMAP-AD-BREAK[\s\S]*?/', '#EXT-X-ENDLIST', 'example.org')
* ```
*
* 3. Prevent fetch call for specific url
* 3. Remove all text content of fetch responses with specific request method
* ```
* example.org#%#//scriptlet('prevent-fetch', '/url\\.part/')
* example.org#%#//scriptlet('trusted-replace-fetch-response', '*', '', 'method:GET')
* ```
*
* 4. Prevent fetch call for specific request method
* 4. Replace response text content of fetch requests matching by URL regex and request methods
* ```
* example.org#%#//scriptlet('prevent-fetch', 'method:HEAD')
* example.org#%#//scriptlet('trusted-replace-fetch-response', '/#EXT-X-VMAP-AD-BREAK[\s\S]*?/', '#EXT-X-ENDLIST', '/\.m3u8/ method:/GET|HEAD/')
* ```
*
* 5. Prevent fetch call for specific url and request method
* 5. Remove text content of all fetch responses for example.com
* ```
* 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')
* example.org#%#//scriptlet('trusted-replace-fetch-response', '*', '', 'example.com')
* ```
*/
/* eslint-enable max-len */
Expand All @@ -98,12 +90,12 @@ export function trustedReplaceFetchResponse(source, pattern = '', replacement =

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

let shouldPrevent = false;
let fetchData;

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

if (typeof propsToMatch === 'undefined') {
Expand Down Expand Up @@ -131,14 +123,65 @@ export function trustedReplaceFetchResponse(source, pattern = '', replacement =
}
}

if (shouldPrevent) {
// REPLACE CONTENT HERE
if (!shouldPrevent) {
return Reflect.apply(target, thisArg, args);
}


hit(source);
// Send original request to retrieve response data
let response;
try {
response = await nativeFetch(...args);
} catch {
return Reflect.apply(target, thisArg, args);
}

return Reflect.apply(target, thisArg, args);
// Try to convert response body to text
let bodyText;
try {
bodyText = await response.text();
} catch {
// log if response body can't be converted to a string
const fetchDataStr = objectToString(fetchData);
const logMessage = `log: Response body can't be converted to text: ${fetchDataStr}`;
log(source, logMessage);
return Reflect.apply(target, thisArg, args);
}

const {
bodyUsed,
headers,
ok,
redirected,
status,
statusText,
type,
url,
} = response;

const patternRegexp = pattern === getWildcardSymbol()
? toRegExp
: toRegExp(pattern);

const modifiedContent = bodyText.replace(patternRegexp, replacement);

// eslint-disable-next-line compat/compat
const fiddledResponse = new Response(modifiedContent, {
status,
statusText,
headers,
});

// Manually set properties which can't be set by Response constructor
Object.defineProperties(fiddledResponse, {
url: { value: url },
type: { value: type },
ok: { value: ok },
bodyUsed: { value: bodyUsed },
redirected: { value: redirected },
});

hit(source);
return fiddledResponse;
};

const fetchHandler = {
Expand Down

0 comments on commit acef717

Please sign in to comment.