@theia/core 1.37.0-next.9 → 1.37.1

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.
Files changed (175) hide show
  1. package/README.md +7 -9
  2. package/i18n/nls.cs.json +6 -1
  3. package/i18n/nls.de.json +6 -1
  4. package/i18n/nls.es.json +6 -1
  5. package/i18n/nls.fr.json +6 -1
  6. package/i18n/nls.hu.json +6 -1
  7. package/i18n/nls.it.json +6 -1
  8. package/i18n/nls.ja.json +6 -1
  9. package/i18n/nls.json +6 -1
  10. package/i18n/nls.pl.json +6 -1
  11. package/i18n/nls.pt-br.json +6 -1
  12. package/i18n/nls.pt-pt.json +6 -1
  13. package/i18n/nls.ru.json +6 -1
  14. package/i18n/nls.zh-cn.json +6 -1
  15. package/lib/browser/core-preferences.d.ts +4 -0
  16. package/lib/browser/core-preferences.d.ts.map +1 -1
  17. package/lib/browser/core-preferences.js +22 -0
  18. package/lib/browser/core-preferences.js.map +1 -1
  19. package/lib/browser/hover-service.d.ts +5 -0
  20. package/lib/browser/hover-service.d.ts.map +1 -1
  21. package/lib/browser/hover-service.js +7 -1
  22. package/lib/browser/hover-service.js.map +1 -1
  23. package/lib/browser/icon-theme-service.js +1 -1
  24. package/lib/browser/icon-theme-service.js.map +1 -1
  25. package/lib/browser/shell/application-shell.d.ts +2 -1
  26. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  27. package/lib/browser/shell/application-shell.js +30 -3
  28. package/lib/browser/shell/application-shell.js.map +1 -1
  29. package/lib/browser/shell/tab-bars.d.ts +21 -5
  30. package/lib/browser/shell/tab-bars.d.ts.map +1 -1
  31. package/lib/browser/shell/tab-bars.js +94 -17
  32. package/lib/browser/shell/tab-bars.js.map +1 -1
  33. package/lib/browser/test/jsdom.js +1 -1
  34. package/lib/browser/test/jsdom.js.map +1 -1
  35. package/lib/browser/tree/tree-iterator.js +4 -4
  36. package/lib/browser/tree/tree-iterator.js.map +1 -1
  37. package/lib/browser/tree/tree-selection-state.spec.js +26 -2
  38. package/lib/browser/tree/tree-selection-state.spec.js.map +1 -1
  39. package/lib/common/i18n/localization.d.ts +1 -0
  40. package/lib/common/i18n/localization.d.ts.map +1 -1
  41. package/lib/common/i18n/localization.js +2 -16
  42. package/lib/common/i18n/localization.js.map +1 -1
  43. package/lib/common/nls.d.ts +1 -0
  44. package/lib/common/nls.d.ts.map +1 -1
  45. package/lib/common/nls.js +2 -1
  46. package/lib/common/nls.js.map +1 -1
  47. package/lib/common/quick-pick-service.d.ts +2 -1
  48. package/lib/common/quick-pick-service.d.ts.map +1 -1
  49. package/lib/common/quick-pick-service.js.map +1 -1
  50. package/lib/electron-browser/electron-clipboard-service.d.ts.map +1 -1
  51. package/lib/electron-browser/electron-clipboard-service.js +2 -3
  52. package/lib/electron-browser/electron-clipboard-service.js.map +1 -1
  53. package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.d.ts.map +1 -1
  54. package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.js +1 -2
  55. package/lib/electron-browser/keyboard/electron-keyboard-layout-change-notifier.js.map +1 -1
  56. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts +2 -3
  57. package/lib/electron-browser/menu/electron-context-menu-renderer.d.ts.map +1 -1
  58. package/lib/electron-browser/menu/electron-context-menu-renderer.js +10 -18
  59. package/lib/electron-browser/menu/electron-context-menu-renderer.js.map +1 -1
  60. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts +10 -8
  61. package/lib/electron-browser/menu/electron-main-menu-factory.d.ts.map +1 -1
  62. package/lib/electron-browser/menu/electron-main-menu-factory.js +33 -32
  63. package/lib/electron-browser/menu/electron-main-menu-factory.js.map +1 -1
  64. package/lib/electron-browser/menu/electron-menu-contribution.d.ts +6 -6
  65. package/lib/electron-browser/menu/electron-menu-contribution.d.ts.map +1 -1
  66. package/lib/electron-browser/menu/electron-menu-contribution.js +39 -54
  67. package/lib/electron-browser/menu/electron-menu-contribution.js.map +1 -1
  68. package/lib/electron-browser/messaging/electron-ipc-connection-provider.d.ts.map +1 -1
  69. package/lib/electron-browser/messaging/electron-ipc-connection-provider.js +2 -6
  70. package/lib/electron-browser/messaging/electron-ipc-connection-provider.js.map +1 -1
  71. package/lib/electron-browser/preload.d.ts +2 -0
  72. package/lib/electron-browser/preload.d.ts.map +1 -0
  73. package/lib/electron-browser/preload.js +183 -0
  74. package/lib/electron-browser/preload.js.map +1 -0
  75. package/lib/electron-browser/token/electron-token-frontend-module.d.ts.map +1 -1
  76. package/lib/electron-browser/token/electron-token-frontend-module.js +1 -2
  77. package/lib/electron-browser/token/electron-token-frontend-module.js.map +1 -1
  78. package/lib/electron-browser/window/electron-frontend-application-state.d.ts.map +1 -1
  79. package/lib/electron-browser/window/electron-frontend-application-state.js +1 -3
  80. package/lib/electron-browser/window/electron-frontend-application-state.js.map +1 -1
  81. package/lib/electron-browser/window/electron-secondary-window-service.d.ts +1 -3
  82. package/lib/electron-browser/window/electron-secondary-window-service.d.ts.map +1 -1
  83. package/lib/electron-browser/window/electron-secondary-window-service.js +6 -34
  84. package/lib/electron-browser/window/electron-secondary-window-service.js.map +1 -1
  85. package/lib/electron-browser/window/electron-window-service.d.ts +1 -8
  86. package/lib/electron-browser/window/electron-window-service.d.ts.map +1 -1
  87. package/lib/electron-browser/window/electron-window-service.js +8 -25
  88. package/lib/electron-browser/window/electron-window-service.js.map +1 -1
  89. package/lib/electron-common/electron-api.d.ts +94 -0
  90. package/lib/electron-common/electron-api.d.ts.map +1 -0
  91. package/lib/electron-common/electron-api.js +53 -0
  92. package/lib/electron-common/electron-api.js.map +1 -0
  93. package/lib/electron-common/messaging/electron-connection-handler.d.ts +0 -7
  94. package/lib/electron-common/messaging/electron-connection-handler.d.ts.map +1 -1
  95. package/lib/electron-common/messaging/electron-connection-handler.js +1 -5
  96. package/lib/electron-common/messaging/electron-connection-handler.js.map +1 -1
  97. package/lib/electron-main/electron-api-main.d.ts +21 -0
  98. package/lib/electron-main/electron-api-main.d.ts.map +1 -0
  99. package/lib/electron-main/electron-api-main.js +261 -0
  100. package/lib/electron-main/electron-api-main.js.map +1 -0
  101. package/lib/electron-main/electron-main-application-module.d.ts.map +1 -1
  102. package/lib/electron-main/electron-main-application-module.js +3 -3
  103. package/lib/electron-main/electron-main-application-module.js.map +1 -1
  104. package/lib/electron-main/electron-main-application.d.ts +8 -2
  105. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  106. package/lib/electron-main/electron-main-application.js +32 -29
  107. package/lib/electron-main/electron-main-application.js.map +1 -1
  108. package/lib/electron-main/electron-security-token-service.d.ts.map +1 -1
  109. package/lib/electron-main/electron-security-token-service.js +2 -1
  110. package/lib/electron-main/electron-security-token-service.js.map +1 -1
  111. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts +4 -5
  112. package/lib/electron-main/messaging/electron-messaging-contribution.d.ts.map +1 -1
  113. package/lib/electron-main/messaging/electron-messaging-contribution.js +4 -7
  114. package/lib/electron-main/messaging/electron-messaging-contribution.js.map +1 -1
  115. package/lib/electron-main/theia-electron-window.d.ts +2 -4
  116. package/lib/electron-main/theia-electron-window.d.ts.map +1 -1
  117. package/lib/electron-main/theia-electron-window.js +11 -34
  118. package/lib/electron-main/theia-electron-window.js.map +1 -1
  119. package/lib/node/i18n/localization-backend-contribution.d.ts.map +1 -1
  120. package/lib/node/i18n/localization-backend-contribution.js +2 -1
  121. package/lib/node/i18n/localization-backend-contribution.js.map +1 -1
  122. package/lib/node/i18n/localization-provider.d.ts.map +1 -1
  123. package/lib/node/i18n/localization-provider.js +2 -1
  124. package/lib/node/i18n/localization-provider.js.map +1 -1
  125. package/package.json +9 -6
  126. package/src/browser/core-preferences.ts +26 -0
  127. package/src/browser/hover-service.ts +12 -1
  128. package/src/browser/icon-theme-service.ts +1 -1
  129. package/src/browser/shell/application-shell.ts +29 -1
  130. package/src/browser/shell/tab-bars.ts +111 -17
  131. package/src/browser/style/hover-service.css +4 -0
  132. package/src/browser/style/tabs.css +25 -0
  133. package/src/browser/style/tree.css +1 -0
  134. package/src/browser/test/jsdom.ts +1 -1
  135. package/src/browser/tree/tree-iterator.ts +4 -4
  136. package/src/browser/tree/tree-selection-state.spec.ts +29 -2
  137. package/src/common/i18n/localization.ts +6 -16
  138. package/src/common/nls.ts +3 -1
  139. package/src/common/quick-pick-service.ts +2 -1
  140. package/src/electron-browser/electron-clipboard-service.ts +2 -3
  141. package/src/electron-browser/keyboard/electron-keyboard-layout-change-notifier.ts +1 -2
  142. package/src/electron-browser/menu/electron-context-menu-renderer.ts +10 -17
  143. package/src/electron-browser/menu/electron-main-menu-factory.ts +51 -44
  144. package/src/electron-browser/menu/electron-menu-contribution.ts +46 -57
  145. package/src/electron-browser/messaging/electron-ipc-connection-provider.ts +4 -9
  146. package/src/electron-browser/preload.ts +208 -0
  147. package/src/electron-browser/token/electron-token-frontend-module.ts +1 -2
  148. package/src/electron-browser/window/electron-frontend-application-state.ts +1 -3
  149. package/src/electron-browser/window/electron-secondary-window-service.ts +7 -31
  150. package/src/electron-browser/window/electron-window-service.ts +8 -25
  151. package/src/electron-common/electron-api.ts +134 -0
  152. package/src/electron-common/messaging/electron-connection-handler.ts +0 -9
  153. package/src/electron-main/electron-api-main.ts +291 -0
  154. package/src/electron-main/electron-main-application-module.ts +3 -4
  155. package/src/electron-main/electron-main-application.ts +33 -37
  156. package/src/electron-main/electron-security-token-service.ts +2 -1
  157. package/src/electron-main/messaging/electron-messaging-contribution.ts +8 -10
  158. package/src/electron-main/theia-electron-window.ts +8 -33
  159. package/src/node/i18n/localization-backend-contribution.ts +2 -1
  160. package/src/node/i18n/localization-provider.ts +2 -1
  161. package/LICENSE +0 -642
  162. package/electron-shared/@electron/remote/index.d.ts +0 -1
  163. package/electron-shared/@electron/remote/index.js +0 -1
  164. package/electron-shared/@electron/remote/main/index.d.ts +0 -1
  165. package/electron-shared/@electron/remote/main/index.js +0 -1
  166. package/lib/electron-common/messaging/electron-messages.d.ts +0 -25
  167. package/lib/electron-common/messaging/electron-messages.d.ts.map +0 -1
  168. package/lib/electron-common/messaging/electron-messages.js +0 -37
  169. package/lib/electron-common/messaging/electron-messages.js.map +0 -1
  170. package/lib/electron-main/electron-native-keymap.d.ts +0 -8
  171. package/lib/electron-main/electron-native-keymap.d.ts.map +0 -1
  172. package/lib/electron-main/electron-native-keymap.js +0 -48
  173. package/lib/electron-main/electron-native-keymap.js.map +0 -1
  174. package/src/electron-common/messaging/electron-messages.ts +0 -42
  175. package/src/electron-main/electron-native-keymap.ts +0 -40
@@ -0,0 +1,291 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2023 STMicroelectronics and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import {
18
+ ipcMain, BrowserWindow, Menu, MenuItemConstructorOptions, webContents, WebContents, session, shell, clipboard, IpcMainEvent
19
+ } from '@theia/electron/shared/electron';
20
+ import * as nativeKeymap from '@theia/electron/shared/native-keymap';
21
+
22
+ import { inject, injectable } from 'inversify';
23
+ import { FrontendApplicationState, StopReason } from '../common/frontend-application-state';
24
+ import { ElectronSecurityToken } from '../electron-common/electron-token';
25
+ import {
26
+ CHANNEL_GET_SECURITY_TOKEN, CHANNEL_SET_MENU, MenuDto, CHANNEL_INVOKE_MENU, CHANNEL_FOCUS_WINDOW,
27
+ CHANNEL_ATTACH_SECURITY_TOKEN, CHANNEL_OPEN_POPUP, CHANNEL_ON_CLOSE_POPUP, CHANNEL_CLOSE_POPUP,
28
+ CHANNEL_GET_TITLE_STYLE_AT_STARTUP,
29
+ CHANNEL_MINIMIZE,
30
+ CHANNEL_MAXIMIZE,
31
+ CHANNEL_UNMAXIMIZE,
32
+ CHANNEL_CLOSE,
33
+ CHANNEL_ON_WINDOW_EVENT,
34
+ WindowEvent,
35
+ CHANNEL_TOGGLE_DEVTOOLS,
36
+ CHANNEL_SET_ZOOM_LEVEL,
37
+ CHANNEL_GET_ZOOM_LEVEL,
38
+ CHANNEL_IS_FULL_SCREENABLE,
39
+ CHANNEL_REQUEST_CLOSE,
40
+ CHANNEL_RESTART,
41
+ CHANNEL_SET_TITLE_STYLE,
42
+ CHANNEL_REQUEST_RELOAD,
43
+ CHANNEL_APP_STATE_CHANGED,
44
+ CHANNEL_SHOW_ITEM_IN_FOLDER,
45
+ CHANNEL_READ_CLIPBOARD,
46
+ CHANNEL_WRITE_CLIPBOARD,
47
+ CHANNEL_IPC_CONNECTION,
48
+ CHANNEL_IS_FULL_SCREEN,
49
+ InternalMenuDto,
50
+ CHANNEL_SET_MENU_BAR_VISIBLE,
51
+ CHANNEL_TOGGLE_FULL_SCREEN,
52
+ CHANNEL_IS_MAXIMIZED
53
+ } from '../electron-common/electron-api';
54
+ import { ElectronMainApplication, ElectronMainApplicationContribution } from './electron-main-application';
55
+ import { Disposable, DisposableCollection, isOSX, MaybePromise } from '../common';
56
+ import { createDisposableListener } from './event-utils';
57
+
58
+ @injectable()
59
+ export class TheiaMainApi implements ElectronMainApplicationContribution {
60
+ @inject(ElectronSecurityToken)
61
+ protected electronSecurityToken: ElectronSecurityToken;
62
+
63
+ protected readonly openPopups = new Map<number, Menu>();
64
+
65
+ onStart(application: ElectronMainApplication): MaybePromise<void> {
66
+ // electron security token
67
+ ipcMain.handle(CHANNEL_GET_SECURITY_TOKEN, () => this.electronSecurityToken.value);
68
+
69
+ ipcMain.handle(CHANNEL_ATTACH_SECURITY_TOKEN, (event, endpoint) => session.defaultSession.cookies.set({
70
+ url: endpoint,
71
+ name: ElectronSecurityToken,
72
+ value: JSON.stringify(this.electronSecurityToken),
73
+ httpOnly: true,
74
+ sameSite: 'no_restriction'
75
+ }));
76
+
77
+ // application menu
78
+ ipcMain.on(CHANNEL_SET_MENU, (event, menuId: number, menu: MenuDto[]) => {
79
+ let electronMenu: Menu | null;
80
+ if (menu) {
81
+ electronMenu = Menu.buildFromTemplate(this.fromMenuDto(event.sender, menuId, menu));
82
+ } else {
83
+ // eslint-disable-next-line no-null/no-null
84
+ electronMenu = null;
85
+ }
86
+ if (isOSX) {
87
+ Menu.setApplicationMenu(electronMenu);
88
+ } else {
89
+ BrowserWindow.fromWebContents(event.sender)?.setMenu(electronMenu);
90
+ }
91
+ });
92
+
93
+ ipcMain.on(CHANNEL_SET_MENU_BAR_VISIBLE, (event, visible: boolean, windowName: string | undefined) => {
94
+ let electronWindow;
95
+ if (windowName) {
96
+ electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName);
97
+ } else {
98
+ electronWindow = BrowserWindow.fromWebContents(event.sender);
99
+ }
100
+ if (electronWindow) {
101
+ electronWindow.setMenuBarVisibility(visible);
102
+ } else {
103
+ console.warn(`There is no known secondary window '${windowName}'. Thus, the menu bar could not be made visible.`);
104
+ }
105
+ });
106
+
107
+ // popup menu
108
+ ipcMain.handle(CHANNEL_OPEN_POPUP, (event, menuId, menu, x, y) => {
109
+ const zoom = event.sender.getZoomFactor();
110
+ // TODO: Remove the offset once Electron fixes https://github.com/electron/electron/issues/31641
111
+ const offset = process.platform === 'win32' ? 0 : 2;
112
+ // x and y values must be Ints or else there is a conversion error
113
+ x = Math.round(x * zoom) + offset;
114
+ y = Math.round(y * zoom) + offset;
115
+ const popup = Menu.buildFromTemplate(this.fromMenuDto(event.sender, menuId, menu));
116
+ this.openPopups.set(menuId, popup);
117
+ popup.popup({
118
+ callback: () => {
119
+ this.openPopups.delete(menuId);
120
+ event.sender.send(CHANNEL_ON_CLOSE_POPUP, menuId);
121
+ }
122
+ });
123
+ });
124
+
125
+ ipcMain.handle(CHANNEL_CLOSE_POPUP, (event, handle) => {
126
+ if (this.openPopups.has(handle)) {
127
+ this.openPopups.get(handle)!.closePopup();
128
+ }
129
+ });
130
+
131
+ // focus windows for secondary window support
132
+ ipcMain.on(CHANNEL_FOCUS_WINDOW, (event, windowName) => {
133
+ const electronWindow = BrowserWindow.getAllWindows().find(win => win.webContents.mainFrame.name === windowName);
134
+ if (electronWindow) {
135
+ if (electronWindow.isMinimized()) {
136
+ electronWindow.restore();
137
+ }
138
+ electronWindow.focus();
139
+ } else {
140
+ console.warn(`There is no known secondary window '${windowName}'. Thus, the window could not be focussed.`);
141
+ }
142
+ });
143
+
144
+ ipcMain.on(CHANNEL_SHOW_ITEM_IN_FOLDER, (event, fsPath) => {
145
+ shell.showItemInFolder(fsPath);
146
+ });
147
+
148
+ ipcMain.handle(CHANNEL_GET_TITLE_STYLE_AT_STARTUP, event => application.getTitleBarStyleAtStartup(event.sender));
149
+
150
+ ipcMain.on(CHANNEL_SET_TITLE_STYLE, (event, style) => application.setTitleBarStyle(event.sender, style));
151
+
152
+ ipcMain.on(CHANNEL_MINIMIZE, event => {
153
+ BrowserWindow.fromWebContents(event.sender)?.minimize();
154
+ });
155
+
156
+ ipcMain.on(CHANNEL_IS_MAXIMIZED, event => {
157
+ event.returnValue = BrowserWindow.fromWebContents(event.sender)?.isMaximized();
158
+ });
159
+
160
+ ipcMain.on(CHANNEL_MAXIMIZE, event => {
161
+ BrowserWindow.fromWebContents(event.sender)?.maximize();
162
+ });
163
+
164
+ ipcMain.on(CHANNEL_UNMAXIMIZE, event => {
165
+ BrowserWindow.fromWebContents(event.sender)?.unmaximize();
166
+ });
167
+
168
+ ipcMain.on(CHANNEL_CLOSE, event => {
169
+ BrowserWindow.fromWebContents(event.sender)?.close();
170
+ });
171
+
172
+ ipcMain.on(CHANNEL_RESTART, event => {
173
+ application.restart(event.sender);
174
+ });
175
+
176
+ ipcMain.on(CHANNEL_TOGGLE_DEVTOOLS, event => {
177
+ event.sender.toggleDevTools();
178
+ });
179
+
180
+ ipcMain.on(CHANNEL_SET_ZOOM_LEVEL, (event, zoomLevel: number) => {
181
+ event.sender.setZoomLevel(zoomLevel);
182
+ });
183
+
184
+ ipcMain.handle(CHANNEL_GET_ZOOM_LEVEL, event => event.sender.getZoomLevel());
185
+
186
+ ipcMain.on(CHANNEL_TOGGLE_FULL_SCREEN, event => {
187
+ const win = BrowserWindow.fromWebContents(event.sender);
188
+ if (win) {
189
+ win.setFullScreen(!win.isFullScreen());
190
+ }
191
+ });
192
+ ipcMain.on(CHANNEL_IS_FULL_SCREENABLE, event => {
193
+ event.returnValue = BrowserWindow.fromWebContents(event.sender)?.isFullScreenable();
194
+ });
195
+
196
+ ipcMain.on(CHANNEL_IS_FULL_SCREEN, event => {
197
+ event.returnValue = BrowserWindow.fromWebContents(event.sender)?.isFullScreen();
198
+ });
199
+
200
+ ipcMain.on(CHANNEL_READ_CLIPBOARD, event => {
201
+ event.returnValue = clipboard.readText();
202
+ });
203
+ ipcMain.on(CHANNEL_WRITE_CLIPBOARD, (event, text) => {
204
+ clipboard.writeText(text);
205
+ });
206
+
207
+ nativeKeymap.onDidChangeKeyboardLayout(() => {
208
+ const newLayout = {
209
+ info: nativeKeymap.getCurrentKeyboardLayout(),
210
+ mapping: nativeKeymap.getKeyMap()
211
+ };
212
+ for (const webContent of webContents.getAllWebContents()) {
213
+ webContent.send('keyboardLayoutChanged', newLayout);
214
+ }
215
+ });
216
+ }
217
+
218
+ fromMenuDto(sender: WebContents, menuId: number, menuDto: InternalMenuDto[]): MenuItemConstructorOptions[] {
219
+ return menuDto.map(dto => {
220
+
221
+ const result: MenuItemConstructorOptions = {
222
+ id: dto.id,
223
+ label: dto.label,
224
+ type: dto.type,
225
+ checked: dto.checked,
226
+ enabled: dto.enabled,
227
+ visible: dto.visible,
228
+ role: dto.role,
229
+ accelerator: dto.accelerator
230
+ };
231
+ if (dto.submenu) {
232
+ result.submenu = this.fromMenuDto(sender, menuId, dto.submenu);
233
+ }
234
+ if (dto.handlerId) {
235
+ result.click = () => {
236
+ sender.send(CHANNEL_INVOKE_MENU, menuId, dto.handlerId);
237
+ };
238
+ }
239
+ return result;
240
+ });
241
+ }
242
+ }
243
+
244
+ let nextReplyChannel: number = 0;
245
+
246
+ export namespace TheiaRendererAPI {
247
+ export function sendWindowEvent(wc: WebContents, event: WindowEvent): void {
248
+ wc.send(CHANNEL_ON_WINDOW_EVENT, event);
249
+ }
250
+
251
+ export function requestClose(wc: WebContents, stopReason: StopReason): Promise<boolean> {
252
+ const channelNr = nextReplyChannel++;
253
+ const confirmChannel = `confirm-${channelNr}`;
254
+ const cancelChannel = `cancel-${channelNr}`;
255
+ const disposables = new DisposableCollection();
256
+
257
+ return new Promise<boolean>(resolve => {
258
+ wc.send(CHANNEL_REQUEST_CLOSE, stopReason, confirmChannel, cancelChannel);
259
+ createDisposableListener(ipcMain, confirmChannel, e => {
260
+ resolve(true);
261
+ }, disposables);
262
+ createDisposableListener(ipcMain, cancelChannel, e => {
263
+ resolve(false);
264
+ }, disposables);
265
+ }).finally(() => disposables.dispose());
266
+ }
267
+
268
+ export function onRequestReload(wc: WebContents, handler: () => void): Disposable {
269
+ return createWindowListener(wc, CHANNEL_REQUEST_RELOAD, handler);
270
+ }
271
+
272
+ export function onApplicationStateChanged(wc: WebContents, handler: (state: FrontendApplicationState) => void): Disposable {
273
+ return createWindowListener(wc, CHANNEL_APP_STATE_CHANGED, state => handler(state as FrontendApplicationState));
274
+ }
275
+
276
+ export function onIpcData(handler: (sender: WebContents, data: Uint8Array) => void): Disposable {
277
+ return createDisposableListener<IpcMainEvent>(ipcMain, CHANNEL_IPC_CONNECTION, (event, data) => handler(event.sender, data as Uint8Array));
278
+ }
279
+
280
+ export function sendData(wc: WebContents, data: Uint8Array): void {
281
+ wc.send(CHANNEL_IPC_CONNECTION, data);
282
+ }
283
+
284
+ function createWindowListener(wc: WebContents, channel: string, handler: (...args: unknown[]) => unknown): Disposable {
285
+ return createDisposableListener<IpcMainEvent>(ipcMain, channel, (event, ...args) => {
286
+ if (wc.id === event.sender.id) {
287
+ handler(...args);
288
+ }
289
+ });
290
+ }
291
+ }
@@ -27,7 +27,7 @@ import { ElectronMessagingService } from './messaging/electron-messaging-service
27
27
  import { ElectronConnectionHandler } from '../electron-common/messaging/electron-connection-handler';
28
28
  import { ElectronSecurityTokenService } from './electron-security-token-service';
29
29
  import { TheiaBrowserWindowOptions, TheiaElectronWindow, TheiaElectronWindowFactory, WindowApplicationConfig } from './theia-electron-window';
30
- import { ElectronNativeKeymap } from './electron-native-keymap';
30
+ import { TheiaMainApi } from './electron-api-main';
31
31
 
32
32
  const electronSecurityToken: ElectronSecurityToken = { value: v4() };
33
33
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -44,6 +44,8 @@ export default new ContainerModule(bind => {
44
44
  bindContributionProvider(bind, ElectronMainApplicationContribution);
45
45
 
46
46
  bind(ElectronMainApplicationContribution).toService(ElectronMessagingContribution);
47
+ bind(TheiaMainApi).toSelf().inSingletonScope();
48
+ bind(ElectronMainApplicationContribution).toService(TheiaMainApi);
47
49
 
48
50
  bind(ElectronMainWindowService).to(ElectronMainWindowServiceImpl).inSingletonScope();
49
51
  bind(ElectronConnectionHandler).toDynamicValue(context =>
@@ -60,7 +62,4 @@ export default new ContainerModule(bind => {
60
62
  child.bind(WindowApplicationConfig).toConstantValue(config);
61
63
  return child.get(TheiaElectronWindow);
62
64
  });
63
-
64
- bind(ElectronNativeKeymap).toSelf().inSingletonScope();
65
- bind(ElectronMainApplicationContribution).toService(ElectronNativeKeymap);
66
65
  });
@@ -15,8 +15,7 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { inject, injectable, named } from 'inversify';
18
- import * as electronRemoteMain from '../../electron-shared/@electron/remote/main';
19
- import { screen, ipcMain, app, BrowserWindow, Event as ElectronEvent, BrowserWindowConstructorOptions, nativeImage } from '../../electron-shared/electron';
18
+ import { screen, app, BrowserWindow, WebContents, Event as ElectronEvent, BrowserWindowConstructorOptions, nativeImage } from '../../electron-shared/electron';
20
19
  import * as path from 'path';
21
20
  import { Argv } from 'yargs';
22
21
  import { AddressInfo } from 'net';
@@ -32,16 +31,12 @@ import { ElectronSecurityTokenService } from './electron-security-token-service'
32
31
  import { ElectronSecurityToken } from '../electron-common/electron-token';
33
32
  import Storage = require('electron-store');
34
33
  import { Disposable, DisposableCollection, isOSX, isWindows } from '../common';
35
- import {
36
- RequestTitleBarStyle,
37
- Restart, StopReason,
38
- TitleBarStyleAtStartup,
39
- TitleBarStyleChanged
40
- } from '../electron-common/messaging/electron-messages';
41
34
  import { DEFAULT_WINDOW_HASH } from '../common/window';
42
35
  import { TheiaBrowserWindowOptions, TheiaElectronWindow, TheiaElectronWindowFactory } from './theia-electron-window';
43
36
  import { ElectronMainApplicationGlobals } from './electron-main-constants';
44
37
  import { createDisposableListener } from './event-utils';
38
+ import { TheiaRendererAPI } from './electron-api-main';
39
+ import { StopReason } from '../common/frontend-application-state';
45
40
 
46
41
  export { ElectronMainApplicationGlobals };
47
42
 
@@ -156,7 +151,6 @@ export namespace ElectronMainProcessArgv {
156
151
 
157
152
  @injectable()
158
153
  export class ElectronMainApplication {
159
-
160
154
  @inject(ContributionProvider)
161
155
  @named(ElectronMainApplicationContribution)
162
156
  protected readonly contributions: ContributionProvider<ElectronMainApplicationContribution>;
@@ -229,6 +223,24 @@ export class ElectronMainApplication {
229
223
  return isWindows ? 'custom' : 'native';
230
224
  }
231
225
 
226
+ public setTitleBarStyle(webContents: WebContents, style: string): void {
227
+ this.useNativeWindowFrame = isOSX || style === 'native';
228
+ const browserWindow = BrowserWindow.fromWebContents(webContents);
229
+ if (browserWindow) {
230
+ this.saveWindowState(browserWindow);
231
+ } else {
232
+ console.warn(`no BrowserWindow with id: ${webContents.id}`);
233
+ }
234
+ }
235
+
236
+ /**
237
+ * @param id the id of the WebContents of the BrowserWindow in question
238
+ * @returns 'native' or 'custom'
239
+ */
240
+ getTitleBarStyleAtStartup(webContents: WebContents): 'native' | 'custom' {
241
+ return this.didUseNativeWindowFrameOnStart.get(webContents.id) ? 'native' : 'custom';
242
+ }
243
+
232
244
  protected async launch(params: ElectronMainExecutionParams): Promise<void> {
233
245
  createYargs(params.argv, params.cwd)
234
246
  .command('$0 [file]', false,
@@ -247,11 +259,13 @@ export class ElectronMainApplication {
247
259
  let options = await asyncOptions;
248
260
  options = this.avoidOverlap(options);
249
261
  const electronWindow = this.windowFactory(options, this.config);
250
- const { window: { id } } = electronWindow;
262
+ const id = electronWindow.window.webContents.id;
251
263
  this.windows.set(id, electronWindow);
252
264
  electronWindow.onDidClose(() => this.windows.delete(id));
265
+ electronWindow.window.on('maximize', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'maximize'));
266
+ electronWindow.window.on('unmaximize', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'unmaximize'));
267
+ electronWindow.window.on('focus', () => TheiaRendererAPI.sendWindowEvent(electronWindow.window.webContents, 'focus'));
253
268
  this.attachSaveWindowState(electronWindow.window);
254
- electronRemoteMain.enable(electronWindow.window.webContents);
255
269
  this.configureNativeSecondaryWindowCreation(electronWindow.window);
256
270
  return electronWindow.window;
257
271
  }
@@ -292,12 +306,13 @@ export class ElectronMainApplication {
292
306
  minHeight: 120,
293
307
  webPreferences: {
294
308
  // `global` is undefined when `true`.
295
- contextIsolation: false,
296
- // https://github.com/eclipse-theia/theia/issues/2018
297
- nodeIntegration: true,
309
+ contextIsolation: true,
310
+ sandbox: false,
311
+ nodeIntegration: false,
298
312
  // Setting the following option to `true` causes some features to break, somehow.
299
313
  // Issue: https://github.com/eclipse-theia/theia/issues/8577
300
314
  nodeIntegrationInWorker: false,
315
+ preload: path.resolve(this.globals.THEIA_APP_PROJECT_PATH, 'lib/preload.js').toString()
301
316
  },
302
317
  ...this.config.electron?.windowOptions || {},
303
318
  };
@@ -418,8 +433,8 @@ export class ElectronMainApplication {
418
433
  }, windowStateListeners);
419
434
  createDisposableListener(electronWindow, 'resize', saveWindowStateDelayed, windowStateListeners);
420
435
  createDisposableListener(electronWindow, 'move', saveWindowStateDelayed, windowStateListeners);
421
- windowStateListeners.push(Disposable.create(() => { try { this.didUseNativeWindowFrameOnStart.delete(electronWindow.id); } catch { } }));
422
- this.didUseNativeWindowFrameOnStart.set(electronWindow.id, this.useNativeWindowFrame);
436
+ windowStateListeners.push(Disposable.create(() => { try { this.didUseNativeWindowFrameOnStart.delete(electronWindow.webContents.id); } catch { } }));
437
+ this.didUseNativeWindowFrameOnStart.set(electronWindow.webContents.id, this.useNativeWindowFrame);
423
438
  electronWindow.once('closed', () => windowStateListeners.dispose());
424
439
  }
425
440
 
@@ -532,24 +547,6 @@ export class ElectronMainApplication {
532
547
  app.on('will-quit', this.onWillQuit.bind(this));
533
548
  app.on('second-instance', this.onSecondInstance.bind(this));
534
549
  app.on('window-all-closed', this.onWindowAllClosed.bind(this));
535
-
536
- ipcMain.on(TitleBarStyleChanged, ({ sender }, titleBarStyle: string) => {
537
- this.useNativeWindowFrame = isOSX || titleBarStyle === 'native';
538
- const browserWindow = BrowserWindow.fromId(sender.id);
539
- if (browserWindow) {
540
- this.saveWindowState(browserWindow);
541
- } else {
542
- console.warn(`no BrowserWindow with id: ${sender.id}`);
543
- }
544
- });
545
-
546
- ipcMain.on(Restart, ({ sender }) => {
547
- this.restart(sender.id);
548
- });
549
-
550
- ipcMain.on(RequestTitleBarStyle, ({ sender }) => {
551
- sender.send(TitleBarStyleAtStartup, this.didUseNativeWindowFrameOnStart.get(sender.id) ? 'native' : 'custom');
552
- });
553
550
  }
554
551
 
555
552
  protected onWillQuit(event: ElectronEvent): void {
@@ -573,10 +570,9 @@ export class ElectronMainApplication {
573
570
  }
574
571
  }
575
572
 
576
- protected async restart(id: number): Promise<void> {
573
+ public async restart(webContents: WebContents): Promise<void> {
577
574
  this.restarting = true;
578
- const window = BrowserWindow.fromId(id);
579
- const wrapper = this.windows.get(window?.id as number); // If it's not a number, we won't get anything.
575
+ const wrapper = this.windows.get(webContents.id);
580
576
  if (wrapper) {
581
577
  const listener = wrapper.onDidClose(async () => {
582
578
  listener.dispose();
@@ -29,7 +29,8 @@ export class ElectronSecurityTokenService {
29
29
  url,
30
30
  name: ElectronSecurityToken,
31
31
  value: JSON.stringify(this.electronSecurityToken),
32
- httpOnly: true
32
+ httpOnly: true,
33
+ sameSite: 'no_restriction'
33
34
  });
34
35
  }
35
36
  }
@@ -14,16 +14,17 @@
14
14
  // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
15
15
  // *****************************************************************************
16
16
 
17
- import { IpcMainEvent, ipcMain, WebContents } from '@theia/electron/shared/electron';
17
+ import { WebContents } from '@theia/electron/shared/electron';
18
18
  import { inject, injectable, named, postConstruct } from 'inversify';
19
19
  import { ContributionProvider } from '../../common/contribution-provider';
20
20
  import { MessagingContribution } from '../../node/messaging/messaging-contribution';
21
- import { ElectronConnectionHandler, THEIA_ELECTRON_IPC_CHANNEL_NAME } from '../../electron-common/messaging/electron-connection-handler';
21
+ import { ElectronConnectionHandler } from '../../electron-common/messaging/electron-connection-handler';
22
22
  import { ElectronMainApplicationContribution } from '../electron-main-application';
23
23
  import { ElectronMessagingService } from './electron-messaging-service';
24
24
  import { AbstractChannel, Channel, ChannelMultiplexer, MessageProvider } from '../../common/message-rpc/channel';
25
- import { Emitter, WriteBuffer } from '../../common';
25
+ import { ConnectionHandler, Emitter, WriteBuffer } from '../../common';
26
26
  import { Uint8ArrayReadBuffer, Uint8ArrayWriteBuffer } from '../../common/message-rpc/uint8-array-message-buffer';
27
+ import { TheiaRendererAPI } from '../electron-api-main';
27
28
 
28
29
  /**
29
30
  * This component replicates the role filled by `MessagingContribution` but for Electron.
@@ -40,7 +41,7 @@ export class ElectronMessagingContribution implements ElectronMainApplicationCon
40
41
  protected readonly messagingContributions: ContributionProvider<ElectronMessagingService.Contribution>;
41
42
 
42
43
  @inject(ContributionProvider) @named(ElectronConnectionHandler)
43
- protected readonly connectionHandlers: ContributionProvider<ElectronConnectionHandler>;
44
+ protected readonly connectionHandlers: ContributionProvider<ConnectionHandler>;
44
45
 
45
46
  protected readonly channelHandlers = new MessagingContribution.ConnectionHandlers<Channel>();
46
47
  /**
@@ -50,13 +51,10 @@ export class ElectronMessagingContribution implements ElectronMainApplicationCon
50
51
 
51
52
  @postConstruct()
52
53
  protected init(): void {
53
- ipcMain.on(THEIA_ELECTRON_IPC_CHANNEL_NAME, (event: IpcMainEvent, data: Uint8Array) => {
54
- this.handleIpcEvent(event, data);
55
- });
54
+ TheiaRendererAPI.onIpcData((sender, data) => this.handleIpcEvent(sender, data));
56
55
  }
57
56
 
58
- protected handleIpcEvent(event: IpcMainEvent, data: Uint8Array): void {
59
- const sender = event.sender;
57
+ protected handleIpcEvent(sender: WebContents, data: Uint8Array): void {
60
58
  // Get the multiplexer for a given window id
61
59
  try {
62
60
  const windowChannelData = this.windowChannelMultiplexer.get(sender.id) ?? this.createWindowChannelData(sender);
@@ -133,7 +131,7 @@ export class ElectronWebContentChannel extends AbstractChannel {
133
131
 
134
132
  writer.onCommit(buffer => {
135
133
  if (!this.sender.isDestroyed()) {
136
- this.sender.send(THEIA_ELECTRON_IPC_CHANNEL_NAME, buffer);
134
+ TheiaRendererAPI.sendData(this.sender, buffer);
137
135
  }
138
136
  });
139
137
 
@@ -15,15 +15,15 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { FrontendApplicationConfig } from '@theia/application-package';
18
- import { FrontendApplicationState } from '../common/frontend-application-state';
19
- import { APPLICATION_STATE_CHANGE_SIGNAL, CLOSE_REQUESTED_SIGNAL, RELOAD_REQUESTED_SIGNAL, StopReason } from '../electron-common/messaging/electron-messages';
20
- import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain, IpcMainEvent } from '../../electron-shared/electron';
18
+ import { FrontendApplicationState, StopReason } from '../common/frontend-application-state';
19
+ import { BrowserWindow, BrowserWindowConstructorOptions } from '../../electron-shared/electron';
21
20
  import { inject, injectable, postConstruct } from '../../shared/inversify';
22
21
  import { ElectronMainApplicationGlobals } from './electron-main-constants';
23
22
  import { DisposableCollection, Emitter, Event } from '../common';
24
23
  import { createDisposableListener } from './event-utils';
25
24
  import { URI } from '../common/uri';
26
25
  import { FileUri } from '../node/file-uri';
26
+ import { TheiaRendererAPI } from './electron-api-main';
27
27
 
28
28
  /**
29
29
  * Theia tracks the maximized state of Electron Browser Windows.
@@ -138,22 +138,7 @@ export class TheiaElectronWindow {
138
138
  }
139
139
 
140
140
  protected checkSafeToStop(reason: StopReason): Promise<boolean> {
141
- const confirmChannel = `safe-to-close-${this._window.id}`;
142
- const cancelChannel = `notSafeToClose-${this._window.id}`;
143
- const temporaryDisposables = new DisposableCollection();
144
- return new Promise<boolean>(resolve => {
145
- this._window.webContents.send(CLOSE_REQUESTED_SIGNAL, { confirmChannel, cancelChannel, reason });
146
- createDisposableListener(ipcMain, confirmChannel, (e: IpcMainEvent) => {
147
- if (this.isSender(e)) {
148
- resolve(true);
149
- }
150
- }, temporaryDisposables);
151
- createDisposableListener(ipcMain, cancelChannel, (e: IpcMainEvent) => {
152
- if (this.isSender(e)) {
153
- resolve(false);
154
- }
155
- }, temporaryDisposables);
156
- }).finally(() => temporaryDisposables.dispose());
141
+ return TheiaRendererAPI.requestClose(this.window.webContents, reason);
157
142
  }
158
143
 
159
144
  protected restoreMaximizedState(): void {
@@ -165,23 +150,13 @@ export class TheiaElectronWindow {
165
150
  }
166
151
 
167
152
  protected trackApplicationState(): void {
168
- createDisposableListener(ipcMain, APPLICATION_STATE_CHANGE_SIGNAL, (e: IpcMainEvent, state: FrontendApplicationState) => {
169
- if (this.isSender(e)) {
170
- this.applicationState = state;
171
- }
172
- }, this.toDispose);
153
+ this.toDispose.push(TheiaRendererAPI.onApplicationStateChanged(this.window.webContents, state => {
154
+ this.applicationState = state;
155
+ }));
173
156
  }
174
157
 
175
158
  protected attachReloadListener(): void {
176
- createDisposableListener(ipcMain, RELOAD_REQUESTED_SIGNAL, (e: IpcMainEvent) => {
177
- if (this.isSender(e)) {
178
- this.reload();
179
- }
180
- }, this.toDispose);
181
- }
182
-
183
- protected isSender(e: IpcMainEvent): boolean {
184
- return BrowserWindow.fromId(e.sender.id) === this._window;
159
+ this.toDispose.push(TheiaRendererAPI.onRequestReload(this.window.webContents, () => this.reload()));
185
160
  }
186
161
 
187
162
  dispose(): void {
@@ -16,6 +16,7 @@
16
16
 
17
17
  import * as express from 'express';
18
18
  import { inject, injectable } from 'inversify';
19
+ import { nls } from '../../common/nls';
19
20
  import { Deferred } from '../../common/promise-util';
20
21
  import { BackendApplicationContribution } from '../backend-application';
21
22
  import { LocalizationRegistry } from './localization-contribution';
@@ -44,7 +45,7 @@ export class LocalizationBackendContribution implements BackendApplicationContri
44
45
  app.get('/i18n/:locale', async (req, res) => {
45
46
  await this.waitForInitialization();
46
47
  let locale = req.params.locale;
47
- locale = this.localizationProvider.getAvailableLanguages().some(e => e.languageId === locale) ? locale : 'en';
48
+ locale = this.localizationProvider.getAvailableLanguages().some(e => e.languageId === locale) ? locale : nls.defaultLocale;
48
49
  this.localizationProvider.setCurrentLanguage(locale);
49
50
  res.send(this.localizationProvider.loadLocalization(locale));
50
51
  });
@@ -15,13 +15,14 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { injectable } from 'inversify';
18
+ import { nls } from '../../common/nls';
18
19
  import { LanguageInfo, Localization } from '../../common/i18n/localization';
19
20
 
20
21
  @injectable()
21
22
  export class LocalizationProvider {
22
23
 
23
24
  protected localizations: Localization[] = [];
24
- protected currentLanguage = 'en';
25
+ protected currentLanguage = nls.defaultLocale;
25
26
 
26
27
  addLocalizations(...localizations: Localization[]): void {
27
28
  this.localizations.push(...localizations);