blob: b822153233fc5191837182484da73a0877dd41cb [file] [log] [blame]
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +01001import MWOptionsWatcherClient from '../../common/mainWorldOptionsWatcher/Client.js';
Adrià Vilanova Martínez8a17fa82023-02-04 19:19:27 +01002import {convertJSONToResponse, convertJSONToResponseText, getResponseJSON} from '../utils.js';
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +01003
Adrià Vilanova Martínez7c1a3c12024-12-05 15:34:40 +01004import createMessageRemoveParentRef from './createMessageRemoveParentRef';
5import flattenThread from './flattenThread';
6import loadMoreThread from './loadMoreThread';
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +01007
8export const responseModifiers = [
Adrià Vilanova Martínez2d9be8d2022-12-28 00:50:14 +01009 loadMoreThread,
10 flattenThread,
Adrià Vilanova Martínez4fb615f2023-02-04 18:55:16 +010011 createMessageRemoveParentRef,
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010012];
13
14// Content script target
15export const kCSTarget = 'TWPT-XHRInterceptorOptionsWatcher-CS';
16// Main world (AKA regular web page) target
17export const kMWTarget = 'TWPT-XHRInterceptorOptionsWatcher-MW';
18
19export default class ResponseModifier {
20 constructor() {
21 this.optionsWatcher = new MWOptionsWatcherClient(
22 Array.from(this.watchingFeatures()), kCSTarget, kMWTarget);
23 }
24
25 watchingFeatures(modifiers) {
26 if (!modifiers) modifiers = responseModifiers;
27
28 const union = new Set();
29 for (const m of modifiers) {
30 if (!m.featureGated) continue;
31 for (const feature of m.features) union.add(feature);
32 }
33 return union;
34 }
35
36 async #getMatchingModifiers(request) {
37 // First filter modifiers which match the request URL regex.
38 const urlModifiers = responseModifiers.filter(
39 modifier => request.$TWPTRequestURL.match(modifier.urlRegex));
40
41 // Now filter modifiers which require a certain feature to be enabled
42 // (feature-gated modifiers).
43 const featuresAreEnabled = await this.optionsWatcher.areEnabled(
44 Array.from(this.watchingFeatures(urlModifiers)));
45
46 // #!if !production
47 if (Object.keys(featuresAreEnabled).length > 0) {
Adrià Vilanova Martínez5b987932023-11-16 02:18:44 +010048 console.debug(
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010049 '[XHR Interceptor - Response Modifier] Requested features',
50 featuresAreEnabled, 'for request', request.$TWPTRequestURL);
51 }
52 // #!endif
53
54 return urlModifiers.filter(modifier => {
55 return !modifier.featureGated || modifier.isEnabled(featuresAreEnabled);
56 });
57 }
58
Adrià Vilanova Martínezb47ec062023-01-15 17:43:26 +010059 async intercept(request) {
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010060 const matchingModifiers = await this.#getMatchingModifiers(request);
61
62 // If we didn't find any matching modifiers, return the response right away.
Adrià Vilanova Martínezb47ec062023-01-15 17:43:26 +010063 if (matchingModifiers.length === 0) return request.xhr.response;
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010064
65 // Otherwise, apply the modifiers sequentially and set the new response.
66 let json = getResponseJSON({
67 responseType: request.xhr.responseType,
68 response: request.xhr.response,
69 $TWPTRequestURL: request.$TWPTRequestURL,
70 $isArrayProto: request.$isArrayProto,
71 });
72 for (const modifier of matchingModifiers) {
73 json = await modifier.interceptor(request, json);
74 }
Adrià Vilanova Martínez8a17fa82023-02-04 19:19:27 +010075 request.$newResponse = convertJSONToResponse(request, json);
76 request.$newResponseText = convertJSONToResponseText(request, json);
Adrià Vilanova Martínezac2a5612022-12-27 21:51:40 +010077 request.$responseModified = true;
78 }
79}