init.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import * as path from 'path';
  2. import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
  3. import type * as ipcRendererInternalModule from '@electron/internal/renderer/ipc-renderer-internal';
  4. import type * as webFrameInitModule from '@electron/internal/renderer/web-frame-init';
  5. import type * as webViewInitModule from '@electron/internal/renderer/web-view/web-view-init';
  6. import type * as windowSetupModule from '@electron/internal/renderer/window-setup';
  7. import type * as securityWarningsModule from '@electron/internal/renderer/security-warnings';
  8. const Module = require('module');
  9. // Make sure globals like "process" and "global" are always available in preload
  10. // scripts even after they are deleted in "loaded" script.
  11. //
  12. // Note 1: We rely on a Node patch to actually pass "process" and "global" and
  13. // other arguments to the wrapper.
  14. //
  15. // Note 2: Node introduced a new code path to use native code to wrap module
  16. // code, which does not work with this hack. However by modifying the
  17. // "Module.wrapper" we can force Node to use the old code path to wrap module
  18. // code with JavaScript.
  19. //
  20. // Note 3: We provide the equivalent extra variables internally through the
  21. // webpack ProvidePlugin in webpack.config.base.js. If you add any extra
  22. // variables to this wrapper please ensure to update that plugin as well.
  23. Module.wrapper = [
  24. '(function (exports, require, module, __filename, __dirname, process, global, Buffer) { ' +
  25. // By running the code in a new closure, it would be possible for the module
  26. // code to override "process" and "Buffer" with local variables.
  27. 'return function (exports, require, module, __filename, __dirname) { ',
  28. '\n}.call(this, exports, require, module, __filename, __dirname); });'
  29. ];
  30. // We modified the original process.argv to let node.js load the
  31. // init.js, we need to restore it here.
  32. process.argv.splice(1, 1);
  33. // Clear search paths.
  34. require('../common/reset-search-paths');
  35. // Import common settings.
  36. require('@electron/internal/common/init');
  37. // The global variable will be used by ipc for event dispatching
  38. const v8Util = process._linkedBinding('electron_common_v8_util');
  39. const { ipcRendererInternal } = require('@electron/internal/renderer/ipc-renderer-internal') as typeof ipcRendererInternalModule;
  40. const ipcRenderer = require('@electron/internal/renderer/api/ipc-renderer').default;
  41. v8Util.setHiddenValue(global, 'ipcNative', {
  42. onMessage (internal: boolean, channel: string, ports: any[], args: any[], senderId: number) {
  43. if (internal && senderId !== 0) {
  44. console.error(`Message ${channel} sent by unexpected WebContents (${senderId})`);
  45. return;
  46. }
  47. const sender = internal ? ipcRendererInternal : ipcRenderer;
  48. sender.emit(channel, { sender, senderId, ports }, ...args);
  49. }
  50. });
  51. process.getProcessMemoryInfo = () => {
  52. return ipcRendererInternal.invoke<Electron.ProcessMemoryInfo>(IPC_MESSAGES.BROWSER_GET_PROCESS_MEMORY_INFO);
  53. };
  54. // Use electron module after everything is ready.
  55. const { webFrameInit } = require('@electron/internal/renderer/web-frame-init') as typeof webFrameInitModule;
  56. webFrameInit();
  57. // Process command line arguments.
  58. const { hasSwitch, getSwitchValue } = process._linkedBinding('electron_common_command_line');
  59. const { mainFrame } = process._linkedBinding('electron_renderer_web_frame');
  60. const contextIsolation = mainFrame.getWebPreference('contextIsolation');
  61. const nodeIntegration = mainFrame.getWebPreference('nodeIntegration');
  62. const webviewTag = mainFrame.getWebPreference('webviewTag');
  63. const isHiddenPage = mainFrame.getWebPreference('hiddenPage');
  64. const usesNativeWindowOpen = mainFrame.getWebPreference('nativeWindowOpen');
  65. const preloadScript = mainFrame.getWebPreference('preload');
  66. const preloadScripts = mainFrame.getWebPreference('preloadScripts');
  67. const isWebView = mainFrame.getWebPreference('isWebView');
  68. const openerId = mainFrame.getWebPreference('openerId');
  69. const appPath = hasSwitch('app-path') ? getSwitchValue('app-path') : null;
  70. // The webContents preload script is loaded after the session preload scripts.
  71. if (preloadScript) {
  72. preloadScripts.push(preloadScript);
  73. }
  74. switch (window.location.protocol) {
  75. case 'devtools:': {
  76. // Override some inspector APIs.
  77. require('@electron/internal/renderer/inspector');
  78. break;
  79. }
  80. case 'chrome-extension:': {
  81. break;
  82. }
  83. case 'chrome:': {
  84. break;
  85. }
  86. default: {
  87. // Override default web functions.
  88. const { windowSetup } = require('@electron/internal/renderer/window-setup') as typeof windowSetupModule;
  89. windowSetup(isWebView, openerId, isHiddenPage, usesNativeWindowOpen);
  90. }
  91. }
  92. // Load webview tag implementation.
  93. if (process.isMainFrame) {
  94. const { webViewInit } = require('@electron/internal/renderer/web-view/web-view-init') as typeof webViewInitModule;
  95. webViewInit(contextIsolation, webviewTag, isWebView);
  96. }
  97. if (nodeIntegration) {
  98. // Export node bindings to global.
  99. const { makeRequireFunction } = __non_webpack_require__('internal/modules/cjs/helpers') // eslint-disable-line
  100. global.module = new Module('electron/js2c/renderer_init');
  101. global.require = makeRequireFunction(global.module);
  102. // Set the __filename to the path of html file if it is file: protocol.
  103. if (window.location.protocol === 'file:') {
  104. const location = window.location;
  105. let pathname = location.pathname;
  106. if (process.platform === 'win32') {
  107. if (pathname[0] === '/') pathname = pathname.substr(1);
  108. const isWindowsNetworkSharePath = location.hostname.length > 0 && process.resourcesPath.startsWith('\\');
  109. if (isWindowsNetworkSharePath) {
  110. pathname = `//${location.host}/${pathname}`;
  111. }
  112. }
  113. global.__filename = path.normalize(decodeURIComponent(pathname));
  114. global.__dirname = path.dirname(global.__filename);
  115. // Set module's filename so relative require can work as expected.
  116. global.module.filename = global.__filename;
  117. // Also search for module under the html file.
  118. global.module.paths = Module._nodeModulePaths(global.__dirname);
  119. } else {
  120. // For backwards compatibility we fake these two paths here
  121. global.__filename = path.join(process.resourcesPath, 'electron.asar', 'renderer', 'init.js');
  122. global.__dirname = path.join(process.resourcesPath, 'electron.asar', 'renderer');
  123. if (appPath) {
  124. // Search for module under the app directory
  125. global.module.paths = Module._nodeModulePaths(appPath);
  126. }
  127. }
  128. // Redirect window.onerror to uncaughtException.
  129. window.onerror = function (_message, _filename, _lineno, _colno, error) {
  130. if (global.process.listenerCount('uncaughtException') > 0) {
  131. // We do not want to add `uncaughtException` to our definitions
  132. // because we don't want anyone else (anywhere) to throw that kind
  133. // of error.
  134. global.process.emit('uncaughtException', error as any);
  135. return true;
  136. } else {
  137. return false;
  138. }
  139. };
  140. } else {
  141. // Delete Node's symbols after the Environment has been loaded in a
  142. // non context-isolated environment
  143. if (!contextIsolation) {
  144. process.once('loaded', function () {
  145. delete (global as any).process;
  146. delete (global as any).Buffer;
  147. delete (global as any).setImmediate;
  148. delete (global as any).clearImmediate;
  149. delete (global as any).global;
  150. delete (global as any).root;
  151. delete (global as any).GLOBAL;
  152. });
  153. }
  154. }
  155. // Load the preload scripts.
  156. for (const preloadScript of preloadScripts) {
  157. try {
  158. Module._load(preloadScript);
  159. } catch (error) {
  160. console.error(`Unable to load preload script: ${preloadScript}`);
  161. console.error(error);
  162. ipcRendererInternal.send(IPC_MESSAGES.BROWSER_PRELOAD_ERROR, preloadScript, error);
  163. }
  164. }
  165. // Warn about security issues
  166. if (process.isMainFrame) {
  167. const { securityWarnings } = require('@electron/internal/renderer/security-warnings') as typeof securityWarningsModule;
  168. securityWarnings(nodeIntegration);
  169. }