init.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
  2. import type * as ipcRendererInternalModule from '@electron/internal/renderer/ipc-renderer-internal';
  3. import type * as ipcRendererUtilsModule from '@electron/internal/renderer/ipc-renderer-internal-utils';
  4. import * as path from 'path';
  5. import { pathToFileURL } from 'url';
  6. const Module = require('module') as NodeJS.ModuleInternal;
  7. // We do not want to allow use of the VM module in the renderer process as
  8. // it conflicts with Blink's V8::Context internal logic.
  9. const originalModuleLoad = Module._load;
  10. Module._load = function (request: string) {
  11. if (request === 'vm') {
  12. console.warn('The vm module of Node.js is unsupported in Electron\'s renderer process due to incompatibilities with the Blink rendering engine. Crashes are likely and avoiding the module is highly recommended. This module may be removed in a future release.');
  13. }
  14. return originalModuleLoad.apply(this, arguments as any);
  15. };
  16. // Make sure globals like "process" and "global" are always available in preload
  17. // scripts even after they are deleted in "loaded" script.
  18. //
  19. // Note 1: We rely on a Node patch to actually pass "process" and "global" and
  20. // other arguments to the wrapper.
  21. //
  22. // Note 2: Node introduced a new code path to use native code to wrap module
  23. // code, which does not work with this hack. However by modifying the
  24. // "Module.wrapper" we can force Node to use the old code path to wrap module
  25. // code with JavaScript.
  26. //
  27. // Note 3: We provide the equivalent extra variables internally through the
  28. // webpack ProvidePlugin in webpack.config.base.js. If you add any extra
  29. // variables to this wrapper please ensure to update that plugin as well.
  30. Module.wrapper = [
  31. '(function (exports, require, module, __filename, __dirname, process, global, Buffer) { ' +
  32. // By running the code in a new closure, it would be possible for the module
  33. // code to override "process" and "Buffer" with local variables.
  34. 'return function (exports, require, module, __filename, __dirname) { ',
  35. '\n}.call(this, exports, require, module, __filename, __dirname); });'
  36. ];
  37. // We modified the original process.argv to let node.js load the
  38. // init.js, we need to restore it here.
  39. process.argv.splice(1, 1);
  40. // Import common settings.
  41. require('@electron/internal/common/init');
  42. const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') as typeof ipcRendererInternalModule;
  43. const ipcRendererUtils = require('@electron/internal/renderer/ipc-renderer-internal-utils') as typeof ipcRendererUtilsModule;
  44. process.getProcessMemoryInfo = () => {
  45. return ipcRendererInternal.invoke<Electron.ProcessMemoryInfo>(IPC_MESSAGES.BROWSER_GET_PROCESS_MEMORY_INFO);
  46. };
  47. // Process command line arguments.
  48. const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_command_line');
  49. const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
  50. const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
  51. const appPath = hasSwitch('app-path') ? getSwitchValue('app-path') : null;
  52. // Common renderer initialization
  53. require('@electron/internal/renderer/common-init');
  54. if (nodeIntegration) {
  55. // Export node bindings to global.
  56. const { makeRequireFunction } = __non_webpack_require__('internal/modules/helpers');
  57. global.module = new Module('electron/js2c/renderer_init');
  58. global.require = makeRequireFunction(global.module);
  59. // Set the __filename to the path of html file if it is file: protocol.
  60. if (window.location.protocol === 'file:') {
  61. const location = window.location;
  62. let pathname = location.pathname;
  63. if (process.platform === 'win32') {
  64. if (pathname[0] === '/') pathname = pathname.substr(1);
  65. const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\');
  66. if (isWindowsNetworkSharePath) {
  67. pathname = `//${location.host}/${pathname}`;
  68. }
  69. }
  70. global.__filename = path.normalize(decodeURIComponent(pathname));
  71. global.__dirname = path.dirname(global.__filename);
  72. // Set module's filename so relative require can work as expected.
  73. global.module.filename = global.__filename;
  74. // Also search for module under the html file.
  75. global.module.paths = Module._nodeModulePaths(global.__dirname);
  76. } else {
  77. // For backwards compatibility we fake these two paths here
  78. global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js');
  79. global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer');
  80. if (appPath) {
  81. // Search for module under the app directory
  82. global.module.paths = Module._nodeModulePaths(appPath);
  83. }
  84. }
  85. // Redirect window.onerror to uncaughtException.
  86. window.onerror = function (_message, _filename, _lineno, _colno, error) {
  87. if (global.process.listenerCount('uncaughtException') > 0) {
  88. // We do not want to add `uncaughtException` to our definitions
  89. // because we don't want anyone else (anywhere) to throw that kind
  90. // of error.
  91. global.process.emit('uncaughtException', error as any);
  92. return true;
  93. } else {
  94. return false;
  95. }
  96. };
  97. } else {
  98. // Delete Node's symbols after the Environment has been loaded in a
  99. // non context-isolated environment
  100. if (!process.contextIsolated) {
  101. process.once('loaded', function () {
  102. delete (global as any).process;
  103. delete (global as any).Buffer;
  104. delete (global as any).setImmediate;
  105. delete (global as any).clearImmediate;
  106. delete (global as any).global;
  107. delete (global as any).root;
  108. delete (global as any).GLOBAL;
  109. });
  110. }
  111. }
  112. const { appCodeLoaded } = process;
  113. delete process.appCodeLoaded;
  114. const { preloadPaths } = ipcRendererUtils.invokeSync<{ preloadPaths: string[] }>(IPC_MESSAGES.BROWSER_NONSANDBOX_LOAD);
  115. const cjsPreloads = preloadPaths.filter(p => path.extname(p) !== '.mjs');
  116. const esmPreloads = preloadPaths.filter(p => path.extname(p) === '.mjs');
  117. if (cjsPreloads.length) {
  118. // Load the preload scripts.
  119. for (const preloadScript of cjsPreloads) {
  120. try {
  121. Module._load(preloadScript);
  122. } catch (error) {
  123. console.error(`Unable to load preload script: ${preloadScript}`);
  124. console.error(error);
  125. ipcRendererInternal.send(IPC_MESSAGES.BROWSER_PRELOAD_ERROR, preloadScript, error);
  126. }
  127. }
  128. }
  129. if (esmPreloads.length) {
  130. const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
  131. runEntryPointWithESMLoader(async (cascadedLoader: any) => {
  132. // Load the preload scripts.
  133. for (const preloadScript of esmPreloads) {
  134. await cascadedLoader.import(pathToFileURL(preloadScript).toString(), undefined, Object.create(null)).catch((err: Error) => {
  135. console.error(`Unable to load preload script: ${preloadScript}`);
  136. console.error(err);
  137. ipcRendererInternal.send(IPC_MESSAGES.BROWSER_PRELOAD_ERROR, preloadScript, err);
  138. });
  139. }
  140. }).finally(() => appCodeLoaded!());
  141. } else {
  142. appCodeLoaded!();
  143. }