Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/core/lib/DevtoolsPreferences.ts
1029 views
1
/**
2
* Copyright (c) Microsoft Corporation.
3
* Modifications copyright (c) Data Liberation Foundation Inc.
4
*
5
* Licensed under the Apache License, Version 2.0 (the "License");
6
* you may not use this file except in compliance with the License.
7
* You may obtain a copy of the License at
8
*
9
* http://www.apache.org/licenses/LICENSE-2.0
10
*
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
16
*/
17
import IDevtoolsSession, { Protocol } from '@secret-agent/interfaces/IDevtoolsSession';
18
import * as fs from 'fs';
19
import { bindFunctions } from '@secret-agent/commons/utils';
20
import BindingCalledEvent = Protocol.Runtime.BindingCalledEvent;
21
22
const devtoolsPreferencesCallback = '_DevtoolsPreferencesCallback';
23
24
export default class DevtoolsPreferences {
25
private cachedPreferences: any;
26
constructor(readonly preferencesPath: string) {
27
bindFunctions(this);
28
}
29
30
installOnConnect(session: IDevtoolsSession): Promise<void> {
31
session.on('Runtime.bindingCalled', event => this.onPreferenceAction(session, event));
32
33
return Promise.all([
34
session.send('Runtime.enable'),
35
session.send('Runtime.addBinding', { name: devtoolsPreferencesCallback }),
36
session.send('Page.enable'),
37
session.send('Page.addScriptToEvaluateOnNewDocument', {
38
source: `(function devtoolsPreferencesInterceptor() {
39
const toIntercept = ['getPreferences', 'setPreference', 'removePreference', 'clearPreferences'];
40
41
let inspector;
42
Object.defineProperty(window, 'InspectorFrontendHost', {
43
configurable: true,
44
enumerable: true,
45
get() { return inspector; },
46
set(v) {
47
inspector = v;
48
// devtoolsHost is initiated when Inspector is created
49
window.DevToolsHost.sendMessageToEmbedder = new Proxy(window.DevToolsHost.sendMessageToEmbedder, {
50
apply(target, thisArg, args) {
51
const method = JSON.parse(args[0]).method;
52
if (toIntercept.includes(method)) {
53
return window.${devtoolsPreferencesCallback}(args[0]);
54
}
55
return Reflect.apply(...arguments);
56
}
57
});
58
},
59
});
60
})()`,
61
}),
62
session.send('Runtime.runIfWaitingForDebugger'),
63
]).catch(() => null);
64
}
65
66
private async onPreferenceAction(
67
session: IDevtoolsSession,
68
event: BindingCalledEvent,
69
): Promise<void> {
70
if (event.name !== devtoolsPreferencesCallback) return;
71
72
const { id, method, params } = JSON.parse(event.payload);
73
74
this.load();
75
76
let result;
77
if (method === 'getPreferences') {
78
result = this.cachedPreferences;
79
} else {
80
if (method === 'setPreference') {
81
this.cachedPreferences[params[0]] = params[1];
82
} else if (method === 'removePreference') {
83
delete this.cachedPreferences[params[0]];
84
} else if (method === 'clearPreferences') {
85
this.cachedPreferences = {};
86
}
87
this.save();
88
}
89
90
await session
91
.send('Runtime.evaluate', {
92
// built-in devtools function/api
93
expression: `window.DevToolsAPI.embedderMessageAck(${id}, ${JSON.stringify(result)})`,
94
contextId: event.executionContextId,
95
})
96
.catch(() => null);
97
}
98
99
private save(): void {
100
fs.writeFileSync(this.preferencesPath, JSON.stringify(this.cachedPreferences, null, 2), 'utf8');
101
}
102
103
private load(): any {
104
if (this.cachedPreferences === undefined) {
105
try {
106
const json = fs.readFileSync(this.preferencesPath, 'utf8');
107
this.cachedPreferences = JSON.parse(json);
108
} catch (e) {
109
this.cachedPreferences = {};
110
}
111
}
112
}
113
}
114
115