init.ts 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import { EventEmitter } from 'events';
  2. import * as fs from 'fs';
  3. import * as path from 'path';
  4. import type * as defaultMenuModule from '@electron/internal/browser/default-menu';
  5. const Module = require('module') as NodeJS.ModuleInternal;
  6. // We modified the original process.argv to let node.js load the init.js,
  7. // we need to restore it here.
  8. process.argv.splice(1, 1);
  9. // Clear search paths.
  10. require('../common/reset-search-paths');
  11. // Import common settings.
  12. require('@electron/internal/common/init');
  13. process._linkedBinding('electron_browser_event_emitter').setEventEmitterPrototype(EventEmitter.prototype);
  14. // Don't quit on fatal error.
  15. process.on('uncaughtException', function (error) {
  16. // Do nothing if the user has a custom uncaught exception handler.
  17. if (process.listenerCount('uncaughtException') > 1) {
  18. return;
  19. }
  20. // Show error in GUI.
  21. // We can't import { dialog } at the top of this file as this file is
  22. // responsible for setting up the require hook for the "electron" module
  23. // so we import it inside the handler down here
  24. import('electron')
  25. .then(({ dialog }) => {
  26. const stack = error.stack ? error.stack : `${error.name}: ${error.message}`;
  27. const message = 'Uncaught Exception:\n' + stack;
  28. dialog.showErrorBox('A JavaScript error occurred in the main process', message);
  29. });
  30. });
  31. // Emit 'exit' event on quit.
  32. const { app } = require('electron');
  33. app.on('quit', (_event, exitCode) => {
  34. process.emit('exit', exitCode);
  35. });
  36. if (process.platform === 'win32') {
  37. // If we are a Squirrel.Windows-installed app, set app user model ID
  38. // so that users don't have to do this.
  39. //
  40. // Squirrel packages are always of the form:
  41. //
  42. // PACKAGE-NAME
  43. // - Update.exe
  44. // - app-VERSION
  45. // - OUREXE.exe
  46. //
  47. // Squirrel itself will always set the shortcut's App User Model ID to the
  48. // form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
  49. // app.setAppUserModelId with a matching identifier so that renderer processes
  50. // will inherit this value.
  51. const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe');
  52. if (fs.existsSync(updateDotExe)) {
  53. const packageDir = path.dirname(path.resolve(updateDotExe));
  54. const packageName = path.basename(packageDir).replaceAll(/\s/g, '');
  55. const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replaceAll(/\s/g, '');
  56. app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`);
  57. }
  58. }
  59. // Map process.exit to app.exit, which quits gracefully.
  60. process.exit = app.exit as () => never;
  61. // Load the RPC server.
  62. require('@electron/internal/browser/rpc-server');
  63. // Load the guest view manager.
  64. require('@electron/internal/browser/guest-view-manager');
  65. // Now we try to load app's package.json.
  66. const v8Util = process._linkedBinding('electron_common_v8_util');
  67. let packagePath = null;
  68. let packageJson = null;
  69. const searchPaths: string[] = v8Util.getHiddenValue(global, 'appSearchPaths');
  70. const searchPathsOnlyLoadASAR: boolean = v8Util.getHiddenValue(global, 'appSearchPathsOnlyLoadASAR');
  71. // Borrow the _getOrCreateArchive asar helper
  72. const getOrCreateArchive = process._getOrCreateArchive;
  73. delete process._getOrCreateArchive;
  74. if (process.resourcesPath) {
  75. for (packagePath of searchPaths) {
  76. try {
  77. packagePath = path.join(process.resourcesPath, packagePath);
  78. if (searchPathsOnlyLoadASAR) {
  79. if (!getOrCreateArchive?.(packagePath)) {
  80. continue;
  81. }
  82. }
  83. packageJson = Module._load(path.join(packagePath, 'package.json'));
  84. break;
  85. } catch {
  86. continue;
  87. }
  88. }
  89. }
  90. if (packageJson == null) {
  91. process.nextTick(function () {
  92. return process.exit(1);
  93. });
  94. throw new Error('Unable to find a valid app');
  95. }
  96. // Set application's version.
  97. if (packageJson.version != null) {
  98. app.setVersion(packageJson.version);
  99. }
  100. // Set application's name.
  101. if (packageJson.productName != null) {
  102. app.name = `${packageJson.productName}`.trim();
  103. } else if (packageJson.name != null) {
  104. app.name = `${packageJson.name}`.trim();
  105. }
  106. // Set application's desktop name.
  107. if (packageJson.desktopName != null) {
  108. app.setDesktopName(packageJson.desktopName);
  109. } else {
  110. app.setDesktopName(`${app.name}.desktop`);
  111. }
  112. // Set v8 flags, deliberately lazy load so that apps that do not use this
  113. // feature do not pay the price
  114. if (packageJson.v8Flags != null) {
  115. require('v8').setFlagsFromString(packageJson.v8Flags);
  116. }
  117. app.setAppPath(packagePath);
  118. // Load the chrome devtools support.
  119. require('@electron/internal/browser/devtools');
  120. // Load protocol module to ensure it is populated on app ready
  121. require('@electron/internal/browser/api/protocol');
  122. // Load web-contents module to ensure it is populated on app ready
  123. require('@electron/internal/browser/api/web-contents');
  124. // Load web-frame-main module to ensure it is populated on app ready
  125. require('@electron/internal/browser/api/web-frame-main');
  126. // Set main startup script of the app.
  127. const mainStartupScript = packageJson.main || 'index.js';
  128. const KNOWN_XDG_DESKTOP_VALUES = new Set(['Pantheon', 'Unity:Unity7', 'pop:GNOME']);
  129. function currentPlatformSupportsAppIndicator () {
  130. if (process.platform !== 'linux') return false;
  131. const currentDesktop = process.env.XDG_CURRENT_DESKTOP;
  132. if (!currentDesktop) return false;
  133. if (KNOWN_XDG_DESKTOP_VALUES.has(currentDesktop)) return true;
  134. // ubuntu based or derived session (default ubuntu one, communitheme…) supports
  135. // indicator too.
  136. if (/ubuntu/ig.test(currentDesktop)) return true;
  137. return false;
  138. }
  139. // Workaround for electron/electron#5050 and electron/electron#9046
  140. process.env.ORIGINAL_XDG_CURRENT_DESKTOP = process.env.XDG_CURRENT_DESKTOP;
  141. if (currentPlatformSupportsAppIndicator()) {
  142. process.env.XDG_CURRENT_DESKTOP = 'Unity';
  143. }
  144. // Quit when all windows are closed and no other one is listening to this.
  145. app.on('window-all-closed', () => {
  146. if (app.listenerCount('window-all-closed') === 1) {
  147. app.quit();
  148. }
  149. });
  150. const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu') as typeof defaultMenuModule;
  151. // Create default menu.
  152. //
  153. // The |will-finish-launching| event is emitted before |ready| event, so default
  154. // menu is set before any user window is created.
  155. app.once('will-finish-launching', setDefaultApplicationMenu);
  156. const { appCodeLoaded } = process;
  157. delete process.appCodeLoaded;
  158. if (packagePath) {
  159. // Finally load app's main.js and transfer control to C++.
  160. if ((packageJson.type === 'module' && !mainStartupScript.endsWith('.cjs')) || mainStartupScript.endsWith('.mjs')) {
  161. const { loadESM } = __non_webpack_require__('internal/process/esm_loader');
  162. const main = require('url').pathToFileURL(path.join(packagePath, mainStartupScript));
  163. loadESM(async (esmLoader: any) => {
  164. try {
  165. await esmLoader.import(main.toString(), undefined, Object.create(null));
  166. appCodeLoaded!();
  167. } catch (err) {
  168. appCodeLoaded!();
  169. process.emit('uncaughtException', err as Error);
  170. }
  171. });
  172. } else {
  173. // Call appCodeLoaded before just for safety, it doesn't matter here as _load is syncronous
  174. appCodeLoaded!();
  175. process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false);
  176. Module._load(path.join(packagePath, mainStartupScript), Module, true);
  177. }
  178. } else {
  179. console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)');
  180. console.error('This normally means you\'ve damaged the Electron package somehow');
  181. appCodeLoaded!();
  182. }