/
githubmirror
/
atom
Обзор
Документация
Войти
/
githubmirror
/
atom
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/main-process/atom-window.js
539 строк
15 KB
steven nguyen
Remove `if (reason === crashed)`
27 окт 2021, 04:43
Не верифицирован
27 окт 2021, 04:43
b903148
Код
Авторство
О чём код?
const { BrowserWindow, app, dialog, ipcMain, nativeImage } = require('electron'); const getAppName = require('../get-app-name'); const path = require('path'); const url = require('url'); const { EventEmitter } = require('events'); const StartupTime = require('../startup-time'); const ICON_PATH = path.resolve(__dirname, '..', '..', 'resources', 'atom.png'); let includeShellLoadTime = true; let nextId = 0; module.exports = class AtomWindow extends EventEmitter { constructor(atomApplication, fileRecoveryService, settings = {}) { StartupTime.addMarker('main-process:atom-window:start'); super(); this.id = nextId++; this.atomApplication = atomApplication; this.fileRecoveryService = fileRecoveryService; this.isSpec = settings.isSpec; this.headless = settings.headless; this.safeMode = settings.safeMode; this.devMode = settings.devMode; this.resourcePath = settings.resourcePath; const locationsToOpen = settings.locationsToOpen || []; this.loadedPromise = new Promise(resolve => { this.resolveLoadedPromise = resolve; }); this.closedPromise = new Promise(resolve => { this.resolveClosedPromise = resolve; }); const options = { show: false, title: getAppName(), tabbingIdentifier: 'atom', webPreferences: { // Prevent specs from throttling when the window is in the background: // this should result in faster CI builds, and an improvement in the // local development experience when running specs through the UI (which // now won't pause when e.g. minimizing the window). backgroundThrottling: !this.isSpec, // Disable the `auxclick` feature so that `click` events are triggered in // response to a middle-click. // (Ref: https://github.com/atom/atom/pull/12696#issuecomment-290496960) disableBlinkFeatures: 'Auxclick', nodeIntegration: true, webviewTag: true, // TodoElectronIssue: remote module is deprecated https://www.electronjs.org/docs/breaking-changes#default-changed-enableremotemodule-defaults-to-false enableRemoteModule: true, // node support in threads nodeIntegrationInWorker: true }, simpleFullscreen: this.getSimpleFullscreen() }; // Don't set icon on Windows so the exe's ico will be used as window and // taskbar's icon. See https://github.com/atom/atom/issues/4811 for more. if (process.platform === 'linux') options.icon = nativeImage.createFromPath(ICON_PATH); if (this.shouldAddCustomTitleBar()) options.titleBarStyle = 'hidden'; if (this.shouldAddCustomInsetTitleBar()) options.titleBarStyle = 'hiddenInset'; if (this.shouldHideTitleBar()) options.frame = false; const BrowserWindowConstructor = settings.browserWindowConstructor || BrowserWindow; this.browserWindow = new BrowserWindowConstructor(options); Object.defineProperty(this.browserWindow, 'loadSettingsJSON', { get: () => JSON.stringify( Object.assign( { userSettings: !this.isSpec ? this.atomApplication.configFile.get() : null }, this.loadSettings ) ) }); this.handleEvents(); this.loadSettings = Object.assign({}, settings); this.loadSettings.appVersion = app.getVersion(); this.loadSettings.appName = getAppName(); this.loadSettings.resourcePath = this.resourcePath; this.loadSettings.atomHome = process.env.ATOM_HOME; if (this.loadSettings.devMode == null) this.loadSettings.devMode = false; if (this.loadSettings.safeMode == null) this.loadSettings.safeMode = false; if (this.loadSettings.clearWindowState == null) this.loadSettings.clearWindowState = false; this.addLocationsToOpen(locationsToOpen); this.loadSettings.hasOpenFiles = locationsToOpen.some( location => location.pathToOpen && !location.isDirectory ); this.loadSettings.initialProjectRoots = this.projectRoots; StartupTime.addMarker('main-process:atom-window:end'); // Expose the startup markers to the renderer process, so we can have unified // measures about startup time between the main process and the renderer process. Object.defineProperty(this.browserWindow, 'startupMarkers', { get: () => { // We only want to make the main process startup data available once, // so if the window is refreshed or a new window is opened, the // renderer process won't use it again. const timingData = StartupTime.exportData(); StartupTime.deleteData(); return timingData; } }); // Only send to the first non-spec window created if (includeShellLoadTime && !this.isSpec) { includeShellLoadTime = false; if (!this.loadSettings.shellLoadTime) { this.loadSettings.shellLoadTime = Date.now() - global.shellStartTime; } } if (!this.loadSettings.env) this.env = this.loadSettings.env; this.browserWindow.on('window:loaded', () => { this.disableZoom(); this.emit('window:loaded'); this.resolveLoadedPromise(); }); this.browserWindow.on('window:locations-opened', () => { this.emit('window:locations-opened'); }); this.browserWindow.on('enter-full-screen', () => { this.browserWindow.webContents.send('did-enter-full-screen'); }); this.browserWindow.on('leave-full-screen', () => { this.browserWindow.webContents.send('did-leave-full-screen'); }); this.browserWindow.loadURL( url.format({ protocol: 'file', pathname: `${this.resourcePath}/static/index.html`, slashes: true }) ); this.browserWindow.showSaveDialog = this.showSaveDialog.bind(this); if (this.isSpec) this.browserWindow.focusOnWebView(); const hasPathToOpen = !( locationsToOpen.length === 1 && locationsToOpen[0].pathToOpen == null ); if (hasPathToOpen && !this.isSpecWindow()) this.openLocations(locationsToOpen); } hasProjectPaths() { return this.projectRoots.length > 0; } setupContextMenu() { const ContextMenu = require('./context-menu'); this.browserWindow.on('context-menu', menuTemplate => { return new ContextMenu(menuTemplate, this); }); } containsLocations(locations) { return locations.every(location => this.containsLocation(location)); } containsLocation(location) { if (!location.pathToOpen) return false; return this.projectRoots.some(projectPath => { if (location.pathToOpen === projectPath) return true; if (location.pathToOpen.startsWith(path.join(projectPath, path.sep))) { if (!location.exists) return true; if (!location.isDirectory) return true; } return false; }); } handleEvents() { this.browserWindow.on('close', async event => { if ( (!this.atomApplication.quitting || this.atomApplication.quittingForUpdate) && !this.unloading ) { event.preventDefault(); this.unloading = true; this.atomApplication.saveCurrentWindowOptions(false); if (await this.prepareToUnload()) this.close(); } }); this.browserWindow.on('closed', () => { this.fileRecoveryService.didCloseWindow(this); this.atomApplication.removeWindow(this); this.resolveClosedPromise(); }); this.browserWindow.on('unresponsive', async () => { if (this.isSpec) return; const result = await dialog.showMessageBox(this.browserWindow, { type: 'warning', buttons: ['Force Close', 'Keep Waiting'], cancelId: 1, // Canceling should be the least destructive action message: 'Editor is not responding', detail: 'The editor is not responding. Would you like to force close it or just keep waiting?' }); if (result.response === 0) this.browserWindow.destroy(); }); this.browserWindow.webContents.on('render-process-gone', async () => { if (this.headless) { console.log('Renderer process crashed, exiting'); this.atomApplication.exit(100); return; } await this.fileRecoveryService.didCrashWindow(this); const result = await dialog.showMessageBox(this.browserWindow, { type: 'warning', buttons: ['Close Window', 'Reload', 'Keep It Open'], cancelId: 2, // Canceling should be the least destructive action message: 'The editor has crashed', detail: 'Please report this issue to https://github.com/atom/atom' }); switch (result.response) { case 0: this.browserWindow.destroy(); break; case 1: this.browserWindow.reload(); break; } }); this.browserWindow.webContents.on('will-navigate', (event, url) => { if (url !== this.browserWindow.webContents.getURL()) event.preventDefault(); }); this.setupContextMenu(); // Spec window's web view should always have focus if (this.isSpec) this.browserWindow.on('blur', () => this.browserWindow.focusOnWebView()); } async prepareToUnload() { if (this.isSpecWindow()) return true; this.lastPrepareToUnloadPromise = new Promise(resolve => { const callback = (event, result) => { if ( BrowserWindow.fromWebContents(event.sender) === this.browserWindow ) { ipcMain.removeListener('did-prepare-to-unload', callback); if (!result) { this.unloading = false; this.atomApplication.quitting = false; } resolve(result); } }; ipcMain.on('did-prepare-to-unload', callback); this.browserWindow.webContents.send('prepare-to-unload'); }); return this.lastPrepareToUnloadPromise; } openPath(pathToOpen, initialLine, initialColumn) { return this.openLocations([{ pathToOpen, initialLine, initialColumn }]); } async openLocations(locationsToOpen) { this.addLocationsToOpen(locationsToOpen); await this.loadedPromise; this.sendMessage('open-locations', locationsToOpen); } didChangeUserSettings(settings) { this.sendMessage('did-change-user-settings', settings); } didFailToReadUserSettings(message) { this.sendMessage('did-fail-to-read-user-settings', message); } addLocationsToOpen(locationsToOpen) { const roots = new Set(this.projectRoots || []); for (const { pathToOpen, isDirectory } of locationsToOpen) { if (isDirectory) { roots.add(pathToOpen); } } this.projectRoots = Array.from(roots); this.projectRoots.sort(); } replaceEnvironment(env) { const { NODE_ENV, NODE_PATH, ATOM_HOME, ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT } = env; this.browserWindow.webContents.send('environment', { NODE_ENV, NODE_PATH, ATOM_HOME, ATOM_DISABLE_SHELLING_OUT_FOR_ENVIRONMENT }); } sendMessage(message, detail) { this.browserWindow.webContents.send('message', message, detail); } sendCommand(command, ...args) { if (this.isSpecWindow()) { if (!this.atomApplication.sendCommandToFirstResponder(command)) { switch (command) { case 'window:reload': return this.reload(); case 'window:toggle-dev-tools': return this.toggleDevTools(); case 'window:close': return this.close(); } } } else if (this.isWebViewFocused()) { this.sendCommandToBrowserWindow(command, ...args); } else if (!this.atomApplication.sendCommandToFirstResponder(command)) { this.sendCommandToBrowserWindow(command, ...args); } } sendURIMessage(uri) { this.browserWindow.webContents.send('uri-message', uri); } sendCommandToBrowserWindow(command, ...args) { const action = args[0] && args[0].contextCommand ? 'context-command' : 'command'; this.browserWindow.webContents.send(action, command, ...args); } getDimensions() { const [x, y] = Array.from(this.browserWindow.getPosition()); const [width, height] = Array.from(this.browserWindow.getSize()); return { x, y, width, height }; } getSimpleFullscreen() { return this.atomApplication.config.get('core.simpleFullScreenWindows'); } shouldAddCustomTitleBar() { return ( !this.isSpec && process.platform === 'darwin' && this.atomApplication.config.get('core.titleBar') === 'custom' ); } shouldAddCustomInsetTitleBar() { return ( !this.isSpec && process.platform === 'darwin' && this.atomApplication.config.get('core.titleBar') === 'custom-inset' ); } shouldHideTitleBar() { return ( !this.isSpec && this.atomApplication.config.get('core.titleBar') === 'hidden' ); } close() { return this.browserWindow.close(); } focus() { return this.browserWindow.focus(); } minimize() { return this.browserWindow.minimize(); } maximize() { return this.browserWindow.maximize(); } unmaximize() { return this.browserWindow.unmaximize(); } restore() { return this.browserWindow.restore(); } setFullScreen(fullScreen) { return this.browserWindow.setFullScreen(fullScreen); } setAutoHideMenuBar(autoHideMenuBar) { return this.browserWindow.setAutoHideMenuBar(autoHideMenuBar); } handlesAtomCommands() { return !this.isSpecWindow() && this.isWebViewFocused(); } isFocused() { return this.browserWindow.isFocused(); } isMaximized() { return this.browserWindow.isMaximized(); } isMinimized() { return this.browserWindow.isMinimized(); } isWebViewFocused() { return this.browserWindow.isWebViewFocused(); } isSpecWindow() { return this.isSpec; } reload() { this.loadedPromise = new Promise(resolve => { this.resolveLoadedPromise = resolve; }); this.prepareToUnload().then(canUnload => { if (canUnload) this.browserWindow.reload(); }); return this.loadedPromise; } showSaveDialog(options, callback) { options = Object.assign( { title: 'Save File', defaultPath: this.projectRoots[0] }, options ); let promise = dialog.showSaveDialog(this.browserWindow, options); if (typeof callback === 'function') { promise = promise.then(({ filePath, bookmark }) => { callback(filePath, bookmark); }); } return promise; } toggleDevTools() { return this.browserWindow.toggleDevTools(); } openDevTools() { return this.browserWindow.openDevTools(); } closeDevTools() { return this.browserWindow.closeDevTools(); } setDocumentEdited(documentEdited) { return this.browserWindow.setDocumentEdited(documentEdited); } setRepresentedFilename(representedFilename) { return this.browserWindow.setRepresentedFilename(representedFilename); } setProjectRoots(projectRootPaths) { this.projectRoots = projectRootPaths; this.projectRoots.sort(); this.loadSettings.initialProjectRoots = this.projectRoots; return this.atomApplication.saveCurrentWindowOptions(); } didClosePathWithWaitSession(path) { this.atomApplication.windowDidClosePathWithWaitSession(this, path); } copy() { return this.browserWindow.copy(); } disableZoom() { return this.browserWindow.webContents.setVisualZoomLevelLimits(1, 1); } getLoadedPromise() { return this.loadedPromise; } };