blob: 43ffe6c7e602792e59ad0f9d0d21e1846aca2d03 [file] [log] [blame]
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +01001import MWOptionsWatcherClient from '../../common/mainWorldOptionsWatcher/Client.js';
2import {convertJSONToResponse, getResponseJSON} from '../utils.js';
3
Adrià Vilanova Martínez2d9be8d2022-12-28 00:50:14 +01004import loadMoreThread from './loadMoreThread.js';
5import flattenThread from './flattenThread.js';
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +01006
7export const responseModifiers = [
Adrià Vilanova Martínez2d9be8d2022-12-28 00:50:14 +01008 loadMoreThread,
9 flattenThread,
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010010];
11
12// Content script target
13export const kCSTarget = 'TWPT-XHRInterceptorOptionsWatcher-CS';
14// Main world (AKA regular web page) target
15export const kMWTarget = 'TWPT-XHRInterceptorOptionsWatcher-MW';
16
17export default class ResponseModifier {
18 constructor() {
19 this.optionsWatcher = new MWOptionsWatcherClient(
20 Array.from(this.watchingFeatures()), kCSTarget, kMWTarget);
21 }
22
23 watchingFeatures(modifiers) {
24 if (!modifiers) modifiers = responseModifiers;
25
26 const union = new Set();
27 for (const m of modifiers) {
28 if (!m.featureGated) continue;
29 for (const feature of m.features) union.add(feature);
30 }
31 return union;
32 }
33
34 async #getMatchingModifiers(request) {
35 // First filter modifiers which match the request URL regex.
36 const urlModifiers = responseModifiers.filter(
37 modifier => request.$TWPTRequestURL.match(modifier.urlRegex));
38
39 // Now filter modifiers which require a certain feature to be enabled
40 // (feature-gated modifiers).
41 const featuresAreEnabled = await this.optionsWatcher.areEnabled(
42 Array.from(this.watchingFeatures(urlModifiers)));
43
44 // #!if !production
45 if (Object.keys(featuresAreEnabled).length > 0) {
46 console.info(
47 '[XHR Interceptor - Response Modifier] Requested features',
48 featuresAreEnabled, 'for request', request.$TWPTRequestURL);
49 }
50 // #!endif
51
52 return urlModifiers.filter(modifier => {
53 return !modifier.featureGated || modifier.isEnabled(featuresAreEnabled);
54 });
55 }
56
Adrià Vilanova Martínezb47ec062023-01-15 17:43:26 +010057 async intercept(request) {
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010058 const matchingModifiers = await this.#getMatchingModifiers(request);
59
60 // If we didn't find any matching modifiers, return the response right away.
Adrià Vilanova Martínezb47ec062023-01-15 17:43:26 +010061 if (matchingModifiers.length === 0) return request.xhr.response;
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010062
63 // Otherwise, apply the modifiers sequentially and set the new response.
64 let json = getResponseJSON({
65 responseType: request.xhr.responseType,
66 response: request.xhr.response,
67 $TWPTRequestURL: request.$TWPTRequestURL,
68 $isArrayProto: request.$isArrayProto,
69 });
70 for (const modifier of matchingModifiers) {
71 json = await modifier.interceptor(request, json);
72 }
Adrià Vilanova Martínezb47ec062023-01-15 17:43:26 +010073 const response = convertJSONToResponse(request, json);
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010074 request.$newResponse = response;
75 request.$responseModified = true;
76 }
77}