-
Notifications
You must be signed in to change notification settings - Fork 29
/
prevent-fetch.js
169 lines (160 loc) · 5.58 KB
/
prevent-fetch.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
import {
hit,
getFetchData,
objectToString,
parseMatchProps,
validateParsedData,
getMatchPropsData,
noopPromiseResolve,
getWildcardSymbol,
// following helpers should be imported and injected
// because they are used by helpers above
toRegExp,
validateStrPattern,
isEmptyObject,
getRequestData,
getObjectEntries,
getObjectFromEntries,
} from '../helpers/index';
/* eslint-disable max-len */
/**
* @scriptlet prevent-fetch
*
* @description
* 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 preventFetch(source, propsToMatch, responseBody = 'emptyObj') {
// 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;
}
let strResponseBody;
if (responseBody === 'emptyObj') {
strResponseBody = '{}';
} else if (responseBody === 'emptyArr') {
strResponseBody = '[]';
} else {
return;
}
const handlerWrapper = (target, thisArg, args) => {
let shouldPrevent = false;
const fetchData = getFetchData(args);
if (typeof propsToMatch === 'undefined') {
// log if no propsToMatch given
const logMessage = `log: fetch( ${objectToString(fetchData)} )`;
hit(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) {
hit(source);
return noopPromiseResolve(strResponseBody);
}
return Reflect.apply(target, thisArg, args);
};
const fetchHandler = {
apply: handlerWrapper,
};
fetch = new Proxy(fetch, fetchHandler); // eslint-disable-line no-global-assign
}
preventFetch.names = [
'prevent-fetch',
// aliases are needed for matching the related scriptlet converted into our syntax
'no-fetch-if.js',
'ubo-no-fetch-if.js',
'ubo-no-fetch-if',
];
preventFetch.injections = [
hit,
getFetchData,
objectToString,
parseMatchProps,
validateParsedData,
getMatchPropsData,
noopPromiseResolve,
getWildcardSymbol,
toRegExp,
validateStrPattern,
isEmptyObject,
getRequestData,
getObjectEntries,
getObjectFromEntries,
];