devtools.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import { dialog, Menu } from 'electron';
  2. import * as fs from 'fs';
  3. import * as url from 'url';
  4. import { ipcMainInternal } from '@electron/internal/browser/ipc-main-internal';
  5. import * as ipcMainUtils from '@electron/internal/browser/ipc-main-internal-utils';
  6. const convertToMenuTemplate = function (items: ContextMenuItem[], handler: (id: number) => void) {
  7. return items.map(function (item) {
  8. const transformed: Electron.MenuItemConstructorOptions = item.type === 'subMenu' ? {
  9. type: 'submenu',
  10. label: item.label,
  11. enabled: item.enabled,
  12. submenu: convertToMenuTemplate(item.subItems, handler)
  13. } : item.type === 'separator' ? {
  14. type: 'separator'
  15. } : item.type === 'checkbox' ? {
  16. type: 'checkbox',
  17. label: item.label,
  18. enabled: item.enabled,
  19. checked: item.checked
  20. } : {
  21. type: 'normal',
  22. label: item.label,
  23. enabled: item.enabled
  24. };
  25. if (item.id != null) {
  26. transformed.click = () => handler(item.id);
  27. }
  28. return transformed;
  29. });
  30. };
  31. const getEditMenuItems = function (): Electron.MenuItemConstructorOptions[] {
  32. return [
  33. { role: 'undo' },
  34. { role: 'redo' },
  35. { type: 'separator' },
  36. { role: 'cut' },
  37. { role: 'copy' },
  38. { role: 'paste' },
  39. { role: 'pasteAndMatchStyle' },
  40. { role: 'delete' },
  41. { role: 'selectAll' }
  42. ];
  43. };
  44. const isChromeDevTools = function (pageURL: string) {
  45. const { protocol } = url.parse(pageURL);
  46. return protocol === 'devtools:';
  47. };
  48. const assertChromeDevTools = function (contents: Electron.WebContents, api: string) {
  49. const pageURL = contents._getURL();
  50. if (!isChromeDevTools(pageURL)) {
  51. console.error(`Blocked ${pageURL} from calling ${api}`);
  52. throw new Error(`Blocked ${api}`);
  53. }
  54. };
  55. ipcMainInternal.handle('ELECTRON_INSPECTOR_CONTEXT_MENU', function (event: Electron.IpcMainInvokeEvent, items: ContextMenuItem[], isEditMenu: boolean) {
  56. return new Promise(resolve => {
  57. assertChromeDevTools(event.sender, 'window.InspectorFrontendHost.showContextMenuAtPoint()');
  58. const template = isEditMenu ? getEditMenuItems() : convertToMenuTemplate(items, resolve);
  59. const menu = Menu.buildFromTemplate(template);
  60. const window = event.sender.getOwnerBrowserWindow();
  61. menu.popup({ window, callback: () => resolve() });
  62. });
  63. });
  64. ipcMainInternal.handle('ELECTRON_INSPECTOR_SELECT_FILE', async function (event: Electron.IpcMainInvokeEvent) {
  65. assertChromeDevTools(event.sender, 'window.UI.createFileSelectorElement()');
  66. const result = await dialog.showOpenDialog({});
  67. if (result.canceled) return [];
  68. const path = result.filePaths[0];
  69. const data = await fs.promises.readFile(path);
  70. return [path, data];
  71. });
  72. ipcMainUtils.handleSync('ELECTRON_INSPECTOR_CONFIRM', async function (event: Electron.IpcMainInvokeEvent, message: string = '', title: string = '') {
  73. assertChromeDevTools(event.sender, 'window.confirm()');
  74. const options = {
  75. message: String(message),
  76. title: String(title),
  77. buttons: ['OK', 'Cancel'],
  78. cancelId: 1
  79. };
  80. const window = event.sender.getOwnerBrowserWindow();
  81. const { response } = await dialog.showMessageBox(window, options);
  82. return response === 0;
  83. });