@lynker-desktop/electron-window-manager 0.0.9-alpha.5 → 0.0.9-alpha.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -2
- package/common/index.d.ts +25 -0
- package/common/index.d.ts.map +1 -1
- package/esm/common/index.d.ts +25 -0
- package/esm/common/index.d.ts.map +1 -1
- package/esm/main/index.d.ts +44 -13
- package/esm/main/index.d.ts.map +1 -1
- package/esm/main/index.js +839 -330
- package/esm/main/index.js.map +1 -1
- package/esm/preload/index.js +17 -2
- package/esm/preload/index.js.map +1 -1
- package/esm/renderer/index.d.ts +5 -5
- package/esm/renderer/index.d.ts.map +1 -1
- package/esm/renderer/index.js +321 -193
- package/esm/renderer/index.js.map +1 -1
- package/main/index.d.ts +44 -13
- package/main/index.d.ts.map +1 -1
- package/main/index.js +839 -330
- package/main/index.js.map +1 -1
- package/package.json +4 -3
- package/preload/index.js +16 -1
- package/preload/index.js.map +1 -1
- package/renderer/index.d.ts +5 -5
- package/renderer/index.d.ts.map +1 -1
- package/renderer/index.js +322 -194
- package/renderer/index.js.map +1 -1
package/esm/main/index.js
CHANGED
|
@@ -3,7 +3,18 @@ import { app, session, BrowserWindow, BrowserView, webContents } from 'electron'
|
|
|
3
3
|
import * as remote from '@electron/remote/main';
|
|
4
4
|
import eIpc from '@lynker-desktop/electron-ipc/main';
|
|
5
5
|
import md5 from 'md5';
|
|
6
|
+
import PQueue from 'p-queue';
|
|
6
7
|
|
|
8
|
+
const getQueue = (() => {
|
|
9
|
+
let queue;
|
|
10
|
+
return async () => {
|
|
11
|
+
if (!queue) {
|
|
12
|
+
// const { default: PQueue } = await import('p-queue')
|
|
13
|
+
queue = new PQueue({ concurrency: 1 });
|
|
14
|
+
}
|
|
15
|
+
return queue;
|
|
16
|
+
};
|
|
17
|
+
})();
|
|
7
18
|
const getCustomSession = (() => {
|
|
8
19
|
let customSession;
|
|
9
20
|
return () => {
|
|
@@ -56,7 +67,9 @@ class WindowsManager {
|
|
|
56
67
|
* - 不带点的(如 example.com)只匹配主域名。
|
|
57
68
|
* - 'localhost'、'127.0.0.1'、'::1' 以及局域网 IP(如 192.168.x.x、10.x.x.x、172.16.x.x~172.31.x.x)都视为本地白名单。
|
|
58
69
|
*/
|
|
59
|
-
constructor(preload, loadingViewUrl, errorViewUrl,
|
|
70
|
+
constructor(preload, loadingViewUrl, errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList) {
|
|
71
|
+
// 按名称索引的 Map,用于加速查找
|
|
72
|
+
this.windowsByName = new Map();
|
|
60
73
|
// 预加载的窗口
|
|
61
74
|
this.preloadedBW = null;
|
|
62
75
|
// 预加载的窗口(无边框,有按钮)
|
|
@@ -67,33 +80,50 @@ class WindowsManager {
|
|
|
67
80
|
this.preloadedBV = null;
|
|
68
81
|
this.preloading = false;
|
|
69
82
|
this.webviewDomainWhiteList = [];
|
|
70
|
-
//
|
|
71
|
-
this.
|
|
72
|
-
|
|
83
|
+
// 窗口销毁检查的防抖函数,避免频繁检查
|
|
84
|
+
this.cleanupDestroyedWindowsDebounced = lodash.debounce(() => {
|
|
85
|
+
this._cleanupDestroyedWindows();
|
|
86
|
+
}, 500);
|
|
87
|
+
/**
|
|
88
|
+
* 防抖的排序方法
|
|
89
|
+
* @param window 目标窗口
|
|
90
|
+
*/
|
|
91
|
+
this.sortBrowserViewsDebounced = lodash.debounce((window, view) => {
|
|
92
|
+
this._sortBrowserViews(window, view);
|
|
93
|
+
}, 50);
|
|
73
94
|
this.preload = preload;
|
|
74
95
|
this.windows = new Map();
|
|
75
96
|
this.loadingViewUrl = `${loadingViewUrl ?? ''}`;
|
|
76
97
|
this.errorViewUrl = `${errorViewUrl ?? ''}`;
|
|
77
|
-
this.
|
|
98
|
+
this.preloadWebContentsConfig = preloadWebContentsConfig;
|
|
78
99
|
this.webviewDomainWhiteList = webviewDomainWhiteList || [];
|
|
79
|
-
log('log', '
|
|
80
|
-
if (this.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
100
|
+
log('log', 'preloadWebContentsConfig: ', this.preloadWebContentsConfig);
|
|
101
|
+
if (this.preloadWebContentsConfig) {
|
|
102
|
+
if (this.preloadWebContentsConfig.nodeIntegration === undefined) {
|
|
103
|
+
this.preloadWebContentsConfig.nodeIntegration = true;
|
|
104
|
+
}
|
|
105
|
+
if (this.preloadWebContentsConfig.contextIsolation === undefined) {
|
|
106
|
+
this.preloadWebContentsConfig.contextIsolation = false;
|
|
107
|
+
}
|
|
108
|
+
getQueue();
|
|
109
|
+
app.whenReady().then(() => {
|
|
110
|
+
if (this.preloadWebContentsConfig) {
|
|
111
|
+
this.setPreloadWebContentsConfig(this.preloadWebContentsConfig);
|
|
84
112
|
}
|
|
85
113
|
});
|
|
86
114
|
}
|
|
87
115
|
}
|
|
88
116
|
/**
|
|
89
|
-
* 设置预加载的webContents
|
|
90
|
-
* @param
|
|
117
|
+
* 设置预加载的webContents配置
|
|
118
|
+
* @param preloadWebContentsConfig 预加载的webContents配置
|
|
91
119
|
*/
|
|
92
|
-
|
|
120
|
+
setPreloadWebContentsConfig(preloadWebContentsConfig) {
|
|
93
121
|
try {
|
|
94
|
-
this.
|
|
95
|
-
if (this.
|
|
96
|
-
|
|
122
|
+
this.preloadWebContentsConfig = preloadWebContentsConfig;
|
|
123
|
+
if (this.preloadWebContentsConfig) {
|
|
124
|
+
getQueue().then(q => q.add(async () => {
|
|
125
|
+
return await this._preloadInstances();
|
|
126
|
+
}));
|
|
97
127
|
}
|
|
98
128
|
else {
|
|
99
129
|
this.preloadedBW = null;
|
|
@@ -103,9 +133,23 @@ class WindowsManager {
|
|
|
103
133
|
}
|
|
104
134
|
}
|
|
105
135
|
catch (error) {
|
|
106
|
-
log('error', '
|
|
136
|
+
log('error', 'setPreloadWebContentsConfig error:', error);
|
|
107
137
|
}
|
|
108
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Promise 超时包装函数
|
|
141
|
+
* @param promise 要包装的 Promise
|
|
142
|
+
* @param timeout 超时时间(毫秒)
|
|
143
|
+
* @param errorMessage 超时错误信息
|
|
144
|
+
*/
|
|
145
|
+
async _withTimeout(promise, timeout, errorMessage) {
|
|
146
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
147
|
+
setTimeout(() => {
|
|
148
|
+
reject(new Error(errorMessage));
|
|
149
|
+
}, timeout);
|
|
150
|
+
});
|
|
151
|
+
return Promise.race([promise, timeoutPromise]);
|
|
152
|
+
}
|
|
109
153
|
/**
|
|
110
154
|
* 预加载实例
|
|
111
155
|
*/
|
|
@@ -114,23 +158,64 @@ class WindowsManager {
|
|
|
114
158
|
return;
|
|
115
159
|
this.preloading = true;
|
|
116
160
|
try {
|
|
117
|
-
if (this.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
this.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
161
|
+
if (this.preloadWebContentsConfig) {
|
|
162
|
+
log('log', 'preloadWebContentsConfig: ', this.preloadWebContentsConfig);
|
|
163
|
+
const preloadPromises = [];
|
|
164
|
+
// 根据配置决定是否预加载普通窗口
|
|
165
|
+
if (this.preloadWebContentsConfig.enableBW !== false && !this.preloadedBW) {
|
|
166
|
+
preloadPromises.push(this._createPreloadBW({}).then(i => {
|
|
167
|
+
this.preloadedBW = i;
|
|
168
|
+
log('log', 'init preloadedBW: ', !!this.preloadedBW);
|
|
169
|
+
}).catch(error => {
|
|
170
|
+
log('error', '预加载 BW 失败:', error);
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
// 根据配置决定是否预加载无边框有按钮的窗口
|
|
174
|
+
if (this.preloadWebContentsConfig.enableBW_FramelessWithButtons !== false && !this.preloadedBW_FramelessWithButtons) {
|
|
175
|
+
preloadPromises.push(this._createPreloadBW({
|
|
176
|
+
frame: false,
|
|
177
|
+
autoHideMenuBar: true,
|
|
178
|
+
titleBarStyle: 'hidden',
|
|
179
|
+
}).then(i => {
|
|
180
|
+
this.preloadedBW_FramelessWithButtons = i;
|
|
181
|
+
log('log', 'init preloadedBW_FramelessWithButtons: ', !!this.preloadedBW_FramelessWithButtons);
|
|
182
|
+
}).catch(error => {
|
|
183
|
+
log('error', '预加载 BW_FramelessWithButtons 失败:', error);
|
|
184
|
+
}));
|
|
185
|
+
}
|
|
186
|
+
// 根据配置决定是否预加载无边框无按钮的窗口
|
|
187
|
+
if (this.preloadWebContentsConfig.enableBW_FramelessNoButtons !== false && !this.preloadedBW_FramelessNoButtons) {
|
|
188
|
+
preloadPromises.push(this._createPreloadBW({
|
|
189
|
+
frame: false,
|
|
190
|
+
autoHideMenuBar: true,
|
|
191
|
+
titleBarStyle: 'default',
|
|
192
|
+
}).then(i => {
|
|
193
|
+
this.preloadedBW_FramelessNoButtons = i;
|
|
194
|
+
log('log', 'init preloadedBW_FramelessNoButtons: ', !!this.preloadedBW_FramelessNoButtons);
|
|
195
|
+
}).catch(error => {
|
|
196
|
+
log('error', '预加载 BW_FramelessNoButtons 失败:', error);
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
// 根据配置决定是否预加载浏览器视图
|
|
200
|
+
if (this.preloadWebContentsConfig.enableBV !== false && !this.preloadedBV) {
|
|
201
|
+
preloadPromises.push(this._createPreloadBV().then(i => {
|
|
202
|
+
this.preloadedBV = i;
|
|
203
|
+
log('log', 'init preloadedBV: ', !!this.preloadedBV);
|
|
204
|
+
}).catch(error => {
|
|
205
|
+
log('error', '预加载 BV 失败:', error);
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
if (preloadPromises.length > 0) {
|
|
209
|
+
// 添加超时机制,默认 10 秒超时
|
|
210
|
+
const timeout = 10000; // 10 秒
|
|
211
|
+
try {
|
|
212
|
+
await this._withTimeout(Promise.allSettled(preloadPromises), timeout, `预加载超时(${timeout}ms)`);
|
|
213
|
+
}
|
|
214
|
+
catch (error) {
|
|
215
|
+
log('error', '预加载超时:', error);
|
|
216
|
+
// 超时后继续执行,不阻塞后续流程
|
|
217
|
+
}
|
|
218
|
+
}
|
|
134
219
|
}
|
|
135
220
|
}
|
|
136
221
|
catch (e) {
|
|
@@ -146,20 +231,25 @@ class WindowsManager {
|
|
|
146
231
|
_createPreloadBW(options = {}) {
|
|
147
232
|
return new Promise((resolve) => {
|
|
148
233
|
const preload = this.preload;
|
|
149
|
-
const url = this.
|
|
150
|
-
if (this.
|
|
234
|
+
const url = this.preloadWebContentsConfig?.url;
|
|
235
|
+
if (this.preloadWebContentsConfig?.url) {
|
|
236
|
+
const webPreferences = (options.webPreferences || {});
|
|
151
237
|
const instance = new BrowserWindow({
|
|
152
|
-
|
|
238
|
+
useContentSize: true,
|
|
153
239
|
show: false,
|
|
240
|
+
backgroundColor: '#ffffff',
|
|
241
|
+
...options,
|
|
154
242
|
webPreferences: {
|
|
155
|
-
...
|
|
243
|
+
...webPreferences,
|
|
244
|
+
sandbox: false,
|
|
156
245
|
webviewTag: true,
|
|
157
246
|
plugins: true,
|
|
158
|
-
nodeIntegration: true,
|
|
159
|
-
contextIsolation: false,
|
|
247
|
+
nodeIntegration: this.preloadWebContentsConfig?.nodeIntegration ?? true,
|
|
248
|
+
contextIsolation: this.preloadWebContentsConfig?.contextIsolation ?? false,
|
|
160
249
|
backgroundThrottling: false,
|
|
161
250
|
webSecurity: false,
|
|
162
|
-
preload: preload,
|
|
251
|
+
preload: webPreferences.preload || preload,
|
|
252
|
+
defaultEncoding: 'utf-8',
|
|
163
253
|
}
|
|
164
254
|
});
|
|
165
255
|
try {
|
|
@@ -176,14 +266,19 @@ class WindowsManager {
|
|
|
176
266
|
log('error', '预加载 BW 设置 _id 失败', error);
|
|
177
267
|
}
|
|
178
268
|
// @ts-ignore
|
|
179
|
-
log('log', '创建预BW: ', instance._id, this.
|
|
180
|
-
instance.webContents.once('did-finish-load', () => {
|
|
181
|
-
|
|
182
|
-
});
|
|
183
|
-
instance.webContents.once('did-fail-load', () => {
|
|
184
|
-
|
|
185
|
-
});
|
|
269
|
+
log('log', '创建预BW: ', instance._id, this.preloadWebContentsConfig?.url);
|
|
270
|
+
// instance.webContents.once('did-finish-load', () => {
|
|
271
|
+
// resolve(instance as BWItem);
|
|
272
|
+
// });
|
|
273
|
+
// instance.webContents.once('did-fail-load', () => {
|
|
274
|
+
// resolve(instance as BWItem);
|
|
275
|
+
// });
|
|
276
|
+
// @ts-ignore
|
|
186
277
|
instance.loadURL(url ? `${url}` : 'about:blank');
|
|
278
|
+
resolve(instance);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
resolve(null);
|
|
187
282
|
}
|
|
188
283
|
});
|
|
189
284
|
}
|
|
@@ -191,20 +286,24 @@ class WindowsManager {
|
|
|
191
286
|
* 创建预加载的浏览器视图
|
|
192
287
|
* @returns 预加载的浏览器视图
|
|
193
288
|
*/
|
|
194
|
-
_createPreloadBV() {
|
|
289
|
+
_createPreloadBV(options = {}) {
|
|
195
290
|
return new Promise((resolve) => {
|
|
196
291
|
const preload = this.preload;
|
|
197
|
-
const url = this.
|
|
198
|
-
if (this.
|
|
292
|
+
const url = this.preloadWebContentsConfig?.url;
|
|
293
|
+
if (this.preloadWebContentsConfig?.url) {
|
|
294
|
+
const webPreferences = (options.webPreferences || {});
|
|
199
295
|
const instance = new BrowserView({
|
|
200
296
|
webPreferences: {
|
|
297
|
+
...webPreferences,
|
|
298
|
+
sandbox: false,
|
|
201
299
|
webviewTag: true,
|
|
202
300
|
plugins: true,
|
|
203
|
-
nodeIntegration: true,
|
|
204
|
-
contextIsolation: false,
|
|
301
|
+
nodeIntegration: this.preloadWebContentsConfig?.nodeIntegration ?? true,
|
|
302
|
+
contextIsolation: this.preloadWebContentsConfig?.contextIsolation ?? false,
|
|
205
303
|
// backgroundThrottling: false,
|
|
206
304
|
webSecurity: false,
|
|
207
|
-
preload: preload,
|
|
305
|
+
preload: webPreferences.preload || preload,
|
|
306
|
+
defaultEncoding: 'utf-8',
|
|
208
307
|
}
|
|
209
308
|
});
|
|
210
309
|
try {
|
|
@@ -216,98 +315,111 @@ class WindowsManager {
|
|
|
216
315
|
try {
|
|
217
316
|
// @ts-ignore
|
|
218
317
|
instance._id = instance.webContents.id;
|
|
318
|
+
// 设置默认zIndex层级
|
|
319
|
+
instance._zIndex = 0;
|
|
219
320
|
}
|
|
220
321
|
catch (error) {
|
|
221
322
|
log('error', '预加载 BV 设置 _id 失败', error);
|
|
222
323
|
}
|
|
223
324
|
// @ts-ignore
|
|
224
|
-
log('log', '创建预BV: ', instance._id, this.
|
|
225
|
-
instance.webContents.once('did-finish-load', () => {
|
|
226
|
-
|
|
227
|
-
});
|
|
228
|
-
instance.webContents.once('did-fail-load', () => {
|
|
229
|
-
|
|
230
|
-
});
|
|
325
|
+
log('log', '创建预BV: ', instance._id, this.preloadWebContentsConfig?.url);
|
|
326
|
+
// instance.webContents.once('did-finish-load', () => {
|
|
327
|
+
// resolve(instance as BVItem);
|
|
328
|
+
// });
|
|
329
|
+
// instance.webContents.once('did-fail-load', () => {
|
|
330
|
+
// resolve(instance as BVItem);
|
|
331
|
+
// });
|
|
332
|
+
// @ts-ignore
|
|
231
333
|
instance.webContents.loadURL(url || 'about:blank');
|
|
334
|
+
resolve(instance);
|
|
232
335
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
create(options) {
|
|
236
|
-
return new Promise((resolve, reject) => {
|
|
237
|
-
// 将创建请求添加到队列
|
|
238
|
-
this.createQueue.push({ options, resolve, reject });
|
|
239
|
-
// 如果当前没有在创建,则开始处理队列
|
|
240
|
-
if (!this.isCreating) {
|
|
241
|
-
this.processCreateQueue();
|
|
336
|
+
else {
|
|
337
|
+
resolve(null);
|
|
242
338
|
}
|
|
243
339
|
});
|
|
244
340
|
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
this.isCreating = true;
|
|
253
|
-
while (this.createQueue.length > 0) {
|
|
254
|
-
const { options, resolve, reject } = this.createQueue.shift();
|
|
255
|
-
try {
|
|
256
|
-
const window = await this._createWindow(options);
|
|
257
|
-
resolve(window);
|
|
258
|
-
}
|
|
259
|
-
catch (error) {
|
|
260
|
-
log('error', 'create window failed:', error);
|
|
261
|
-
reject(error);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
this.isCreating = false;
|
|
341
|
+
async create(options) {
|
|
342
|
+
const queue = await getQueue();
|
|
343
|
+
const win = await queue.add(async () => {
|
|
344
|
+
const window = await this._createWindow(options);
|
|
345
|
+
return window;
|
|
346
|
+
});
|
|
347
|
+
return win;
|
|
265
348
|
}
|
|
266
349
|
/**
|
|
267
350
|
* 实际的窗口创建逻辑
|
|
268
351
|
*/
|
|
269
352
|
async _createWindow(options) {
|
|
270
353
|
let window;
|
|
271
|
-
const { usePreload = true, type = 'BW', name = 'anonymous', url, loadingView = { url: undefined }, errorView = { url: undefined }, browserWindow: browserWindowOptions, openDevTools = false, preventOriginClose = false, } = options;
|
|
354
|
+
const { usePreload = true, type = 'BW', name = 'anonymous', url, loadingView = { url: undefined }, errorView = { url: undefined }, browserWindow: browserWindowOptions, openDevTools = false, preventOriginClose = false, zIndex = 0, } = options;
|
|
355
|
+
const existingWinId = this.windowsByName.get(options.name);
|
|
356
|
+
if (existingWinId) {
|
|
357
|
+
window = this.windows.get(existingWinId);
|
|
358
|
+
if (window) {
|
|
359
|
+
return window;
|
|
360
|
+
}
|
|
361
|
+
// 清理无效的引用
|
|
362
|
+
this.windowsByName.delete(options.name);
|
|
363
|
+
}
|
|
272
364
|
options.type = type;
|
|
273
365
|
// 优先复用预创建实例
|
|
274
366
|
let preloadWin = null;
|
|
275
|
-
if (type === 'BW' && usePreload && this.
|
|
367
|
+
if (type === 'BW' && usePreload && this.preloadWebContentsConfig?.url) {
|
|
276
368
|
const bwOptions = browserWindowOptions || {};
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
369
|
+
if (bwOptions.frame === false) {
|
|
370
|
+
if (bwOptions.titleBarStyle === 'default' || !bwOptions.titleBarStyle) {
|
|
371
|
+
if (this.preloadWebContentsConfig.enableBW_FramelessNoButtons !== false && this.preloadedBW_FramelessNoButtons) {
|
|
372
|
+
preloadWin = this.preloadedBW_FramelessNoButtons;
|
|
373
|
+
this.preloadedBW_FramelessNoButtons = await this._createPreloadBW({
|
|
374
|
+
frame: false,
|
|
375
|
+
// transparent: true,
|
|
376
|
+
titleBarStyle: 'default',
|
|
377
|
+
webPreferences: {
|
|
378
|
+
preload: bwOptions?.webPreferences?.preload || this.preload,
|
|
379
|
+
}
|
|
380
|
+
});
|
|
381
|
+
}
|
|
283
382
|
}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
383
|
+
else {
|
|
384
|
+
if (this.preloadWebContentsConfig.enableBW_FramelessWithButtons !== false && this.preloadedBW_FramelessWithButtons) {
|
|
385
|
+
preloadWin = this.preloadedBW_FramelessWithButtons;
|
|
386
|
+
this.preloadedBW_FramelessWithButtons = await this._createPreloadBW({
|
|
387
|
+
frame: false,
|
|
388
|
+
// transparent: true,
|
|
389
|
+
titleBarStyle: 'hidden',
|
|
390
|
+
webPreferences: {
|
|
391
|
+
preload: this.preload,
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
}
|
|
290
395
|
}
|
|
291
396
|
}
|
|
292
397
|
else {
|
|
293
|
-
if (this.preloadedBW) {
|
|
398
|
+
if (this.preloadWebContentsConfig.enableBW !== false && this.preloadedBW) {
|
|
294
399
|
preloadWin = this.preloadedBW;
|
|
295
|
-
this.preloadedBW =
|
|
296
|
-
|
|
400
|
+
this.preloadedBW = await this._createPreloadBW({
|
|
401
|
+
webPreferences: {
|
|
402
|
+
preload: bwOptions?.webPreferences?.preload || this.preload,
|
|
403
|
+
}
|
|
404
|
+
});
|
|
297
405
|
}
|
|
298
406
|
}
|
|
299
407
|
}
|
|
300
|
-
if (type === 'BV' && usePreload && this.
|
|
301
|
-
|
|
408
|
+
if (type === 'BV' && usePreload && this.preloadWebContentsConfig?.url) {
|
|
409
|
+
const bvOptions = browserWindowOptions || {};
|
|
410
|
+
if (this.preloadWebContentsConfig.enableBV !== false && this.preloadedBV) {
|
|
302
411
|
preloadWin = this.preloadedBV;
|
|
303
|
-
this.preloadedBV =
|
|
304
|
-
|
|
412
|
+
this.preloadedBV = await this._createPreloadBV({
|
|
413
|
+
webPreferences: {
|
|
414
|
+
preload: bvOptions?.webPreferences?.preload || this.preload,
|
|
415
|
+
}
|
|
416
|
+
});
|
|
305
417
|
}
|
|
306
418
|
}
|
|
307
419
|
if (preloadWin) {
|
|
308
420
|
const win = preloadWin;
|
|
309
421
|
log('log', `${name} 使用预加载窗口(${type})`, win._id);
|
|
310
|
-
win._type =
|
|
422
|
+
win._type = type;
|
|
311
423
|
win._name = options.name || 'anonymous';
|
|
312
424
|
win._extraData = `${options?.extraData || ''}`;
|
|
313
425
|
win._initUrl = `${options?.url || ''}`;
|
|
@@ -321,27 +433,101 @@ class WindowsManager {
|
|
|
321
433
|
if (type === 'BV') {
|
|
322
434
|
this._applyBrowserViewOptions(win, options);
|
|
323
435
|
}
|
|
436
|
+
if (typeof this.preloadWebContentsConfig?.customLoadURL === 'function') {
|
|
437
|
+
try {
|
|
438
|
+
if (type === 'BW') {
|
|
439
|
+
// @ts-ignore
|
|
440
|
+
const originLoadURL = win.loadURL;
|
|
441
|
+
// @ts-ignore
|
|
442
|
+
win.loadURL = async (url, useNativeLoadURL = false) => {
|
|
443
|
+
return new Promise(async (resolve, reject) => {
|
|
444
|
+
if (useNativeLoadURL) {
|
|
445
|
+
log('log', 'useNativeLoadURL win.loadURL');
|
|
446
|
+
try {
|
|
447
|
+
await originLoadURL.call(win, url);
|
|
448
|
+
resolve(undefined);
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
reject(error);
|
|
452
|
+
}
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
try {
|
|
456
|
+
log('log', 'customLoadURL win.loadURL');
|
|
457
|
+
// @ts-ignore
|
|
458
|
+
await this.preloadWebContentsConfig.customLoadURL(url || 'about:blank', (url) => originLoadURL.call(win, url), win.webContents);
|
|
459
|
+
try {
|
|
460
|
+
win.emit('ready-to-show');
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
log('error', 'emit ready-to-show event failed:', error);
|
|
464
|
+
}
|
|
465
|
+
resolve(undefined);
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
468
|
+
reject(error);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
const originWebContentsLoadURL = win.webContents.loadURL;
|
|
474
|
+
// @ts-ignore
|
|
475
|
+
win.webContents.loadURL = async (url, useNativeLoadURL = false) => {
|
|
476
|
+
return new Promise(async (resolve, reject) => {
|
|
477
|
+
if (useNativeLoadURL) {
|
|
478
|
+
log('log', 'useNativeLoadURL win.webContents.loadURL');
|
|
479
|
+
try {
|
|
480
|
+
await originWebContentsLoadURL.call(win.webContents, url);
|
|
481
|
+
resolve(undefined);
|
|
482
|
+
}
|
|
483
|
+
catch (error) {
|
|
484
|
+
reject(error);
|
|
485
|
+
}
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
log('log', 'customLoadURL win.webContents.loadURL');
|
|
490
|
+
// @ts-ignore
|
|
491
|
+
await this.preloadWebContentsConfig.customLoadURL(url || 'about:blank', (url) => originWebContentsLoadURL.call(win.webContents, url), win.webContents);
|
|
492
|
+
try {
|
|
493
|
+
win.webContents.emit('ready-to-show');
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
log('error', 'emit ready-to-show event failed:', error);
|
|
497
|
+
}
|
|
498
|
+
resolve(undefined);
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
reject(error);
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
log('error', 'customLoadURL error', error);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
324
510
|
window = win;
|
|
325
511
|
}
|
|
326
512
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
513
|
+
loadingView.url = `${loadingView?.url ?? this.loadingViewUrl}`;
|
|
514
|
+
lodash.merge(options, {
|
|
515
|
+
loadingView,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
log('error', 'loadingView error:', loadingView, this.loadingViewUrl);
|
|
520
|
+
}
|
|
521
|
+
try {
|
|
522
|
+
errorView.url = `${errorView?.url ?? this.errorViewUrl}`;
|
|
523
|
+
lodash.merge(options, {
|
|
524
|
+
errorView,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
catch (error) {
|
|
528
|
+
log('error', 'errorView error:', errorView, this.errorViewUrl);
|
|
529
|
+
}
|
|
530
|
+
try {
|
|
345
531
|
let parentWin = undefined;
|
|
346
532
|
if (typeof browserWindowOptions?.parent === 'number') {
|
|
347
533
|
parentWin = BrowserWindow.fromId(browserWindowOptions?.parent) || undefined;
|
|
@@ -359,6 +545,7 @@ class WindowsManager {
|
|
|
359
545
|
window = type === 'BV' ?
|
|
360
546
|
new BrowserView(lodash.merge((browserWindowOptions || {}), {
|
|
361
547
|
webPreferences: lodash.merge({
|
|
548
|
+
sandbox: false,
|
|
362
549
|
webviewTag: true,
|
|
363
550
|
// session: getCustomSession(),
|
|
364
551
|
plugins: true,
|
|
@@ -369,6 +556,7 @@ class WindowsManager {
|
|
|
369
556
|
nativeWindowOpen: true,
|
|
370
557
|
webSecurity: false,
|
|
371
558
|
preload: preload,
|
|
559
|
+
defaultEncoding: 'utf-8',
|
|
372
560
|
}, browserWindowOptions?.webPreferences || {})
|
|
373
561
|
}))
|
|
374
562
|
: new BrowserWindow(lodash.merge({
|
|
@@ -376,6 +564,7 @@ class WindowsManager {
|
|
|
376
564
|
}, (browserWindowOptions || {}), {
|
|
377
565
|
parent: parentWin,
|
|
378
566
|
webPreferences: lodash.merge({
|
|
567
|
+
sandbox: false,
|
|
379
568
|
webviewTag: true,
|
|
380
569
|
// session: getCustomSession(),
|
|
381
570
|
plugins: true,
|
|
@@ -385,6 +574,7 @@ class WindowsManager {
|
|
|
385
574
|
nativeWindowOpen: true,
|
|
386
575
|
webSecurity: false,
|
|
387
576
|
preload: preload,
|
|
577
|
+
defaultEncoding: 'utf-8',
|
|
388
578
|
}, browserWindowOptions?.webPreferences || {})
|
|
389
579
|
}));
|
|
390
580
|
log('log', `${name} 不使用 ${type === 'BV' ? 'preloadedBV' : 'preloadedBW'}`, window?.webContents?.id);
|
|
@@ -395,6 +585,8 @@ class WindowsManager {
|
|
|
395
585
|
log('error', 'enable: ', error);
|
|
396
586
|
}
|
|
397
587
|
}
|
|
588
|
+
// 停止加载
|
|
589
|
+
// window.webContents?.stop?.();
|
|
398
590
|
// @ts-ignore
|
|
399
591
|
try {
|
|
400
592
|
window.id = Number(`${window.id || window.webContents.id}`);
|
|
@@ -413,14 +605,16 @@ class WindowsManager {
|
|
|
413
605
|
window._name = name;
|
|
414
606
|
window._extraData = `${options?.extraData || ''}`;
|
|
415
607
|
window._initUrl = `${options?.url || ''}`;
|
|
608
|
+
// 设置zIndex层级
|
|
609
|
+
window._zIndex = options.zIndex ?? 0;
|
|
416
610
|
log('log', 'create 5: ', window.id, window._id, window._name);
|
|
417
|
-
if (loadingView?.url) {
|
|
611
|
+
if (loadingView?.url && loadingView?.url !== 'about:blank') {
|
|
418
612
|
if (type === 'BW') {
|
|
419
613
|
// @ts-ignore
|
|
420
614
|
this._setLoadingView(window, options);
|
|
421
615
|
}
|
|
422
616
|
}
|
|
423
|
-
if (errorView?.url) {
|
|
617
|
+
if (errorView?.url && errorView?.url !== 'about:blank') {
|
|
424
618
|
if (type === 'BW') {
|
|
425
619
|
const showErrorView = lodash.debounce(() => {
|
|
426
620
|
const _url = window._initUrl;
|
|
@@ -472,46 +666,45 @@ class WindowsManager {
|
|
|
472
666
|
}
|
|
473
667
|
window.webContents.on('did-attach-webview', (_event, webContents) => {
|
|
474
668
|
const tryEnable = () => {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
669
|
+
try {
|
|
670
|
+
const url = webContents.getURL();
|
|
671
|
+
if (!url || url === 'about:blank') {
|
|
672
|
+
return;
|
|
673
|
+
}
|
|
674
|
+
if (this.webviewDomainWhiteList && this.webviewDomainWhiteList.length > 0) {
|
|
675
|
+
try {
|
|
676
|
+
const { hostname } = new URL(url);
|
|
677
|
+
// 优化白名单判断,支持 .example.com 形式的子域名通配和本地/内网IP
|
|
678
|
+
const isWhiteListed = this.webviewDomainWhiteList.some(domain => {
|
|
679
|
+
if (domain === 'localhost' || domain === '127.0.0.1' || domain === '::1') {
|
|
680
|
+
return this._isLocalhost(hostname);
|
|
681
|
+
}
|
|
682
|
+
if (domain.startsWith('.')) {
|
|
683
|
+
// .example.com 允许所有 *.example.com
|
|
684
|
+
return hostname === domain.slice(1) || hostname.endsWith(domain);
|
|
685
|
+
}
|
|
686
|
+
else {
|
|
687
|
+
// 精确匹配
|
|
688
|
+
return hostname === domain;
|
|
689
|
+
}
|
|
690
|
+
}) || this._isLocalhost(hostname); // 允许本地回环和内网地址
|
|
691
|
+
if (isWhiteListed) {
|
|
692
|
+
enable(webContents);
|
|
496
693
|
}
|
|
497
694
|
else {
|
|
498
|
-
|
|
499
|
-
return hostname === domain;
|
|
695
|
+
log('log', 'webview 域名未在白名单,未启用 remote', url);
|
|
500
696
|
}
|
|
501
|
-
}) || isLocalhost(hostname); // 允许本地回环和内网地址
|
|
502
|
-
if (isWhiteListed) {
|
|
503
|
-
enable(webContents);
|
|
504
697
|
}
|
|
505
|
-
|
|
506
|
-
log('log', 'webview
|
|
698
|
+
catch {
|
|
699
|
+
log('log', 'webview url 解析失败,未启用 remote', url);
|
|
507
700
|
}
|
|
508
701
|
}
|
|
509
|
-
|
|
510
|
-
|
|
702
|
+
else {
|
|
703
|
+
enable(webContents); // 没有配置白名单则全部允许
|
|
511
704
|
}
|
|
512
705
|
}
|
|
513
|
-
|
|
514
|
-
|
|
706
|
+
catch (error) {
|
|
707
|
+
log('error', 'tryEnable webview error:', error);
|
|
515
708
|
}
|
|
516
709
|
};
|
|
517
710
|
// 只监听一次,防止多次触发
|
|
@@ -523,11 +716,14 @@ class WindowsManager {
|
|
|
523
716
|
webContents.on('did-navigate', onDidNavigate);
|
|
524
717
|
webContents.on('did-finish-load', onDidNavigate);
|
|
525
718
|
});
|
|
526
|
-
window.webContents.on('close', () => {
|
|
527
|
-
|
|
528
|
-
})
|
|
719
|
+
// window.webContents.on('close', () => {
|
|
720
|
+
// this.windows.delete(window.id || window._id)
|
|
721
|
+
// })
|
|
529
722
|
window.webContents.on('destroyed', () => {
|
|
530
|
-
|
|
723
|
+
const winId = window.id || window._id;
|
|
724
|
+
this.windows.delete(winId);
|
|
725
|
+
// 同步清理名称索引
|
|
726
|
+
this.windowsByName.delete(window._name);
|
|
531
727
|
});
|
|
532
728
|
window.webContents.on('dom-ready', () => {
|
|
533
729
|
if (openDevTools) {
|
|
@@ -542,22 +738,28 @@ class WindowsManager {
|
|
|
542
738
|
// @ts-ignore
|
|
543
739
|
window.on('closed', () => {
|
|
544
740
|
log('log', 'closed', window.id, window._name);
|
|
545
|
-
|
|
741
|
+
const winId = window.id || window._id;
|
|
742
|
+
this.windows.delete(winId);
|
|
743
|
+
// 同步清理名称索引
|
|
744
|
+
this.windowsByName.delete(window._name);
|
|
546
745
|
});
|
|
547
746
|
}
|
|
548
747
|
if (type === 'BV') {
|
|
549
748
|
parentWin?.addBrowserView(window);
|
|
550
749
|
log('log', 'create - addBrowserView');
|
|
551
750
|
}
|
|
552
|
-
|
|
751
|
+
const winId = window.id || window._id || window.webContents.id;
|
|
752
|
+
this.windows.set(winId, window);
|
|
753
|
+
// 同步更新名称索引
|
|
754
|
+
this.windowsByName.set(window._name, winId);
|
|
553
755
|
log('log', 'create', this.windows.keys());
|
|
554
756
|
// 初始化值
|
|
555
757
|
window.webContents.on('did-finish-load', () => {
|
|
556
|
-
|
|
758
|
+
log('log', 'did-finish-load', window.webContents.id);
|
|
557
759
|
initWebContentsVal(window, `${preload || ''}`);
|
|
558
760
|
});
|
|
559
761
|
window.webContents.on('did-start-loading', () => {
|
|
560
|
-
|
|
762
|
+
log('log', 'did-start-loading', window.webContents.id);
|
|
561
763
|
initWebContentsVal(window, `${preload || ''}`);
|
|
562
764
|
});
|
|
563
765
|
if (type === 'BW') {
|
|
@@ -568,7 +770,7 @@ class WindowsManager {
|
|
|
568
770
|
try {
|
|
569
771
|
window.dispatchEvent(new Event('focus'));
|
|
570
772
|
} catch (error) {
|
|
571
|
-
|
|
773
|
+
// 忽略错误,避免影响主流程
|
|
572
774
|
}
|
|
573
775
|
`);
|
|
574
776
|
}
|
|
@@ -582,7 +784,7 @@ class WindowsManager {
|
|
|
582
784
|
try {
|
|
583
785
|
window.dispatchEvent(new Event('blur'));
|
|
584
786
|
} catch (error) {
|
|
585
|
-
|
|
787
|
+
// 忽略错误,避免影响主流程
|
|
586
788
|
}
|
|
587
789
|
`);
|
|
588
790
|
}
|
|
@@ -618,39 +820,58 @@ class WindowsManager {
|
|
|
618
820
|
});
|
|
619
821
|
try {
|
|
620
822
|
const _addBrowserView = window.addBrowserView;
|
|
621
|
-
window.addBrowserView = (view) => {
|
|
823
|
+
window.addBrowserView = (view, isSort = false) => {
|
|
622
824
|
_addBrowserView.call(window, view);
|
|
623
825
|
handleBrowserViewFocus(view);
|
|
826
|
+
// 添加BrowserView后重新排序(如果未禁用自动排序)
|
|
827
|
+
log('log', 'addBrowserView-sort', isSort, window.getBrowserViews());
|
|
828
|
+
if (isSort) {
|
|
829
|
+
this.sortBrowserViewsDebounced(window, view);
|
|
830
|
+
}
|
|
624
831
|
};
|
|
625
832
|
const _removeBrowserView = window.removeBrowserView;
|
|
626
|
-
window.removeBrowserView = (view) => {
|
|
833
|
+
window.removeBrowserView = (view, isSort = false) => {
|
|
627
834
|
_removeBrowserView.call(window, view);
|
|
628
835
|
handleBrowserViewBlur(view);
|
|
836
|
+
// 移除BrowserView后重新排序(如果未禁用自动排序)
|
|
837
|
+
log('log', 'removeBrowserView-sort', isSort);
|
|
838
|
+
if (isSort) {
|
|
839
|
+
this.sortBrowserViewsDebounced(window, view);
|
|
840
|
+
}
|
|
629
841
|
};
|
|
630
842
|
const _setBrowserView = window.setBrowserView;
|
|
631
|
-
window.setBrowserView = (view) => {
|
|
843
|
+
window.setBrowserView = (view, isSort = false) => {
|
|
632
844
|
const views = window.getBrowserViews() || [];
|
|
633
|
-
for (const
|
|
634
|
-
handleBrowserViewBlur(
|
|
845
|
+
for (const existingView of views) {
|
|
846
|
+
handleBrowserViewBlur(existingView);
|
|
635
847
|
}
|
|
636
848
|
_setBrowserView.call(window, view);
|
|
637
849
|
handleBrowserViewFocus(view);
|
|
850
|
+
log('log', 'setBrowserView-sort', isSort);
|
|
851
|
+
if (isSort) {
|
|
852
|
+
this.sortBrowserViewsDebounced(window, view);
|
|
853
|
+
}
|
|
638
854
|
};
|
|
639
855
|
}
|
|
640
856
|
catch (error) {
|
|
641
857
|
log('error', 'focus', error);
|
|
642
858
|
}
|
|
643
859
|
}
|
|
644
|
-
console.log('message xxxx', options.url);
|
|
645
860
|
if (options.url) {
|
|
646
861
|
// @ts-ignore
|
|
647
862
|
window.loadURL ? window.loadURL(options.url) : window.webContents.loadURL(options.url);
|
|
648
|
-
|
|
649
|
-
|
|
863
|
+
if (options.browserWindow?.focusable !== false) {
|
|
864
|
+
window?.focus?.();
|
|
865
|
+
}
|
|
866
|
+
window?.webContents?.focus?.();
|
|
650
867
|
}
|
|
651
868
|
}
|
|
652
869
|
catch (error) {
|
|
653
870
|
log('error', 'create', error);
|
|
871
|
+
throw error;
|
|
872
|
+
}
|
|
873
|
+
if (!window) {
|
|
874
|
+
throw new Error(`Failed to create window: ${name}`);
|
|
654
875
|
}
|
|
655
876
|
return window;
|
|
656
877
|
}
|
|
@@ -659,6 +880,7 @@ class WindowsManager {
|
|
|
659
880
|
const { loadingView, preventOriginNavigate = false, } = createOptions;
|
|
660
881
|
let _loadingView = new BrowserView({
|
|
661
882
|
webPreferences: {
|
|
883
|
+
sandbox: false,
|
|
662
884
|
// session: getCustomSession(),
|
|
663
885
|
contextIsolation: false,
|
|
664
886
|
nodeIntegration: true,
|
|
@@ -671,7 +893,7 @@ class WindowsManager {
|
|
|
671
893
|
}
|
|
672
894
|
const loadLoadingView = () => {
|
|
673
895
|
const [viewWidth, viewHeight] = window.getSize();
|
|
674
|
-
window.
|
|
896
|
+
window.addBrowserView(_loadingView);
|
|
675
897
|
_loadingView.setBounds({
|
|
676
898
|
x: 0,
|
|
677
899
|
y: 0,
|
|
@@ -711,7 +933,7 @@ class WindowsManager {
|
|
|
711
933
|
return;
|
|
712
934
|
}
|
|
713
935
|
if (_loadingView.webContents && !_loadingView.webContents.isDestroyed()) {
|
|
714
|
-
window.
|
|
936
|
+
window.addBrowserView(_loadingView);
|
|
715
937
|
}
|
|
716
938
|
else {
|
|
717
939
|
// if loadingView has been destroyed
|
|
@@ -727,149 +949,215 @@ class WindowsManager {
|
|
|
727
949
|
window.webContents.on('did-stop-loading', onFailure);
|
|
728
950
|
}
|
|
729
951
|
}
|
|
952
|
+
/**
|
|
953
|
+
* 检查窗口是否已销毁
|
|
954
|
+
*/
|
|
955
|
+
_isWindowDestroyed(win) {
|
|
956
|
+
try {
|
|
957
|
+
return !win || !win.webContents || win.webContents.isDestroyed();
|
|
958
|
+
}
|
|
959
|
+
catch {
|
|
960
|
+
return true;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/**
|
|
964
|
+
* 判断是否本地/内网IP
|
|
965
|
+
*/
|
|
966
|
+
_isLocalhost(hostname) {
|
|
967
|
+
return (hostname === 'localhost' ||
|
|
968
|
+
hostname === '127.0.0.1' ||
|
|
969
|
+
hostname === '::1' ||
|
|
970
|
+
/^192\.168\./.test(hostname) ||
|
|
971
|
+
/^10\./.test(hostname) ||
|
|
972
|
+
/^172\.(1[6-9]|2[0-9]|3[0-1])\./.test(hostname));
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* 清理已销毁的窗口(延迟执行,避免频繁检查)
|
|
976
|
+
*/
|
|
977
|
+
_cleanupDestroyedWindows() {
|
|
978
|
+
const toDelete = [];
|
|
979
|
+
this.windows.forEach((win, key) => {
|
|
980
|
+
if (this._isWindowDestroyed(win)) {
|
|
981
|
+
toDelete.push(key);
|
|
982
|
+
// 同步清理名称索引
|
|
983
|
+
if (win?._name) {
|
|
984
|
+
this.windowsByName.delete(win._name);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
});
|
|
988
|
+
toDelete.forEach(key => this.windows.delete(key));
|
|
989
|
+
}
|
|
730
990
|
get(idOrName) {
|
|
731
991
|
log('log', 'get', idOrName);
|
|
732
992
|
let win;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
993
|
+
if (typeof idOrName === 'number') {
|
|
994
|
+
// 按 ID 查找(O(1))
|
|
995
|
+
win = this.windows.get(idOrName);
|
|
996
|
+
// 如果找不到,尝试按 _id 查找
|
|
997
|
+
if (!win) {
|
|
998
|
+
this.windows.forEach(w => {
|
|
999
|
+
if (w._id === idOrName) {
|
|
1000
|
+
win = w;
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
741
1003
|
}
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
1004
|
+
}
|
|
1005
|
+
else if (typeof idOrName === 'string') {
|
|
1006
|
+
// 按名称查找(O(1),使用索引)
|
|
1007
|
+
const winId = this.windowsByName.get(idOrName);
|
|
1008
|
+
if (winId !== undefined) {
|
|
1009
|
+
win = this.windows.get(winId);
|
|
746
1010
|
}
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
1011
|
+
// 如果索引中找不到,回退到遍历查找(兼容旧数据)
|
|
1012
|
+
if (!win) {
|
|
1013
|
+
this.windows.forEach(w => {
|
|
1014
|
+
if (w._name === idOrName) {
|
|
1015
|
+
win = w;
|
|
1016
|
+
// 更新索引
|
|
1017
|
+
this.windowsByName.set(idOrName, w.id || w._id);
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
751
1020
|
}
|
|
752
|
-
}
|
|
753
|
-
//
|
|
754
|
-
if (win &&
|
|
1021
|
+
}
|
|
1022
|
+
// 检查找到的窗口是否已销毁
|
|
1023
|
+
if (win && !this._isWindowDestroyed(win)) {
|
|
755
1024
|
return win;
|
|
756
1025
|
}
|
|
1026
|
+
// 窗口已销毁,触发清理
|
|
1027
|
+
if (win) {
|
|
1028
|
+
const winId = win.id || win._id;
|
|
1029
|
+
if (winId !== undefined) {
|
|
1030
|
+
this.windows.delete(winId);
|
|
1031
|
+
}
|
|
1032
|
+
if (win._name) {
|
|
1033
|
+
this.windowsByName.delete(win._name);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
// 延迟清理其他已销毁的窗口
|
|
1037
|
+
this.cleanupDestroyedWindowsDebounced();
|
|
757
1038
|
return undefined;
|
|
758
1039
|
}
|
|
759
1040
|
getAll(type) {
|
|
760
1041
|
log('log', 'getAll');
|
|
761
|
-
|
|
762
|
-
const
|
|
1042
|
+
// 先清理已销毁的窗口
|
|
1043
|
+
const toDelete = [];
|
|
763
1044
|
this.windows.forEach((win, key) => {
|
|
764
|
-
if (
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
}
|
|
770
|
-
if (win?._type === 'BV') {
|
|
771
|
-
bvWindows.set(key, win);
|
|
1045
|
+
if (this._isWindowDestroyed(win)) {
|
|
1046
|
+
toDelete.push(key);
|
|
1047
|
+
if (win?._name) {
|
|
1048
|
+
this.windowsByName.delete(win._name);
|
|
1049
|
+
}
|
|
772
1050
|
}
|
|
773
1051
|
});
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
if (type === 'BV') {
|
|
778
|
-
return bvWindows;
|
|
1052
|
+
toDelete.forEach(key => this.windows.delete(key));
|
|
1053
|
+
if (!type) {
|
|
1054
|
+
return this.windows;
|
|
779
1055
|
}
|
|
780
|
-
|
|
1056
|
+
const result = new Map();
|
|
1057
|
+
this.windows.forEach((win, key) => {
|
|
1058
|
+
if (!this._isWindowDestroyed(win) && win._type === type) {
|
|
1059
|
+
result.set(key, win);
|
|
1060
|
+
}
|
|
1061
|
+
});
|
|
1062
|
+
// 使用类型断言,TypeScript 会通过方法重载确保类型正确
|
|
1063
|
+
return result;
|
|
781
1064
|
}
|
|
782
1065
|
close(idOrName) {
|
|
783
1066
|
log('log', 'close', idOrName);
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
});
|
|
797
|
-
// @ts-ignore
|
|
798
|
-
win && this.windows.delete(win?.id);
|
|
799
|
-
// @ts-ignore
|
|
800
|
-
if (win) {
|
|
801
|
-
// @ts-ignore
|
|
1067
|
+
const win = this.get(idOrName);
|
|
1068
|
+
if (!win) {
|
|
1069
|
+
return false;
|
|
1070
|
+
}
|
|
1071
|
+
const winId = win.id || win._id;
|
|
1072
|
+
this.windows.delete(winId);
|
|
1073
|
+
if (win._name) {
|
|
1074
|
+
this.windowsByName.delete(win._name);
|
|
1075
|
+
}
|
|
1076
|
+
try {
|
|
802
1077
|
if (win._type === 'BV') {
|
|
1078
|
+
// 从所有 BW 窗口中移除该 BV
|
|
803
1079
|
this.windows.forEach(i => {
|
|
804
|
-
if (i?._type === 'BW') {
|
|
1080
|
+
if (i?._type === 'BW' && !this._isWindowDestroyed(i)) {
|
|
805
1081
|
const _win = i;
|
|
806
|
-
|
|
1082
|
+
try {
|
|
1083
|
+
_win.removeBrowserView(win);
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
// 忽略错误,可能已经移除
|
|
1087
|
+
}
|
|
807
1088
|
}
|
|
808
1089
|
});
|
|
809
1090
|
}
|
|
810
1091
|
// @ts-ignore
|
|
811
|
-
win
|
|
1092
|
+
win.webContents?.destroy?.();
|
|
812
1093
|
// @ts-ignore
|
|
813
|
-
win
|
|
1094
|
+
win.close?.();
|
|
814
1095
|
// @ts-ignore
|
|
815
|
-
win
|
|
1096
|
+
win.destroy?.();
|
|
1097
|
+
}
|
|
1098
|
+
catch (error) {
|
|
1099
|
+
log('error', 'close window error:', error);
|
|
816
1100
|
}
|
|
817
1101
|
return true;
|
|
818
1102
|
}
|
|
819
1103
|
closeAll() {
|
|
820
1104
|
log('log', 'closeAll');
|
|
821
|
-
this.windows.
|
|
1105
|
+
const windowsToClose = Array.from(this.windows.values());
|
|
1106
|
+
// 先清空 Map,避免在遍历过程中修改
|
|
1107
|
+
this.windows.clear();
|
|
1108
|
+
this.windowsByName.clear();
|
|
1109
|
+
// 收集所有 BW 窗口,用于批量移除 BV
|
|
1110
|
+
const bwWindows = windowsToClose.filter(w => w._type === 'BW' && !this._isWindowDestroyed(w));
|
|
1111
|
+
const bvWindows = windowsToClose.filter(w => w._type === 'BV');
|
|
1112
|
+
// 先从所有 BW 窗口中移除 BV
|
|
1113
|
+
bvWindows.forEach(bv => {
|
|
1114
|
+
bwWindows.forEach(bw => {
|
|
1115
|
+
try {
|
|
1116
|
+
bw.removeBrowserView(bv);
|
|
1117
|
+
}
|
|
1118
|
+
catch (error) {
|
|
1119
|
+
// 忽略错误,可能已经移除
|
|
1120
|
+
}
|
|
1121
|
+
});
|
|
1122
|
+
});
|
|
1123
|
+
// 然后销毁所有窗口
|
|
1124
|
+
windowsToClose.forEach((win) => {
|
|
822
1125
|
try {
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
if (win._type === 'BV') {
|
|
826
|
-
this.windows.forEach(i => {
|
|
827
|
-
if (i?._type === 'BW') {
|
|
828
|
-
const _win = i;
|
|
829
|
-
_win.removeBrowserView(win);
|
|
830
|
-
}
|
|
831
|
-
});
|
|
1126
|
+
if (this._isWindowDestroyed(win)) {
|
|
1127
|
+
return;
|
|
832
1128
|
}
|
|
833
1129
|
// @ts-ignore
|
|
834
|
-
win
|
|
1130
|
+
win.webContents?.destroy?.();
|
|
835
1131
|
// @ts-ignore
|
|
836
|
-
win
|
|
1132
|
+
win.close?.();
|
|
837
1133
|
// @ts-ignore
|
|
838
|
-
win
|
|
1134
|
+
win.destroy?.();
|
|
839
1135
|
}
|
|
840
1136
|
catch (error) {
|
|
1137
|
+
log('error', 'closeAll error:', error);
|
|
841
1138
|
}
|
|
842
1139
|
});
|
|
843
1140
|
}
|
|
844
1141
|
rename(idOrName, newName) {
|
|
845
1142
|
log('log', 'rename', idOrName, newName);
|
|
846
|
-
|
|
847
|
-
// 先查找目标窗口
|
|
848
|
-
if (typeof idOrName === 'number') {
|
|
849
|
-
win = this.get(idOrName);
|
|
850
|
-
}
|
|
851
|
-
else if (typeof idOrName === 'string') {
|
|
852
|
-
this.windows.forEach(i => {
|
|
853
|
-
if (i?._name === idOrName) {
|
|
854
|
-
win = i;
|
|
855
|
-
}
|
|
856
|
-
});
|
|
857
|
-
}
|
|
1143
|
+
const win = this.get(idOrName);
|
|
858
1144
|
if (!win) {
|
|
859
|
-
// 没有找到目标窗口
|
|
860
1145
|
return undefined;
|
|
861
1146
|
}
|
|
862
|
-
//
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
1147
|
+
// 检查新名字是否已存在(使用索引加速)
|
|
1148
|
+
const existingWinId = this.windowsByName.get(newName);
|
|
1149
|
+
if (existingWinId !== undefined) {
|
|
1150
|
+
const existingWin = this.windows.get(existingWinId);
|
|
1151
|
+
if (existingWin && existingWin !== win && !this._isWindowDestroyed(existingWin)) {
|
|
1152
|
+
// 新名字已被占用
|
|
1153
|
+
return undefined;
|
|
867
1154
|
}
|
|
868
|
-
});
|
|
869
|
-
if (nameExists) {
|
|
870
|
-
// 新名字已被占用
|
|
871
|
-
return undefined;
|
|
872
1155
|
}
|
|
1156
|
+
// 更新名称索引
|
|
1157
|
+
const oldName = win._name;
|
|
1158
|
+
const winId = win.id || win._id;
|
|
1159
|
+
this.windowsByName.delete(oldName);
|
|
1160
|
+
this.windowsByName.set(newName, winId);
|
|
873
1161
|
// 修改名字并同步 webContents
|
|
874
1162
|
win._name = newName;
|
|
875
1163
|
initWebContentsVal(win, `${this.preload || ''}`);
|
|
@@ -877,12 +1165,7 @@ class WindowsManager {
|
|
|
877
1165
|
}
|
|
878
1166
|
reInitUrl(idOrName, url) {
|
|
879
1167
|
log('log', 'reInitUrl', idOrName, url);
|
|
880
|
-
|
|
881
|
-
this.windows.forEach(i => {
|
|
882
|
-
if (i?._name === idOrName) {
|
|
883
|
-
win = i;
|
|
884
|
-
}
|
|
885
|
-
});
|
|
1168
|
+
const win = this.get(idOrName);
|
|
886
1169
|
if (!win) {
|
|
887
1170
|
return undefined;
|
|
888
1171
|
}
|
|
@@ -911,6 +1194,86 @@ class WindowsManager {
|
|
|
911
1194
|
if (typeof browserWindowOptions.alwaysOnTop === 'boolean') {
|
|
912
1195
|
win.setAlwaysOnTop(browserWindowOptions.alwaysOnTop);
|
|
913
1196
|
}
|
|
1197
|
+
// 设置背景颜色
|
|
1198
|
+
if (typeof browserWindowOptions.backgroundColor === 'string') {
|
|
1199
|
+
win.setBackgroundColor(browserWindowOptions.backgroundColor);
|
|
1200
|
+
}
|
|
1201
|
+
// 居中
|
|
1202
|
+
if (browserWindowOptions?.center !== false) {
|
|
1203
|
+
win.center();
|
|
1204
|
+
}
|
|
1205
|
+
// 设置窗口移动
|
|
1206
|
+
if (typeof browserWindowOptions.movable === 'boolean') {
|
|
1207
|
+
win.setMovable(browserWindowOptions.movable);
|
|
1208
|
+
}
|
|
1209
|
+
// 设置窗口大小调整
|
|
1210
|
+
if (typeof browserWindowOptions.resizable === 'boolean') {
|
|
1211
|
+
win.setResizable(browserWindowOptions.resizable);
|
|
1212
|
+
}
|
|
1213
|
+
// 设置全屏模式
|
|
1214
|
+
if (typeof browserWindowOptions.fullscreenable === 'boolean') {
|
|
1215
|
+
win.setFullScreenable(browserWindowOptions.fullscreenable);
|
|
1216
|
+
}
|
|
1217
|
+
// 设置窗口阴影
|
|
1218
|
+
if (typeof browserWindowOptions.hasShadow === 'boolean') {
|
|
1219
|
+
win.setHasShadow(browserWindowOptions.hasShadow);
|
|
1220
|
+
}
|
|
1221
|
+
// 设置窗口最小尺寸
|
|
1222
|
+
if (typeof browserWindowOptions.minWidth === 'number' && typeof browserWindowOptions.minHeight === 'number') {
|
|
1223
|
+
win.setMinimumSize(browserWindowOptions.minWidth, browserWindowOptions.minHeight);
|
|
1224
|
+
}
|
|
1225
|
+
// 设置窗口最大尺寸
|
|
1226
|
+
if (typeof browserWindowOptions.maxWidth === 'number' && typeof browserWindowOptions.maxHeight === 'number') {
|
|
1227
|
+
win.setMaximumSize(browserWindowOptions.maxWidth, browserWindowOptions.maxHeight);
|
|
1228
|
+
}
|
|
1229
|
+
// 设置窗口位置
|
|
1230
|
+
if (typeof browserWindowOptions.x === 'number' && typeof browserWindowOptions.y === 'number') {
|
|
1231
|
+
win.setPosition(browserWindowOptions.x, browserWindowOptions.y);
|
|
1232
|
+
}
|
|
1233
|
+
// 设置窗口标题
|
|
1234
|
+
if (typeof browserWindowOptions.title === 'string') {
|
|
1235
|
+
win.setTitle(browserWindowOptions.title);
|
|
1236
|
+
}
|
|
1237
|
+
// 设置窗口图标
|
|
1238
|
+
if (typeof browserWindowOptions.icon === 'string') {
|
|
1239
|
+
win.setIcon(browserWindowOptions.icon);
|
|
1240
|
+
}
|
|
1241
|
+
// 设置窗口菜单栏可见性
|
|
1242
|
+
if (typeof browserWindowOptions.autoHideMenuBar === 'boolean') {
|
|
1243
|
+
win.setAutoHideMenuBar(browserWindowOptions.autoHideMenuBar);
|
|
1244
|
+
}
|
|
1245
|
+
// 设置窗口最小化按钮
|
|
1246
|
+
if (browserWindowOptions.minimizable === false) {
|
|
1247
|
+
win.setMinimizable(false);
|
|
1248
|
+
}
|
|
1249
|
+
// 设置窗口最大化按钮
|
|
1250
|
+
if (browserWindowOptions.maximizable === false) {
|
|
1251
|
+
win.setMaximizable(false);
|
|
1252
|
+
}
|
|
1253
|
+
// 设置窗口关闭按钮
|
|
1254
|
+
if (browserWindowOptions.closable === false) {
|
|
1255
|
+
win.setClosable(false);
|
|
1256
|
+
}
|
|
1257
|
+
// 设置窗口焦点
|
|
1258
|
+
if (browserWindowOptions.focusable === false) {
|
|
1259
|
+
win.setFocusable(false);
|
|
1260
|
+
}
|
|
1261
|
+
// 设置窗口全屏
|
|
1262
|
+
if (browserWindowOptions.fullscreen === true) {
|
|
1263
|
+
win.setFullScreen(true);
|
|
1264
|
+
}
|
|
1265
|
+
// 设置窗口背景材质
|
|
1266
|
+
if (typeof browserWindowOptions.vibrancy === 'string') {
|
|
1267
|
+
win.setVibrancy(browserWindowOptions.vibrancy);
|
|
1268
|
+
}
|
|
1269
|
+
// 设置窗口透明度
|
|
1270
|
+
if (typeof browserWindowOptions.opacity === 'number') {
|
|
1271
|
+
win.setOpacity(browserWindowOptions.opacity);
|
|
1272
|
+
}
|
|
1273
|
+
// 设置窗口显示状态
|
|
1274
|
+
if (browserWindowOptions.show === false) {
|
|
1275
|
+
win.hide();
|
|
1276
|
+
}
|
|
914
1277
|
// 可继续扩展其他动态属性
|
|
915
1278
|
}
|
|
916
1279
|
_applyBrowserViewOptions(view, options) {
|
|
@@ -926,35 +1289,171 @@ class WindowsManager {
|
|
|
926
1289
|
if (typeof browserWindowOptions.width === 'number' && typeof browserWindowOptions.height === 'number') {
|
|
927
1290
|
view.setBounds({ x: 0, y: 0, width: browserWindowOptions.width, height: browserWindowOptions.height });
|
|
928
1291
|
}
|
|
1292
|
+
// 设置视图位置
|
|
1293
|
+
if (typeof browserWindowOptions.x === 'number' && typeof browserWindowOptions.y === 'number') {
|
|
1294
|
+
const bounds = view.getBounds();
|
|
1295
|
+
view.setBounds({
|
|
1296
|
+
x: browserWindowOptions.x,
|
|
1297
|
+
y: browserWindowOptions.y,
|
|
1298
|
+
width: bounds.width,
|
|
1299
|
+
height: bounds.height
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1302
|
+
// 设置视图背景颜色
|
|
1303
|
+
if (typeof browserWindowOptions.backgroundColor === 'string') {
|
|
1304
|
+
view.setBackgroundColor(browserWindowOptions.backgroundColor);
|
|
1305
|
+
}
|
|
929
1306
|
// 可继续扩展其他动态属性
|
|
930
1307
|
}
|
|
931
1308
|
// 生成一个bv 做为预加载资源窗口,加载完成后销毁
|
|
932
1309
|
async createPreloadWebContents(url) {
|
|
933
|
-
|
|
934
|
-
|
|
1310
|
+
let bv = null;
|
|
1311
|
+
try {
|
|
1312
|
+
bv = await this.create({
|
|
935
1313
|
type: 'BV',
|
|
936
1314
|
url,
|
|
937
1315
|
name: `preload-web-contents-${md5(url)}`,
|
|
938
1316
|
extraData: `${url}`
|
|
939
1317
|
});
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1318
|
+
return new Promise((resolve, reject) => {
|
|
1319
|
+
const winId = bv.id || bv._id;
|
|
1320
|
+
if (!winId) {
|
|
1321
|
+
reject(new Error('Failed to get window ID'));
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
bv.webContents.on('did-finish-load', () => {
|
|
1325
|
+
if (winId) {
|
|
1326
|
+
this.close(winId);
|
|
1327
|
+
}
|
|
1328
|
+
resolve(true);
|
|
1329
|
+
bv = null;
|
|
1330
|
+
});
|
|
1331
|
+
bv.webContents.on('did-fail-load', () => {
|
|
1332
|
+
if (winId) {
|
|
1333
|
+
this.close(winId);
|
|
1334
|
+
}
|
|
1335
|
+
reject(new Error('Failed to load web contents'));
|
|
1336
|
+
bv = null;
|
|
1337
|
+
});
|
|
1338
|
+
bv.webContents.loadURL(url);
|
|
944
1339
|
});
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1340
|
+
}
|
|
1341
|
+
catch (error) {
|
|
1342
|
+
if (bv) {
|
|
1343
|
+
const winId = bv.id || bv._id;
|
|
1344
|
+
if (winId) {
|
|
1345
|
+
this.close(winId);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
throw error;
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
async getWindowForWebContentsId(wcId) {
|
|
1352
|
+
const wc = webContents.fromId(wcId);
|
|
1353
|
+
if (!wc)
|
|
1354
|
+
return undefined;
|
|
1355
|
+
// Case 1: BrowserView
|
|
1356
|
+
for (const win of BrowserWindow.getAllWindows()) {
|
|
1357
|
+
for (const view of win.getBrowserViews()) {
|
|
1358
|
+
if (view.webContents.id === wcId) {
|
|
1359
|
+
return win;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
// Case 2: WebView
|
|
1364
|
+
// webview 有 hostWebContents,指向它所在的 BrowserWindow 的 webContents
|
|
1365
|
+
if (wc.hostWebContents) {
|
|
1366
|
+
return BrowserWindow.fromWebContents(wc.hostWebContents);
|
|
1367
|
+
}
|
|
1368
|
+
// Case 3: 普通 window 本身
|
|
1369
|
+
const win = BrowserWindow.fromWebContents(wc);
|
|
1370
|
+
if (win)
|
|
1371
|
+
return win;
|
|
1372
|
+
return undefined;
|
|
1373
|
+
}
|
|
1374
|
+
/**
|
|
1375
|
+
* 手动对BrowserView进行排序
|
|
1376
|
+
* @param windowId 窗口ID或名称
|
|
1377
|
+
*/
|
|
1378
|
+
sortBrowserViews(windowId) {
|
|
1379
|
+
const window = this.get(windowId);
|
|
1380
|
+
if (window && window._type === 'BW') {
|
|
1381
|
+
this._sortBrowserViews(window);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* 对BrowserView进行排序
|
|
1386
|
+
* @param window 目标窗口
|
|
1387
|
+
*/
|
|
1388
|
+
_sortBrowserViews(window, addView) {
|
|
1389
|
+
try {
|
|
1390
|
+
if (window.isDestroyed()) {
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
const views = window.getBrowserViews() || [];
|
|
1394
|
+
if (views.length <= 1)
|
|
1395
|
+
return;
|
|
1396
|
+
log('log', 'sortBrowserViews', views?.map(i => i.webContents.id));
|
|
1397
|
+
// 创建排序后的视图数组(不修改原数组)
|
|
1398
|
+
const sortedViews = [...views].sort((a, b) => {
|
|
1399
|
+
const zIndexA = a._zIndex ?? 0;
|
|
1400
|
+
const zIndexB = b._zIndex ?? 0;
|
|
1401
|
+
return zIndexA - zIndexB;
|
|
949
1402
|
});
|
|
950
|
-
|
|
951
|
-
|
|
1403
|
+
// 检查是否已经按正确顺序排列(优化:提前退出)
|
|
1404
|
+
let needsReorder = false;
|
|
1405
|
+
for (let i = 0; i < views.length; i++) {
|
|
1406
|
+
const view = views[i];
|
|
1407
|
+
const sortedView = sortedViews[i];
|
|
1408
|
+
if (!view || !sortedView || view.webContents.id !== sortedView.webContents.id) {
|
|
1409
|
+
needsReorder = true;
|
|
1410
|
+
break;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
log('log', 'sortBrowserViews needsReorder', needsReorder, sortedViews?.map(i => i.webContents.id));
|
|
1414
|
+
// 如果已经按正确顺序排列,则不需要重新排序
|
|
1415
|
+
if (!needsReorder)
|
|
1416
|
+
return;
|
|
1417
|
+
// 移除所有BrowserView(排除刚添加的视图,避免不必要的操作)
|
|
1418
|
+
views.forEach(view => {
|
|
1419
|
+
if (addView?.webContents?.id !== view.webContents.id) {
|
|
1420
|
+
try {
|
|
1421
|
+
// @ts-ignore
|
|
1422
|
+
window.removeBrowserView(view, false);
|
|
1423
|
+
}
|
|
1424
|
+
catch (error) {
|
|
1425
|
+
// 忽略错误,视图可能已经被移除
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
});
|
|
1429
|
+
// 按正确顺序重新添加
|
|
1430
|
+
sortedViews.forEach((view, index) => {
|
|
1431
|
+
try {
|
|
1432
|
+
if (index === 0) {
|
|
1433
|
+
// 第一个设置为当前视图
|
|
1434
|
+
// @ts-ignore
|
|
1435
|
+
window.setBrowserView(view, false);
|
|
1436
|
+
}
|
|
1437
|
+
else {
|
|
1438
|
+
// 其他视图添加到后面
|
|
1439
|
+
// @ts-ignore
|
|
1440
|
+
window.addBrowserView(view, false);
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
catch (error) {
|
|
1444
|
+
log('error', 'addBrowserView in sortBrowserViews error:', error);
|
|
1445
|
+
}
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
catch (error) {
|
|
1449
|
+
log('error', 'sortBrowserViews error:', error);
|
|
1450
|
+
}
|
|
952
1451
|
}
|
|
953
1452
|
}
|
|
954
1453
|
// @ts-ignore
|
|
955
1454
|
global['__ELECTRON_WINDOWS_MANAGER__'] = undefined;
|
|
956
1455
|
let isInitialized = false;
|
|
957
|
-
const initialize = (preload, loadingViewUrl, errorViewUrl,
|
|
1456
|
+
const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList) => {
|
|
958
1457
|
// @ts-ignore
|
|
959
1458
|
if (isInitialized && global['__ELECTRON_WINDOWS_MANAGER__']) {
|
|
960
1459
|
// @ts-ignore
|
|
@@ -962,7 +1461,7 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
962
1461
|
}
|
|
963
1462
|
isInitialized = true;
|
|
964
1463
|
// @ts-ignore
|
|
965
|
-
const wm = global['__ELECTRON_WINDOWS_MANAGER__'] = new WindowsManager(preload, loadingViewUrl, errorViewUrl,
|
|
1464
|
+
const wm = global['__ELECTRON_WINDOWS_MANAGER__'] = new WindowsManager(preload, loadingViewUrl, errorViewUrl, preloadWebContentsConfig, webviewDomainWhiteList);
|
|
966
1465
|
eIpc.mainIPC.handleRenderer('__ELECTRON_WINDOW_MANAGER_IPC_CHANNEL__', async (data) => {
|
|
967
1466
|
if (data?.type === 'create') {
|
|
968
1467
|
const opt = data;
|
|
@@ -978,12 +1477,29 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
978
1477
|
findWin.webContents.reload();
|
|
979
1478
|
}, 100);
|
|
980
1479
|
}
|
|
1480
|
+
if (opt.data.browserWindow?.parent) {
|
|
1481
|
+
try {
|
|
1482
|
+
if (findWin._type === 'BW') {
|
|
1483
|
+
findWin.setParentWindow(BrowserWindow.fromId(Number(opt.data.browserWindow.parent)));
|
|
1484
|
+
}
|
|
1485
|
+
if (findWin._type === 'BV') {
|
|
1486
|
+
BrowserWindow.fromId(Number(opt.data.browserWindow.parent))?.addBrowserView(findWin);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
catch (error) {
|
|
1490
|
+
log('error', 'setParentWindow error:', error);
|
|
1491
|
+
}
|
|
1492
|
+
}
|
|
1493
|
+
if (findWin?._type === 'BV' && opt.data.zIndex) {
|
|
1494
|
+
findWin._zIndex = opt.data.zIndex;
|
|
1495
|
+
}
|
|
981
1496
|
return {
|
|
982
1497
|
winId: Number(`${findWin?.id || findWin?._id || -1}`),
|
|
983
1498
|
winName: `${findWin?._name || ''}`,
|
|
984
1499
|
winType: `${findWin?._type || ''}`,
|
|
985
1500
|
winExtraData: `${findWin?._extraData || ''}`,
|
|
986
1501
|
winInitUrl: `${findWin?._initUrl || ''}`,
|
|
1502
|
+
winZIndex: `${findWin._zIndex || 0}`,
|
|
987
1503
|
};
|
|
988
1504
|
}
|
|
989
1505
|
const res = await wm.create(opt.data);
|
|
@@ -993,6 +1509,7 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
993
1509
|
winType: `${res?._type || ''}`,
|
|
994
1510
|
winExtraData: `${res?._extraData || ''}`,
|
|
995
1511
|
winInitUrl: `${res?._initUrl || ''}`,
|
|
1512
|
+
winZIndex: `${res?._zIndex || 0}`,
|
|
996
1513
|
};
|
|
997
1514
|
}
|
|
998
1515
|
if (data?.type === 'get') {
|
|
@@ -1004,20 +1521,24 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
1004
1521
|
winType: `${res?._type || ''}`,
|
|
1005
1522
|
winExtraData: `${res?._extraData || ''}`,
|
|
1006
1523
|
winInitUrl: `${res?._initUrl || ''}`,
|
|
1524
|
+
winZIndex: `${res?._zIndex || 0}`,
|
|
1007
1525
|
};
|
|
1008
1526
|
}
|
|
1009
1527
|
if (data?.type === 'getAll') {
|
|
1010
1528
|
const res = wm.getAll();
|
|
1011
1529
|
const obj = {};
|
|
1012
1530
|
res.forEach(i => {
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
winId
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1531
|
+
const winId = i.id || i._id;
|
|
1532
|
+
if (winId !== undefined) {
|
|
1533
|
+
obj[winId] = {
|
|
1534
|
+
winId: Number(`${winId}`),
|
|
1535
|
+
winName: `${i?._name || ''}`,
|
|
1536
|
+
winType: `${i?._type || ''}`,
|
|
1537
|
+
winExtraData: `${i?._extraData || ''}`,
|
|
1538
|
+
winInitUrl: `${i?._initUrl || ''}`,
|
|
1539
|
+
winZIndex: `${i?._zIndex || 0}`,
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1021
1542
|
});
|
|
1022
1543
|
return obj;
|
|
1023
1544
|
}
|
|
@@ -1040,6 +1561,7 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
1040
1561
|
winType: `${res?._type || ''}`,
|
|
1041
1562
|
winExtraData: `${res?._extraData || ''}`,
|
|
1042
1563
|
winInitUrl: `${res?._initUrl || ''}`,
|
|
1564
|
+
winZIndex: `${res?._zIndex || 0}`,
|
|
1043
1565
|
};
|
|
1044
1566
|
}
|
|
1045
1567
|
return undefined;
|
|
@@ -1054,34 +1576,16 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
1054
1576
|
winType: `${res?._type || ''}`,
|
|
1055
1577
|
winExtraData: `${res?._extraData || ''}`,
|
|
1056
1578
|
winInitUrl: `${res?._initUrl || ''}`,
|
|
1579
|
+
winZIndex: `${res?._zIndex || 0}`,
|
|
1057
1580
|
};
|
|
1058
1581
|
}
|
|
1059
1582
|
return undefined;
|
|
1060
1583
|
}
|
|
1061
|
-
if (data?.type === '
|
|
1584
|
+
if (data?.type === 'getWindowForWebContentsId') {
|
|
1062
1585
|
const opt = data;
|
|
1063
|
-
const
|
|
1064
|
-
if (
|
|
1065
|
-
|
|
1066
|
-
if (!win) {
|
|
1067
|
-
// 获取所有的 BrowserWindows
|
|
1068
|
-
let allWindows = BrowserWindow.getAllWindows();
|
|
1069
|
-
// 遍历所有窗口,检查每个窗口的 BrowserView
|
|
1070
|
-
for (let _win of allWindows) {
|
|
1071
|
-
let views = _win.getBrowserViews();
|
|
1072
|
-
// 遍历窗口的所有 BrowserView
|
|
1073
|
-
for (let view of views) {
|
|
1074
|
-
if (view.webContents === targetWebContents) {
|
|
1075
|
-
win = _win;
|
|
1076
|
-
break;
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1079
|
-
if (win)
|
|
1080
|
-
break;
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
// @ts-ignore
|
|
1084
|
-
return win?.id || win?._id;
|
|
1586
|
+
const res = await wm.getWindowForWebContentsId(opt.data);
|
|
1587
|
+
if (res) {
|
|
1588
|
+
return res?.id;
|
|
1085
1589
|
}
|
|
1086
1590
|
return undefined;
|
|
1087
1591
|
}
|
|
@@ -1122,15 +1626,20 @@ const initialize = (preload, loadingViewUrl, errorViewUrl, preloadWebContentsUrl
|
|
|
1122
1626
|
return undefined;
|
|
1123
1627
|
}
|
|
1124
1628
|
// 是否开启预加载窗口
|
|
1125
|
-
if (data?.type === '
|
|
1629
|
+
if (data?.type === 'setPreloadWebContentsConfig') {
|
|
1126
1630
|
const opt = data;
|
|
1127
|
-
wm.
|
|
1631
|
+
wm.setPreloadWebContentsConfig(opt.data);
|
|
1128
1632
|
}
|
|
1129
1633
|
if (data?.type === 'createPreloadWebContents') {
|
|
1130
1634
|
const opt = data;
|
|
1131
1635
|
const res = await wm.createPreloadWebContents(opt.data);
|
|
1132
1636
|
return res;
|
|
1133
1637
|
}
|
|
1638
|
+
if (data?.type === 'sortBrowserViews') {
|
|
1639
|
+
const opt = data;
|
|
1640
|
+
wm.sortBrowserViews(opt.data);
|
|
1641
|
+
return true;
|
|
1642
|
+
}
|
|
1134
1643
|
return undefined;
|
|
1135
1644
|
});
|
|
1136
1645
|
return wm;
|