preload.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { IPC_MESSAGES } from '@electron/internal/common/ipc-messages';
  2. import { ipcRendererInternal } from '@electron/internal/renderer/ipc-renderer-internal';
  3. import { EventEmitter } from 'events';
  4. interface PreloadContext {
  5. loadedModules: Map<string, any>;
  6. loadableModules: Map<string, any>;
  7. /** Process object to pass into preloads. */
  8. process: NodeJS.Process;
  9. createPreloadScript: (src: string) => Function
  10. /** Globals to be exposed to preload context. */
  11. exposeGlobals: any;
  12. }
  13. export function createPreloadProcessObject (): NodeJS.Process {
  14. const preloadProcess: NodeJS.Process = new EventEmitter() as any;
  15. preloadProcess.getProcessMemoryInfo = () => {
  16. return ipcRendererInternal.invoke<Electron.ProcessMemoryInfo>(IPC_MESSAGES.BROWSER_GET_PROCESS_MEMORY_INFO);
  17. };
  18. Object.defineProperty(preloadProcess, 'noDeprecation', {
  19. get () {
  20. return process.noDeprecation;
  21. },
  22. set (value) {
  23. process.noDeprecation = value;
  24. }
  25. });
  26. const { hasSwitch } = process._linkedBinding('electron_common_command_line');
  27. // Similar to nodes --expose-internals flag, this exposes _linkedBinding so
  28. // that tests can call it to get access to some test only bindings
  29. if (hasSwitch('unsafely-expose-electron-internals-for-testing')) {
  30. preloadProcess._linkedBinding = process._linkedBinding;
  31. }
  32. return preloadProcess;
  33. }
  34. // This is the `require` function that will be visible to the preload script
  35. function preloadRequire (context: PreloadContext, module: string) {
  36. if (context.loadedModules.has(module)) {
  37. return context.loadedModules.get(module);
  38. }
  39. if (context.loadableModules.has(module)) {
  40. const loadedModule = context.loadableModules.get(module)!();
  41. context.loadedModules.set(module, loadedModule);
  42. return loadedModule;
  43. }
  44. throw new Error(`module not found: ${module}`);
  45. }
  46. // Wrap the script into a function executed in global scope. It won't have
  47. // access to the current scope, so we'll expose a few objects as arguments:
  48. //
  49. // - `require`: The `preloadRequire` function
  50. // - `process`: The `preloadProcess` object
  51. // - `Buffer`: Shim of `Buffer` implementation
  52. // - `global`: The window object, which is aliased to `global` by webpack.
  53. function runPreloadScript (context: PreloadContext, preloadSrc: string) {
  54. const globalVariables = [];
  55. const fnParameters = [];
  56. for (const [key, value] of Object.entries(context.exposeGlobals)) {
  57. globalVariables.push(key);
  58. fnParameters.push(value);
  59. }
  60. const preloadWrapperSrc = `(function(require, process, exports, module, ${globalVariables.join(', ')}) {
  61. ${preloadSrc}
  62. })`;
  63. // eval in window scope
  64. const preloadFn = context.createPreloadScript(preloadWrapperSrc);
  65. const exports = {};
  66. preloadFn(preloadRequire.bind(null, context), context.process, exports, { exports }, ...fnParameters);
  67. }
  68. /**
  69. * Execute preload scripts within a sandboxed process.
  70. */
  71. export function executeSandboxedPreloadScripts (context: PreloadContext, preloadScripts: ElectronInternal.PreloadScript[]) {
  72. for (const { filePath, contents, error } of preloadScripts) {
  73. try {
  74. if (contents) {
  75. runPreloadScript(context, contents);
  76. } else if (error) {
  77. throw error;
  78. }
  79. } catch (error) {
  80. console.error(`Unable to load preload script: ${filePath}`);
  81. console.error(error);
  82. ipcRendererInternal.send(IPC_MESSAGES.BROWSER_PRELOAD_ERROR, filePath, error);
  83. }
  84. }
  85. }