init.ts 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import * as path from 'path';
  2. import { pathToFileURL } from 'url';
  3. import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
  4. import type * as ipcRendererInternalModule from '@electron/internal/renderer/ipc-renderer-internal';
  5. import type * as ipcRendererUtilsModule from '@electron/internal/renderer/ipc-renderer-internal-utils';
  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 deprecated in the renderer process and will be removed.');
  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 { loadESM } = __non_webpack_require__('internal/process/esm_loader');
  131. loadESM(async (esmLoader: any) => {
  132. // Load the preload scripts.
  133. for (const preloadScript of esmPreloads) {
  134. await esmLoader.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. }