blob: 2d0b7ce26e1a6a5eebe6404ca8d00107ab811b11 [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
57 async intercept(request, response) {
58 const matchingModifiers = await this.#getMatchingModifiers(request);
59
60 // If we didn't find any matching modifiers, return the response right away.
61 if (matchingModifiers.length === 0) return response;
62
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 }
73 response = convertJSONToResponse(request, json);
74 request.$newResponse = response;
75 request.$responseModified = true;
76 }
77}