/
debugBridge.ts
140 lines (133 loc) · 4.09 KB
/
debugBridge.ts
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
import { BasicTool } from "../basic";
import ToolkitGlobal from "../managers/toolkitGlobal";
/**
* Debug bridge.
*
* @remarks
* Global variables: Zotero, window(main window).
*
* @example
* Run script directly. The `run` is URIencoded script.
*
* `zotero://ztoolkit-debug/?run=Zotero.getMainWindow().alert(%22HelloWorld!%22)&app=developer`
* @example
* Run script from file. The `file` is URIencoded path to js file starts with `file:///`
*
* `zotero://ztoolkit-debug/?file=file%3A%2F%2F%2FC%3A%2FUsers%2Fw_xia%2FDesktop%2Frun.js&app=developer`
*/
export class DebugBridge {
public static readonly version: number = 2;
public static readonly passwordPref: string =
"extensions.zotero.debug-bridge.password";
public get version(): number {
return DebugBridge.version;
}
private _disableDebugBridgePassword: boolean;
public get disableDebugBridgePassword(): boolean {
return this._disableDebugBridgePassword;
}
public set disableDebugBridgePassword(value: boolean) {
this._disableDebugBridgePassword = value;
}
public get password(): string {
return BasicTool.getZotero().Prefs.get(
DebugBridge.passwordPref,
true
) as string;
}
public set password(v: string) {
BasicTool.getZotero().Prefs.set(DebugBridge.passwordPref, v, true);
}
constructor() {
this._disableDebugBridgePassword = false;
this.initializeDebugBridge();
}
public static setModule(instance: ToolkitGlobal) {
if (
!instance.debugBridge?.version ||
instance.debugBridge.version < DebugBridge.version
) {
instance.debugBridge = new DebugBridge();
}
}
private initializeDebugBridge() {
const debugBridgeExtension = {
noContent: true,
doAction: async (uri: { spec: string }) => {
const Zotero = BasicTool.getZotero();
const window = Zotero.getMainWindow();
const uriString = uri.spec.split("//").pop();
if (!uriString) {
return;
}
const params: { [key: string]: any } = {};
uriString
.split("?")
.pop()
?.split("&")
.forEach((p: string) => {
params[p.split("=")[0]] = decodeURIComponent(p.split("=")[1]);
});
const skipPasswordCheck =
ToolkitGlobal.getInstance().debugBridge.disableDebugBridgePassword;
let allowed = false;
if (skipPasswordCheck) {
allowed = true;
} else {
// If password is not set, ask permission for commands without password.
if (
typeof params.password === "undefined" &&
typeof this.password === "undefined"
) {
allowed = window.confirm(
`External App ${
params.app
} wants to execute command without password.\nCommand:\n${(
params.run ||
params.file ||
""
).slice(
0,
100
)}\nIf you do not know what it is, please click Cancel to deny.`
);
} else {
allowed = this.password === params.password;
}
}
if (allowed) {
if (params.run) {
try {
const AsyncFunction = Object.getPrototypeOf(
async function () {}
).constructor;
const f = new AsyncFunction("Zotero,window", params.run);
await f(Zotero, window);
} catch (e) {
Zotero.debug(e);
(window as any).console.log(e);
}
}
if (params.file) {
try {
Services.scriptloader.loadSubScript(params.file, {
Zotero,
window,
});
} catch (e) {
Zotero.debug(e);
(window as any).console.log(e);
}
}
}
},
newChannel: function (uri: any) {
this.doAction(uri);
},
};
// @ts-ignore
Services.io.getProtocolHandler("zotero").wrappedJSObject._extensions[
"zotero://ztoolkit-debug"
] = debugBridgeExtension;
}
}