@theia/core 1.73.0-next.7 → 1.73.0

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 (259) hide show
  1. package/README.md +26 -16
  2. package/i18n/nls.cs.json +268 -36
  3. package/i18n/nls.de.json +268 -36
  4. package/i18n/nls.es.json +268 -36
  5. package/i18n/nls.fr.json +268 -36
  6. package/i18n/nls.hu.json +268 -36
  7. package/i18n/nls.it.json +268 -36
  8. package/i18n/nls.ja.json +268 -36
  9. package/i18n/nls.json +268 -36
  10. package/i18n/nls.ko.json +268 -36
  11. package/i18n/nls.pl.json +268 -36
  12. package/i18n/nls.pt-br.json +268 -36
  13. package/i18n/nls.ru.json +268 -36
  14. package/i18n/nls.tr.json +268 -36
  15. package/i18n/nls.zh-cn.json +268 -36
  16. package/i18n/nls.zh-tw.json +268 -36
  17. package/lib/browser/authentication-service.d.ts +2 -0
  18. package/lib/browser/authentication-service.d.ts.map +1 -1
  19. package/lib/browser/authentication-service.js +8 -2
  20. package/lib/browser/authentication-service.js.map +1 -1
  21. package/lib/browser/catalog.json +251 -72
  22. package/lib/browser/frontend-application.d.ts +2 -0
  23. package/lib/browser/frontend-application.d.ts.map +1 -1
  24. package/lib/browser/frontend-application.js +15 -9
  25. package/lib/browser/frontend-application.js.map +1 -1
  26. package/lib/browser/json-schema-store.d.ts +2 -0
  27. package/lib/browser/json-schema-store.d.ts.map +1 -1
  28. package/lib/browser/json-schema-store.js +8 -2
  29. package/lib/browser/json-schema-store.js.map +1 -1
  30. package/lib/browser/keybinding.js +2 -1
  31. package/lib/browser/keybinding.js.map +1 -1
  32. package/lib/browser/markdown-rendering/markdown.d.ts +1 -1
  33. package/lib/browser/markdown-rendering/markdown.d.ts.map +1 -1
  34. package/lib/browser/markdown-rendering/markdown.js +1 -1
  35. package/lib/browser/markdown-rendering/markdown.js.map +1 -1
  36. package/lib/browser/menu/composite-menu-node.js +4 -4
  37. package/lib/browser/menu/composite-menu-node.js.map +1 -1
  38. package/lib/browser/messaging/ws-connection-source.d.ts.map +1 -1
  39. package/lib/browser/messaging/ws-connection-source.js +1 -6
  40. package/lib/browser/messaging/ws-connection-source.js.map +1 -1
  41. package/lib/browser/preferences/frontend-config-preference-contributions.d.ts +2 -0
  42. package/lib/browser/preferences/frontend-config-preference-contributions.d.ts.map +1 -1
  43. package/lib/browser/preferences/frontend-config-preference-contributions.js +7 -1
  44. package/lib/browser/preferences/frontend-config-preference-contributions.js.map +1 -1
  45. package/lib/browser/preferences/preference-proxy.spec.js +7 -0
  46. package/lib/browser/preferences/preference-proxy.spec.js.map +1 -1
  47. package/lib/browser/preferences/preference-schema-provider.spec.js +3 -0
  48. package/lib/browser/preferences/preference-schema-provider.spec.js.map +1 -1
  49. package/lib/browser/preferences/preference-validation-service.d.ts +2 -0
  50. package/lib/browser/preferences/preference-validation-service.d.ts.map +1 -1
  51. package/lib/browser/preferences/preference-validation-service.js +11 -5
  52. package/lib/browser/preferences/preference-validation-service.js.map +1 -1
  53. package/lib/browser/preferences/preference-validation-service.spec.js +2 -0
  54. package/lib/browser/preferences/preference-validation-service.spec.js.map +1 -1
  55. package/lib/browser/secondary-window-handler.d.ts +2 -0
  56. package/lib/browser/secondary-window-handler.d.ts.map +1 -1
  57. package/lib/browser/secondary-window-handler.js +12 -6
  58. package/lib/browser/secondary-window-handler.js.map +1 -1
  59. package/lib/browser/shell/application-shell.d.ts +2 -0
  60. package/lib/browser/shell/application-shell.d.ts.map +1 -1
  61. package/lib/browser/shell/application-shell.js +9 -3
  62. package/lib/browser/shell/application-shell.js.map +1 -1
  63. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js +6 -6
  64. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.js.map +1 -1
  65. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js +2 -2
  66. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.js.map +1 -1
  67. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js +131 -0
  68. package/lib/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.js.map +1 -1
  69. package/lib/browser/status-bar/status-bar.d.ts +2 -2
  70. package/lib/browser/status-bar/status-bar.d.ts.map +1 -1
  71. package/lib/browser/status-bar/status-bar.js.map +1 -1
  72. package/lib/browser/test/jsdom.d.ts +4 -0
  73. package/lib/browser/test/jsdom.d.ts.map +1 -1
  74. package/lib/browser/test/jsdom.js +18 -0
  75. package/lib/browser/test/jsdom.js.map +1 -1
  76. package/lib/browser/tree/tree-container.spec.js +4 -0
  77. package/lib/browser/tree/tree-container.spec.js.map +1 -1
  78. package/lib/browser/tree/tree-model.d.ts +1 -0
  79. package/lib/browser/tree/tree-model.d.ts.map +1 -1
  80. package/lib/browser/tree/tree-model.js +3 -0
  81. package/lib/browser/tree/tree-model.js.map +1 -1
  82. package/lib/browser/tree/tree-view-welcome-widget.d.ts +1 -0
  83. package/lib/browser/tree/tree-view-welcome-widget.d.ts.map +1 -1
  84. package/lib/browser/tree/tree-view-welcome-widget.js +8 -6
  85. package/lib/browser/tree/tree-view-welcome-widget.js.map +1 -1
  86. package/lib/browser/tree/tree-view-welcome-widget.spec.d.ts +2 -0
  87. package/lib/browser/tree/tree-view-welcome-widget.spec.d.ts.map +1 -0
  88. package/lib/browser/tree/tree-view-welcome-widget.spec.js +68 -0
  89. package/lib/browser/tree/tree-view-welcome-widget.spec.js.map +1 -0
  90. package/lib/browser/tree/tree-widget.js +2 -2
  91. package/lib/browser/tree/tree-widget.js.map +1 -1
  92. package/lib/browser/tree/tree.d.ts +24 -2
  93. package/lib/browser/tree/tree.d.ts.map +1 -1
  94. package/lib/browser/tree/tree.js +20 -2
  95. package/lib/browser/tree/tree.js.map +1 -1
  96. package/lib/browser/tree/tree.spec.js +27 -0
  97. package/lib/browser/tree/tree.spec.js.map +1 -1
  98. package/lib/browser/window/default-window-service.d.ts +2 -1
  99. package/lib/browser/window/default-window-service.d.ts.map +1 -1
  100. package/lib/browser/window/default-window-service.js +10 -5
  101. package/lib/browser/window/default-window-service.js.map +1 -1
  102. package/lib/browser/window/default-window-service.spec.js +2 -0
  103. package/lib/browser/window/default-window-service.spec.js.map +1 -1
  104. package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts +2 -0
  105. package/lib/browser-only/messaging/frontend-only-service-connection-provider.d.ts.map +1 -1
  106. package/lib/browser-only/messaging/frontend-only-service-connection-provider.js +8 -2
  107. package/lib/browser-only/messaging/frontend-only-service-connection-provider.js.map +1 -1
  108. package/lib/common/command.d.ts +2 -0
  109. package/lib/common/command.d.ts.map +1 -1
  110. package/lib/common/command.js +10 -4
  111. package/lib/common/command.js.map +1 -1
  112. package/lib/common/preferences/injectable-preference-proxy.d.ts +3 -3
  113. package/lib/common/preferences/injectable-preference-proxy.d.ts.map +1 -1
  114. package/lib/common/preferences/injectable-preference-proxy.js +6 -1
  115. package/lib/common/preferences/injectable-preference-proxy.js.map +1 -1
  116. package/lib/common/preferences/preference-proxy.d.ts.map +1 -1
  117. package/lib/common/preferences/preference-proxy.js +6 -1
  118. package/lib/common/preferences/preference-proxy.js.map +1 -1
  119. package/lib/electron-main/electron-main-application.d.ts.map +1 -1
  120. package/lib/electron-main/electron-main-application.js +4 -2
  121. package/lib/electron-main/electron-main-application.js.map +1 -1
  122. package/lib/electron-node/hosting/electron-ws-origin-validator.d.ts.map +1 -1
  123. package/lib/electron-node/hosting/electron-ws-origin-validator.js +5 -2
  124. package/lib/electron-node/hosting/electron-ws-origin-validator.js.map +1 -1
  125. package/lib/electron-node/hosting/electron-ws-origin-validator.spec.d.ts +2 -0
  126. package/lib/electron-node/hosting/electron-ws-origin-validator.spec.d.ts.map +1 -0
  127. package/lib/electron-node/hosting/electron-ws-origin-validator.spec.js +59 -0
  128. package/lib/electron-node/hosting/electron-ws-origin-validator.spec.js.map +1 -0
  129. package/lib/electron-node/request/electron-backend-request-service.d.ts +2 -0
  130. package/lib/electron-node/request/electron-backend-request-service.d.ts.map +1 -1
  131. package/lib/electron-node/request/electron-backend-request-service.js +7 -1
  132. package/lib/electron-node/request/electron-backend-request-service.js.map +1 -1
  133. package/lib/electron-node/token/electron-token-backend-contribution.d.ts +2 -0
  134. package/lib/electron-node/token/electron-token-backend-contribution.d.ts.map +1 -1
  135. package/lib/electron-node/token/electron-token-backend-contribution.js +7 -1
  136. package/lib/electron-node/token/electron-token-backend-contribution.js.map +1 -1
  137. package/lib/electron-node/token/electron-token-validator.d.ts +2 -0
  138. package/lib/electron-node/token/electron-token-validator.d.ts.map +1 -1
  139. package/lib/electron-node/token/electron-token-validator.js +7 -1
  140. package/lib/electron-node/token/electron-token-validator.js.map +1 -1
  141. package/lib/node/backend-application-module.d.ts.map +1 -1
  142. package/lib/node/backend-application-module.js +2 -0
  143. package/lib/node/backend-application-module.js.map +1 -1
  144. package/lib/node/backend-application.d.ts +11 -0
  145. package/lib/node/backend-application.d.ts.map +1 -1
  146. package/lib/node/backend-application.js +40 -12
  147. package/lib/node/backend-application.js.map +1 -1
  148. package/lib/node/backend-application.spec.js +4 -2
  149. package/lib/node/backend-application.spec.js.map +1 -1
  150. package/lib/node/hosting/backend-hosting-module.d.ts.map +1 -1
  151. package/lib/node/hosting/backend-hosting-module.js +7 -0
  152. package/lib/node/hosting/backend-hosting-module.js.map +1 -1
  153. package/lib/node/hosting/browser-connection-token.d.ts +46 -0
  154. package/lib/node/hosting/browser-connection-token.d.ts.map +1 -0
  155. package/lib/node/hosting/browser-connection-token.js +120 -0
  156. package/lib/node/hosting/browser-connection-token.js.map +1 -0
  157. package/lib/node/hosting/browser-connection-token.spec.d.ts +2 -0
  158. package/lib/node/hosting/browser-connection-token.spec.d.ts.map +1 -0
  159. package/lib/node/hosting/browser-connection-token.spec.js +128 -0
  160. package/lib/node/hosting/browser-connection-token.spec.js.map +1 -0
  161. package/lib/node/hosting/ws-origin-validator.d.ts.map +1 -1
  162. package/lib/node/hosting/ws-origin-validator.js +21 -4
  163. package/lib/node/hosting/ws-origin-validator.js.map +1 -1
  164. package/lib/node/hosting/ws-origin-validator.spec.d.ts +2 -0
  165. package/lib/node/hosting/ws-origin-validator.spec.d.ts.map +1 -0
  166. package/lib/node/hosting/ws-origin-validator.spec.js +131 -0
  167. package/lib/node/hosting/ws-origin-validator.spec.js.map +1 -0
  168. package/lib/node/index.d.ts +1 -0
  169. package/lib/node/index.d.ts.map +1 -1
  170. package/lib/node/index.js +3 -1
  171. package/lib/node/index.js.map +1 -1
  172. package/lib/node/logger-cli-contribution.d.ts +6 -0
  173. package/lib/node/logger-cli-contribution.d.ts.map +1 -1
  174. package/lib/node/logger-cli-contribution.js +28 -5
  175. package/lib/node/logger-cli-contribution.js.map +1 -1
  176. package/lib/node/logger-cli-contribution.spec.js +54 -0
  177. package/lib/node/logger-cli-contribution.spec.js.map +1 -1
  178. package/lib/node/messaging/default-messaging-service.d.ts +2 -0
  179. package/lib/node/messaging/default-messaging-service.d.ts.map +1 -1
  180. package/lib/node/messaging/default-messaging-service.js +8 -2
  181. package/lib/node/messaging/default-messaging-service.js.map +1 -1
  182. package/lib/node/messaging/test/default-messaging-service.spec.js +2 -0
  183. package/lib/node/messaging/test/default-messaging-service.spec.js.map +1 -1
  184. package/lib/node/messaging/websocket-endpoint.d.ts +7 -0
  185. package/lib/node/messaging/websocket-endpoint.d.ts.map +1 -1
  186. package/lib/node/messaging/websocket-endpoint.js +21 -6
  187. package/lib/node/messaging/websocket-endpoint.js.map +1 -1
  188. package/lib/node/process-utils.d.ts +2 -0
  189. package/lib/node/process-utils.d.ts.map +1 -1
  190. package/lib/node/process-utils.js +8 -2
  191. package/lib/node/process-utils.js.map +1 -1
  192. package/lib/node/process-utils.spec.js +11 -9
  193. package/lib/node/process-utils.spec.js.map +1 -1
  194. package/lib/node/request/backend-request-facade.d.ts +19 -0
  195. package/lib/node/request/backend-request-facade.d.ts.map +1 -1
  196. package/lib/node/request/backend-request-facade.js +93 -3
  197. package/lib/node/request/backend-request-facade.js.map +1 -1
  198. package/lib/node/request/backend-request-facade.spec.d.ts +2 -0
  199. package/lib/node/request/backend-request-facade.spec.d.ts.map +1 -0
  200. package/lib/node/request/backend-request-facade.spec.js +171 -0
  201. package/lib/node/request/backend-request-facade.spec.js.map +1 -0
  202. package/package.json +29 -20
  203. package/src/browser/authentication-service.ts +6 -3
  204. package/src/browser/frontend-application.ts +13 -9
  205. package/src/browser/json-schema-store.ts +6 -2
  206. package/src/browser/keybinding.ts +2 -2
  207. package/src/browser/markdown-rendering/markdown.tsx +2 -2
  208. package/src/browser/menu/composite-menu-node.ts +4 -4
  209. package/src/browser/messaging/ws-connection-source.ts +1 -6
  210. package/src/browser/preferences/frontend-config-preference-contributions.ts +7 -2
  211. package/src/browser/preferences/preference-proxy.spec.ts +8 -0
  212. package/src/browser/preferences/preference-schema-provider.spec.ts +5 -0
  213. package/src/browser/preferences/preference-validation-service.spec.ts +3 -1
  214. package/src/browser/preferences/preference-validation-service.ts +9 -6
  215. package/src/browser/secondary-window-handler.ts +11 -7
  216. package/src/browser/shell/application-shell.ts +8 -4
  217. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-menu-adapters.tsx +6 -6
  218. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar-registry.ts +2 -2
  219. package/src/browser/shell/tab-bar-toolbar/tab-bar-toolbar.spec.ts +159 -1
  220. package/src/browser/status-bar/status-bar.tsx +3 -3
  221. package/src/browser/test/jsdom.ts +18 -0
  222. package/src/browser/tree/tree-container.spec.ts +6 -0
  223. package/src/browser/tree/tree-model.ts +4 -0
  224. package/src/browser/tree/tree-view-welcome-widget.spec.ts +82 -0
  225. package/src/browser/tree/tree-view-welcome-widget.tsx +10 -11
  226. package/src/browser/tree/tree-widget.tsx +2 -2
  227. package/src/browser/tree/tree.spec.ts +30 -0
  228. package/src/browser/tree/tree.ts +31 -4
  229. package/src/browser/window/default-window-service.spec.ts +3 -1
  230. package/src/browser/window/default-window-service.ts +9 -6
  231. package/src/browser-only/messaging/frontend-only-service-connection-provider.ts +8 -3
  232. package/src/common/command.ts +8 -4
  233. package/src/common/i18n/nls.metadata.json +2659 -374
  234. package/src/common/preferences/injectable-preference-proxy.ts +10 -4
  235. package/src/common/preferences/preference-proxy.ts +10 -4
  236. package/src/electron-main/electron-main-application.ts +4 -2
  237. package/src/electron-node/hosting/electron-ws-origin-validator.spec.ts +69 -0
  238. package/src/electron-node/hosting/electron-ws-origin-validator.ts +5 -2
  239. package/src/electron-node/request/electron-backend-request-service.ts +6 -2
  240. package/src/electron-node/token/electron-token-backend-contribution.ts +6 -2
  241. package/src/electron-node/token/electron-token-validator.ts +6 -2
  242. package/src/node/backend-application-module.ts +4 -2
  243. package/src/node/backend-application.spec.ts +5 -3
  244. package/src/node/backend-application.ts +33 -11
  245. package/src/node/hosting/backend-hosting-module.ts +10 -0
  246. package/src/node/hosting/browser-connection-token.spec.ts +167 -0
  247. package/src/node/hosting/browser-connection-token.ts +126 -0
  248. package/src/node/hosting/ws-origin-validator.spec.ts +193 -0
  249. package/src/node/hosting/ws-origin-validator.ts +23 -4
  250. package/src/node/index.ts +1 -0
  251. package/src/node/logger-cli-contribution.spec.ts +65 -0
  252. package/src/node/logger-cli-contribution.ts +32 -5
  253. package/src/node/messaging/default-messaging-service.ts +6 -2
  254. package/src/node/messaging/test/default-messaging-service.spec.ts +3 -1
  255. package/src/node/messaging/websocket-endpoint.ts +23 -7
  256. package/src/node/process-utils.spec.ts +12 -9
  257. package/src/node/process-utils.ts +7 -3
  258. package/src/node/request/backend-request-facade.spec.ts +203 -0
  259. package/src/node/request/backend-request-facade.ts +108 -3
@@ -106,7 +106,13 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
106
106
  }
107
107
  }
108
108
 
109
- get(target: unknown, property: string, receiver: unknown): unknown {
109
+ get(target: unknown, property: string | symbol, receiver: unknown): unknown {
110
+ // React 19 dev mode calls Object.prototype.toString when handling prop diffs, which
111
+ // probes Symbol.toStringTag. Throwing should be avoided in this case, it crashes the React DOM.
112
+ if (property === Symbol.toStringTag) {
113
+ return undefined;
114
+ }
115
+
110
116
  if (typeof property !== 'string') {
111
117
  throw new Error(`Unexpected property: ${String(property)}`);
112
118
  }
@@ -156,7 +162,7 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
156
162
  }
157
163
  }
158
164
 
159
- set(target: unknown, property: string, value: unknown, receiver: unknown): boolean {
165
+ set(target: unknown, property: string | symbol, value: unknown, receiver: unknown): boolean {
160
166
  if (typeof property !== 'string') {
161
167
  throw new Error(`Unexpected property: ${String(property)}`);
162
168
  }
@@ -212,8 +218,8 @@ export class InjectablePreferenceProxy<T extends Record<string, JSONValue>> impl
212
218
  return properties;
213
219
  }
214
220
 
215
- getOwnPropertyDescriptor(target: unknown, property: string): PropertyDescriptor {
216
- if (this.ownKeys().includes(property)) {
221
+ getOwnPropertyDescriptor(target: unknown, property: string | symbol): PropertyDescriptor {
222
+ if (typeof property === 'string' && this.ownKeys().includes(property)) {
217
223
  return {
218
224
  enumerable: true,
219
225
  configurable: true
@@ -239,7 +239,7 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, promise
239
239
  return properties;
240
240
  };
241
241
 
242
- const set: (target: any, prop: string, value: any, receiver: any) => boolean = (_, property: string | symbol | number, value: any) => {
242
+ const set: (target: any, prop: string | symbol, value: any, receiver: any) => boolean = (_, property: string | symbol | number, value: any) => {
243
243
  if (typeof property !== 'string') {
244
244
  throw new Error(`unexpected property: ${String(property)}`);
245
245
  }
@@ -270,7 +270,13 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, promise
270
270
  return false;
271
271
  };
272
272
 
273
- const get: (target: any, prop: string) => any = (_, property: string | symbol | number) => {
273
+ const get: (target: any, prop: string | symbol) => any = (_, property: string | symbol | number) => {
274
+ // React 19 dev mode calls Object.prototype.toString when handling prop diffs, which
275
+ // probes Symbol.toStringTag. Throwing should be avoided in this case, it crashes the React DOM.
276
+ if (property === Symbol.toStringTag) {
277
+ return undefined;
278
+ }
279
+
274
280
  if (typeof property !== 'string') {
275
281
  throw new Error(`unexpected property: ${String(property)}`);
276
282
  }
@@ -347,8 +353,8 @@ export function createPreferenceProxy<T>(preferences: PreferenceService, promise
347
353
  return new Proxy({}, {
348
354
  get,
349
355
  ownKeys,
350
- getOwnPropertyDescriptor: (_, property: string) => {
351
- if (ownKeys().indexOf(property) !== -1) {
356
+ getOwnPropertyDescriptor: (_, property: string | symbol) => {
357
+ if (typeof property === 'string' && ownKeys().indexOf(property) !== -1) {
352
358
  return {
353
359
  enumerable: true,
354
360
  configurable: true
@@ -394,6 +394,8 @@ export class ElectronMainApplication {
394
394
  const cancelTokenSource = new CancellationTokenSource();
395
395
  const minTime = timeout(splashScreenOptions.minDuration ?? 0, cancelTokenSource.token);
396
396
  const maxTime = timeout(splashScreenOptions.maxDuration ?? 30000, cancelTokenSource.token);
397
+ // Swallow rejections that occur when the cancellation token is cancelled after one of the timers wins.
398
+ const ignoreCancellation = () => { /* timer was cancelled, intentionally ignored */ };
397
399
 
398
400
  const showWindowAndCloseSplashScreen = () => {
399
401
  cancelTokenSource.cancel();
@@ -404,10 +406,10 @@ export class ElectronMainApplication {
404
406
  };
405
407
  TheiaRendererAPI.onApplicationStateChanged(mainWindow.webContents, state => {
406
408
  if (state === 'ready') {
407
- minTime.then(() => showWindowAndCloseSplashScreen());
409
+ minTime.then(() => showWindowAndCloseSplashScreen(), ignoreCancellation);
408
410
  }
409
411
  });
410
- maxTime.then(() => showWindowAndCloseSplashScreen());
412
+ maxTime.then(() => showWindowAndCloseSplashScreen(), ignoreCancellation);
411
413
  return splashScreenWindow;
412
414
  }
413
415
 
@@ -0,0 +1,69 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2026 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-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { expect } from 'chai';
18
+ import * as http from 'http';
19
+ import { ElectronWsOriginValidator } from './electron-ws-origin-validator';
20
+ import { BackendRemoteService } from '../../node/remote/backend-remote-service';
21
+
22
+ describe('ElectronWsOriginValidator', () => {
23
+
24
+ function createValidator(isRemote = false): ElectronWsOriginValidator {
25
+ const remoteService = { isRemoteServer: () => isRemote } as BackendRemoteService;
26
+ const validator = new ElectronWsOriginValidator();
27
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
28
+ (validator as any)['backendRemoteService'] = remoteService;
29
+ return validator;
30
+ }
31
+
32
+ function createRequest(origin?: string): http.IncomingMessage {
33
+ const request = {
34
+ headers: {} as http.IncomingHttpHeaders
35
+ } as http.IncomingMessage;
36
+ if (origin !== undefined) {
37
+ request.headers.origin = origin;
38
+ }
39
+ return request;
40
+ }
41
+
42
+ it('should allow file:// origin (standard Electron case)', () => {
43
+ expect(createValidator().allowWsUpgrade(createRequest('file://'))).to.be.true;
44
+ });
45
+
46
+ it('should allow null origin (opaque origin from file:// pages)', () => {
47
+ expect(createValidator().allowWsUpgrade(createRequest('null'))).to.be.true;
48
+ });
49
+
50
+ it('should allow absent origin (same-origin polling requests)', () => {
51
+ expect(createValidator().allowWsUpgrade(createRequest(undefined))).to.be.true;
52
+ });
53
+
54
+ it('should reject cross-origin requests', () => {
55
+ expect(createValidator().allowWsUpgrade(createRequest('http://evil.com'))).to.be.false;
56
+ });
57
+
58
+ it('should reject http://localhost origin (not expected in Electron)', () => {
59
+ expect(createValidator().allowWsUpgrade(createRequest('http://localhost:3000'))).to.be.false;
60
+ });
61
+
62
+ it('should allow empty string origin (treated as absent)', () => {
63
+ expect(createValidator().allowWsUpgrade(createRequest(''))).to.be.true;
64
+ });
65
+
66
+ it('should allow any origin when running as remote server', () => {
67
+ expect(createValidator(true).allowWsUpgrade(createRequest('http://remote-host.com'))).to.be.true;
68
+ });
69
+ });
@@ -30,8 +30,11 @@ export class ElectronWsOriginValidator implements WsRequestValidatorContribution
30
30
  if (this.backendRemoteService.isRemoteServer()) {
31
31
  return true;
32
32
  }
33
+ const origin = request.headers.origin;
33
34
  // On Electron the main page is served from the `file` protocol.
34
- // We don't expect the requests to come from anywhere else.
35
- return request.headers.origin === 'file://';
35
+ // Chromium may send "file://" or "null" (opaque origin) or omit the
36
+ // header entirely depending on the request type (WebSocket upgrade vs
37
+ // HTTP polling). All three are expected for same-origin Electron requests.
38
+ return !origin || origin === 'file://' || origin === 'null';
36
39
  }
37
40
  }
@@ -14,15 +14,19 @@
14
14
  * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
15
  ********************************************************************************/
16
16
 
17
- import { decorate, injectable } from 'inversify';
17
+ import { decorate, injectable, inject, named } from 'inversify';
18
18
  import { NodeRequestOptions, NodeRequestService } from '@theia/request/lib/node-request-service';
19
19
  import { ElectronSecurityToken } from '../../electron-common/electron-token';
20
+ import { ILogger } from '../../common';
20
21
 
21
22
  decorate(injectable(), NodeRequestService);
22
23
 
23
24
  @injectable()
24
25
  export class ElectronBackendRequestService extends NodeRequestService {
25
26
 
27
+ @inject(ILogger) @named('core:ElectronBackendRequestService')
28
+ protected readonly logger: ILogger;
29
+
26
30
  override async getProxyUrl(url: string): Promise<string | undefined> {
27
31
  if (this.proxyUrl) {
28
32
  return this.proxyUrl;
@@ -34,7 +38,7 @@ export class ElectronBackendRequestService extends NodeRequestService {
34
38
  return this.buildProxyUrl(url, proxyHost);
35
39
  }
36
40
  } catch (e) {
37
- console.error('Could not resolve electron proxy.', e);
41
+ this.logger.error('Could not resolve electron proxy.', e);
38
42
  }
39
43
  return super.getProxyUrl(url);
40
44
  }
@@ -15,9 +15,10 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import express = require('express');
18
- import { injectable, inject } from 'inversify';
18
+ import { injectable, inject, named } from 'inversify';
19
19
  import { BackendApplicationContribution } from '../../node';
20
20
  import { ElectronTokenValidator } from './electron-token-validator';
21
+ import { ILogger } from '../../common/logger';
21
22
 
22
23
  /**
23
24
  * This component contributes an Express middleware that will refuse all
@@ -26,6 +27,9 @@ import { ElectronTokenValidator } from './electron-token-validator';
26
27
  @injectable()
27
28
  export class ElectronTokenBackendContribution implements BackendApplicationContribution {
28
29
 
30
+ @inject(ILogger) @named('core:ElectronTokenBackendContribution')
31
+ protected readonly logger: ILogger;
32
+
29
33
  @inject(ElectronTokenValidator)
30
34
  protected readonly tokenValidator: ElectronTokenValidator;
31
35
 
@@ -40,7 +44,7 @@ export class ElectronTokenBackendContribution implements BackendApplicationContr
40
44
  if (this.tokenValidator.allowRequest(req)) {
41
45
  next();
42
46
  } else {
43
- console.error(`refused an http request: ${req.connection.remoteAddress}`);
47
+ this.logger.error(`refused an http request: ${req.connection.remoteAddress}`);
44
48
  res.sendStatus(403);
45
49
  }
46
50
  }
@@ -17,10 +17,11 @@
17
17
  import * as http from 'http';
18
18
  import * as cookie from 'cookie';
19
19
  import * as crypto from 'crypto';
20
- import { injectable, postConstruct } from 'inversify';
20
+ import { injectable, postConstruct, inject, named } from 'inversify';
21
21
  import { isObject, isString, MaybePromise } from '../../common';
22
22
  import { ElectronSecurityToken } from '../../electron-common/electron-token';
23
23
  import { WsRequestValidatorContribution } from '../../node/ws-request-validators';
24
+ import { ILogger } from '../../common/logger';
24
25
 
25
26
  /**
26
27
  * On Electron, we want to make sure that only Electron's browser-windows access the backend services.
@@ -28,6 +29,9 @@ import { WsRequestValidatorContribution } from '../../node/ws-request-validators
28
29
  @injectable()
29
30
  export class ElectronTokenValidator implements WsRequestValidatorContribution {
30
31
 
32
+ @inject(ILogger) @named('core:ElectronTokenValidator')
33
+ protected readonly logger: ILogger;
34
+
31
35
  protected electronSecurityToken?: ElectronSecurityToken;
32
36
 
33
37
  @postConstruct()
@@ -70,7 +74,7 @@ export class ElectronTokenValidator implements WsRequestValidatorContribution {
70
74
  const expected = Buffer.from(this.electronSecurityToken!.value, 'utf8');
71
75
  return received.byteLength === expected.byteLength && crypto.timingSafeEqual(received, expected);
72
76
  } catch (error) {
73
- console.error(error);
77
+ this.logger.error(error);
74
78
  }
75
79
  }
76
80
  return false;
@@ -36,7 +36,7 @@ import {
36
36
  } from '../common';
37
37
  import {
38
38
  BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution,
39
- BackendApplicationServer, BackendApplicationPath, RootContainer
39
+ BackendApplicationServer, BackendApplicationPath, EarlyExpressMiddleware, RootContainer
40
40
  } from './backend-application';
41
41
  import { CliManager, CliContribution } from './cli';
42
42
  import { IPCConnectionProvider } from './messaging';
@@ -55,7 +55,7 @@ import { ProcessUtils } from './process-utils';
55
55
  import { ProxyCliContribution } from './request/proxy-cli-contribution';
56
56
  import { bindNodeStopwatch, bindBackendStopwatchServer } from './performance';
57
57
  import { OSBackendProviderImpl } from './os-backend-provider';
58
- import { BackendRequestFacade } from './request/backend-request-facade';
58
+ import { BackendRequestFacade, BackendRequestAllowedContribution } from './request/backend-request-facade';
59
59
  import { FileSystemLocking, FileSystemLockingImpl } from './filesystem-locking';
60
60
  import { BackendRemoteService } from './remote/backend-remote-service';
61
61
  import { RemoteCliContribution } from './remote/remote-cli-contribution';
@@ -89,6 +89,7 @@ export const backendApplicationModule = new ContainerModule(bind => {
89
89
  bind(BackendApplicationCliContribution).toSelf().inSingletonScope();
90
90
  bind(CliContribution).toService(BackendApplicationCliContribution);
91
91
 
92
+ bind(EarlyExpressMiddleware).toSelf().inSingletonScope();
92
93
  bind(BackendApplication).toSelf().inSingletonScope();
93
94
  bind(RootContainer).toDynamicValue(({ container }) => {
94
95
  let root = container;
@@ -152,6 +153,7 @@ export const backendApplicationModule = new ContainerModule(bind => {
152
153
 
153
154
  bindRootContributionProvider(bind, RemoteCliContribution);
154
155
  bind(BackendRemoteService).toSelf().inSingletonScope();
156
+ bindRootContributionProvider(bind, BackendRequestAllowedContribution);
155
157
  bind(BackendRequestFacade).toSelf().inSingletonScope();
156
158
  bind(ConnectionHandler).toDynamicValue(
157
159
  ctx => new RpcConnectionHandler(REQUEST_SERVICE_PATH, () => ctx.container.get(BackendRequestFacade))
@@ -19,16 +19,17 @@ import * as sinon from 'sinon';
19
19
  import { Container, ContainerModule, injectable, preDestroy } from 'inversify';
20
20
  import { bindContributionProvider, ILogger, Stopwatch } from '../common';
21
21
  import { Deferred } from '../common/promise-util';
22
- import { MockLogger } from '../common/test/mock-logger';
23
22
  import { NodeStopwatch } from './performance/node-stopwatch';
24
23
  import { ProcessUtils } from './process-utils';
25
24
  import {
26
25
  BackendApplication,
27
26
  BackendApplicationCliContribution,
28
27
  BackendApplicationContribution,
28
+ EarlyExpressMiddleware,
29
29
  RootContainer
30
30
  } from './backend-application';
31
31
  import { CliContribution } from './cli';
32
+ import { MockLogger } from '../common/test/mock-logger';
32
33
 
33
34
  /**
34
35
  * Test subclass that exposes the protected `gracefulShutdown` for direct testing.
@@ -82,12 +83,12 @@ describe('BackendApplication', () => {
82
83
  const container = new Container();
83
84
 
84
85
  container.bind(RootContainer).toConstantValue(container);
85
-
86
86
  container.bind(ILogger).to(MockLogger).inSingletonScope();
87
87
  container.bind(Stopwatch).to(NodeStopwatch).inSingletonScope();
88
88
  container.bind(ProcessUtils).toSelf().inSingletonScope();
89
89
  container.bind(BackendApplicationCliContribution).toSelf().inSingletonScope();
90
90
  container.bind(CliContribution).toService(BackendApplicationCliContribution);
91
+ container.bind(EarlyExpressMiddleware).toSelf().inSingletonScope();
91
92
  bindContributionProvider(container, BackendApplicationContribution);
92
93
 
93
94
  container.bind(TestBackendApplication).toSelf().inSingletonScope();
@@ -242,7 +243,8 @@ describe('BackendApplication', () => {
242
243
  container.bind(BackendApplicationContribution).toConstantValue({
243
244
  onStop: () => { secondRan = true; }
244
245
  });
245
- const errorStub = sandbox.stub(console, 'error');
246
+ const mockLogger = container.get(ILogger) as ILogger;
247
+ const errorStub = sandbox.stub(mockLogger, 'error');
246
248
 
247
249
  const app = container.get(TestBackendApplication);
248
250
  await app.invokeGracefulShutdown();
@@ -28,6 +28,7 @@ import { Deferred, timeoutReject } from '../common/promise-util';
28
28
  import { environment } from '../common/index';
29
29
  import { AddressInfo } from 'net';
30
30
  import { ProcessUtils } from './process-utils';
31
+ import { ILogger } from '../common/logger';
31
32
 
32
33
  /**
33
34
  * The path to the application project directory. This is the directory where the application code is located.
@@ -53,6 +54,16 @@ const DEFAULT_HOST = 'localhost';
53
54
  const DEFAULT_SSL = false;
54
55
  const DEFAULT_DNS_DEFAULT_RESULT_ORDER: DnsResultOrder = 'ipv4first';
55
56
 
57
+ /**
58
+ * Shared registry for Express middleware that must run before static file handlers.
59
+ * Contributions push handlers during `initialize()`, and `BackendApplication`
60
+ * applies them at the start of `configure()`, before gzipped handlers and `express.static()`.
61
+ */
62
+ @injectable()
63
+ export class EarlyExpressMiddleware {
64
+ readonly handlers: express.Handler[] = [];
65
+ }
66
+
56
67
  export const BackendApplicationServer = Symbol('BackendApplicationServer');
57
68
  /**
58
69
  * This service is responsible for serving the frontend files.
@@ -174,12 +185,18 @@ export class BackendApplication {
174
185
 
175
186
  protected readonly app: express.Application = express();
176
187
 
188
+ @inject(EarlyExpressMiddleware)
189
+ protected readonly earlyMiddleware: EarlyExpressMiddleware;
190
+
177
191
  @inject(ProcessUtils)
178
192
  protected readonly processUtils: ProcessUtils;
179
193
 
180
194
  @inject(Stopwatch)
181
195
  protected readonly stopwatch: Stopwatch;
182
196
 
197
+ @inject(ILogger) @named('core:BackendApplication')
198
+ protected readonly logger: ILogger;
199
+
183
200
  @inject(RootContainer)
184
201
  protected readonly rootContainer: interfaces.Container;
185
202
 
@@ -201,7 +218,7 @@ export class BackendApplication {
201
218
  // Workaround for Electron not installing a handler to ignore SIGPIPE error
202
219
  // (https://github.com/electron/electron/issues/13254)
203
220
  process.on('SIGPIPE', () => {
204
- console.error(new Error('Unexpected SIGPIPE'));
221
+ this.logger.error(new Error('Unexpected SIGPIPE'));
205
222
  });
206
223
 
207
224
  // Handles normal process termination.
@@ -221,7 +238,7 @@ export class BackendApplication {
221
238
  await this.measureContribution(contribution, 'initialize',
222
239
  () => contribution.initialize!());
223
240
  } catch (error) {
224
- console.error('Could not initialize contribution', error);
241
+ this.logger.error('Could not initialize contribution', error);
225
242
  }
226
243
  }
227
244
  }));
@@ -240,6 +257,11 @@ export class BackendApplication {
240
257
  protected async configure(): Promise<void> {
241
258
  await this.initialize();
242
259
 
260
+ // Apply early middleware before static file handlers.
261
+ for (const handler of this.earlyMiddleware.handlers) {
262
+ this.app.use(handler);
263
+ }
264
+
243
265
  this.app.get('*.js', this.serveGzipped.bind(this, 'text/javascript'));
244
266
  this.app.get('*.js.map', this.serveGzipped.bind(this, 'application/json'));
245
267
  this.app.get('*.css', this.serveGzipped.bind(this, 'text/css'));
@@ -258,11 +280,11 @@ export class BackendApplication {
258
280
  await this.measureContribution(contribution, 'configure',
259
281
  () => contribution.configure!(this.app));
260
282
  } catch (error) {
261
- console.error('Could not configure contribution', error);
283
+ this.logger.error('Could not configure contribution', error);
262
284
  }
263
285
  }
264
286
  }));
265
- console.info('configured all backend app contributions');
287
+ this.logger.info('configured all backend app contributions');
266
288
  }
267
289
 
268
290
  use(...handlers: express.Handler[]): void {
@@ -297,14 +319,14 @@ export class BackendApplication {
297
319
  try {
298
320
  key = await fs.readFile(this.cliParams.certkey as string);
299
321
  } catch (err) {
300
- console.error("Can't read certificate key");
322
+ this.logger.error("Can't read certificate key");
301
323
  throw err;
302
324
  }
303
325
 
304
326
  try {
305
327
  cert = await fs.readFile(this.cliParams.cert as string);
306
328
  } catch (err) {
307
- console.error("Can't read certificate");
329
+ this.logger.error("Can't read certificate");
308
330
  throw err;
309
331
  }
310
332
  server = https.createServer({ key, cert }, this.app);
@@ -323,7 +345,7 @@ export class BackendApplication {
323
345
  // address should be defined at this point
324
346
  const address = server.address()!;
325
347
  const url = typeof address === 'string' ? address : this.getHttpUrl(address, this.cliParams.ssl);
326
- console.info(`Theia app listening on ${url}.`);
348
+ this.logger.info(`Theia app listening on ${url}.`);
327
349
  deferred.resolve(server);
328
350
  });
329
351
 
@@ -336,7 +358,7 @@ export class BackendApplication {
336
358
  await this.measureContribution(contribution, 'onStart',
337
359
  () => contribution.onStart!(server));
338
360
  } catch (error) {
339
- console.error('Could not start contribution', error);
361
+ this.logger.error('Could not start contribution', error);
340
362
  }
341
363
  }
342
364
  }
@@ -415,7 +437,7 @@ export class BackendApplication {
415
437
  try {
416
438
  await contrib.onStop(this.app);
417
439
  } catch (error) {
418
- console.error('Could not stop contribution', error);
440
+ this.logger.error('Could not stop contribution', error);
419
441
  }
420
442
  }
421
443
  }));
@@ -466,9 +488,9 @@ export class BackendApplication {
466
488
 
467
489
  protected handleUncaughtError(error: Error): void {
468
490
  if (error) {
469
- console.error('Uncaught Exception: ', error.toString());
491
+ this.logger.error('Uncaught Exception: ', error.toString());
470
492
  if (error.stack) {
471
- console.error(error.stack);
493
+ this.logger.error(error.stack);
472
494
  }
473
495
  }
474
496
  }
@@ -15,12 +15,22 @@
15
15
  // *****************************************************************************
16
16
 
17
17
  import { ContainerModule } from 'inversify';
18
+ import { BackendApplicationContribution } from '../backend-application';
18
19
  import { WsRequestValidatorContribution } from '../ws-request-validators';
19
20
  import { BackendApplicationHosts } from './backend-application-hosts';
21
+ import {
22
+ BrowserConnectionToken, BrowserConnectionTokenBackendContribution, createBrowserConnectionToken
23
+ } from './browser-connection-token';
20
24
  import { WsOriginValidator } from './ws-origin-validator';
21
25
 
22
26
  export default new ContainerModule(bind => {
23
27
  bind(BackendApplicationHosts).toSelf().inSingletonScope();
24
28
  bind(WsOriginValidator).toSelf().inSingletonScope();
25
29
  bind(WsRequestValidatorContribution).toService(WsOriginValidator);
30
+
31
+ // Cookie-based connection token protects both HTTP and WebSocket endpoints.
32
+ bind(BrowserConnectionToken).toConstantValue(createBrowserConnectionToken());
33
+ bind(BrowserConnectionTokenBackendContribution).toSelf().inSingletonScope();
34
+ bind(BackendApplicationContribution).toService(BrowserConnectionTokenBackendContribution);
35
+ bind(WsRequestValidatorContribution).toService(BrowserConnectionTokenBackendContribution);
26
36
  });