electron/lib/browser/init.ts

217 lines
6.9 KiB
TypeScript
Raw Normal View History

2020-03-20 20:28:31 +00:00
import { Buffer } from 'buffer';
import { EventEmitter } from 'events';
import * as fs from 'fs';
import { Socket } from 'net';
import * as path from 'path';
import * as util from 'util';
2016-03-18 18:51:02 +00:00
2020-03-20 20:28:31 +00:00
const Module = require('module');
2016-01-12 02:40:23 +00:00
// We modified the original process.argv to let node.js load the init.js,
2016-01-14 18:35:29 +00:00
// we need to restore it here.
2020-03-20 20:28:31 +00:00
process.argv.splice(1, 1);
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Clear search paths.
2020-03-20 20:28:31 +00:00
require('../common/reset-search-paths');
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Import common settings.
2020-03-20 20:28:31 +00:00
require('@electron/internal/common/init');
2016-01-12 02:40:23 +00:00
process._linkedBinding('electron_browser_event_emitter').setEventEmitterPrototype(EventEmitter.prototype);
2016-01-12 02:40:23 +00:00
if (process.platform === 'win32') {
2016-01-14 18:44:21 +00:00
// Redirect node's console to use our own implementations, since node can not
// handle console output when running as GUI program.
const consoleLog = (...args: any[]) => {
// @ts-ignore this typing is incorrect; 'format' is an optional parameter
// See https://nodejs.org/api/util.html#util_util_format_format_args
2020-03-20 20:28:31 +00:00
return process.log(util.format(...args) + '\n');
};
const streamWrite: Socket['write'] = function (chunk: Buffer | string, encoding?: any, callback?: Function) {
2016-01-12 02:40:23 +00:00
if (Buffer.isBuffer(chunk)) {
2020-03-20 20:28:31 +00:00
chunk = chunk.toString(encoding);
2016-01-12 02:40:23 +00:00
}
2020-03-20 20:28:31 +00:00
process.log(chunk);
2016-01-12 02:40:23 +00:00
if (callback) {
2020-03-20 20:28:31 +00:00
callback();
2016-01-12 02:40:23 +00:00
}
2020-03-20 20:28:31 +00:00
return true;
};
console.log = console.error = console.warn = consoleLog;
process.stdout.write = process.stderr.write = streamWrite;
2016-01-12 02:40:23 +00:00
}
2016-01-14 18:35:29 +00:00
// Don't quit on fatal error.
process.on('uncaughtException', function (error) {
2016-01-14 18:35:29 +00:00
// Do nothing if the user has a custom uncaught exception handler.
if (process.listenerCount('uncaughtException') > 1) {
2020-03-20 20:28:31 +00:00
return;
2016-01-12 02:40:23 +00:00
}
2016-01-14 18:35:29 +00:00
// Show error in GUI.
// We can't import { dialog } at the top of this file as this file is
// responsible for setting up the require hook for the "electron" module
// so we import it inside the handler down here
import('electron')
.then(({ dialog }) => {
2020-03-20 20:28:31 +00:00
const stack = error.stack ? error.stack : `${error.name}: ${error.message}`;
const message = 'Uncaught Exception:\n' + stack;
dialog.showErrorBox('A JavaScript error occurred in the main process', message);
});
});
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Emit 'exit' event on quit.
2020-03-20 20:28:31 +00:00
const { app } = require('electron');
2016-01-12 02:40:23 +00:00
app.on('quit', function (event, exitCode) {
2020-03-20 20:28:31 +00:00
process.emit('exit', exitCode);
});
2016-01-12 02:40:23 +00:00
if (process.platform === 'win32') {
// If we are a Squirrel.Windows-installed app, set app user model ID
// so that users don't have to do this.
//
// Squirrel packages are always of the form:
//
// PACKAGE-NAME
// - Update.exe
// - app-VERSION
// - OUREXE.exe
//
// Squirrel itself will always set the shortcut's App User Model ID to the
// form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
// app.setAppUserModelId with a matching identifier so that renderer processes
// will inherit this value.
2020-03-20 20:28:31 +00:00
const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe');
2016-08-02 08:45:46 +00:00
if (fs.existsSync(updateDotExe)) {
2020-03-20 20:28:31 +00:00
const packageDir = path.dirname(path.resolve(updateDotExe));
const packageName = path.basename(packageDir).replace(/\s/g, '');
const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replace(/\s/g, '');
2020-03-20 20:28:31 +00:00
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`);
}
}
2016-01-14 18:35:29 +00:00
// Map process.exit to app.exit, which quits gracefully.
2020-03-20 20:28:31 +00:00
process.exit = app.exit as () => never;
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Load the RPC server.
2020-03-20 20:28:31 +00:00
require('@electron/internal/browser/rpc-server');
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Load the guest view manager.
2020-03-20 20:28:31 +00:00
require('@electron/internal/browser/guest-view-manager');
require('@electron/internal/browser/guest-window-manager');
2016-01-12 02:40:23 +00:00
2016-01-14 18:35:29 +00:00
// Now we try to load app's package.json.
2020-03-20 20:28:31 +00:00
let packagePath = null;
let packageJson = null;
const searchPaths = ['app', 'app.asar', 'default_app.asar'];
if (process.resourcesPath) {
for (packagePath of searchPaths) {
try {
2020-03-20 20:28:31 +00:00
packagePath = path.join(process.resourcesPath, packagePath);
packageJson = Module._load(path.join(packagePath, 'package.json'));
break;
} catch {
2020-03-20 20:28:31 +00:00
continue;
}
2016-01-12 02:40:23 +00:00
}
}
if (packageJson == null) {
process.nextTick(function () {
2020-03-20 20:28:31 +00:00
return process.exit(1);
});
throw new Error('Unable to find a valid app');
2016-01-12 02:40:23 +00:00
}
2016-01-14 18:35:29 +00:00
// Set application's version.
2016-01-12 02:40:23 +00:00
if (packageJson.version != null) {
2020-03-20 20:28:31 +00:00
app.setVersion(packageJson.version);
2016-01-12 02:40:23 +00:00
}
2016-01-14 18:35:29 +00:00
// Set application's name.
2016-01-12 02:40:23 +00:00
if (packageJson.productName != null) {
2020-03-20 20:28:31 +00:00
app.name = `${packageJson.productName}`.trim();
2016-01-12 02:40:23 +00:00
} else if (packageJson.name != null) {
2020-03-20 20:28:31 +00:00
app.name = `${packageJson.name}`.trim();
2016-01-12 02:40:23 +00:00
}
2016-01-14 18:35:29 +00:00
// Set application's desktop name.
2016-01-12 02:40:23 +00:00
if (packageJson.desktopName != null) {
2020-03-20 20:28:31 +00:00
app.setDesktopName(packageJson.desktopName);
2016-01-12 02:40:23 +00:00
} else {
2020-03-20 20:28:31 +00:00
app.setDesktopName(`${app.name}.desktop`);
2016-01-12 02:40:23 +00:00
}
// Set v8 flags, delibrately lazy load so that apps that do not use this
// feature do not pay the price
if (packageJson.v8Flags != null) {
2020-03-20 20:28:31 +00:00
require('v8').setFlagsFromString(packageJson.v8Flags);
}
2020-03-20 20:28:31 +00:00
app._setDefaultAppPaths(packagePath);
2016-01-12 02:40:23 +00:00
// Load the chrome devtools support.
2020-03-20 20:28:31 +00:00
require('@electron/internal/browser/devtools');
2016-01-14 18:35:29 +00:00
// Load the chrome extension support.
require('@electron/internal/browser/chrome-extension-shim');
2016-01-12 02:40:23 +00:00
if (BUILDFLAG(ENABLE_REMOTE_MODULE)) {
2020-03-20 20:28:31 +00:00
require('@electron/internal/browser/remote/server');
}
2016-06-23 01:51:39 +00:00
// Load protocol module to ensure it is populated on app ready
2020-03-20 20:28:31 +00:00
require('@electron/internal/browser/api/protocol');
2016-06-23 01:51:39 +00:00
2016-01-14 18:35:29 +00:00
// Set main startup script of the app.
2020-03-20 20:28:31 +00:00
const mainStartupScript = packageJson.main || 'index.js';
2016-01-12 02:40:23 +00:00
2020-03-20 20:28:31 +00:00
const KNOWN_XDG_DESKTOP_VALUES = ['Pantheon', 'Unity:Unity7', 'pop:GNOME'];
2018-05-16 12:34:13 +00:00
function currentPlatformSupportsAppIndicator () {
2020-03-20 20:28:31 +00:00
if (process.platform !== 'linux') return false;
const currentDesktop = process.env.XDG_CURRENT_DESKTOP;
2020-03-20 20:28:31 +00:00
if (!currentDesktop) return false;
if (KNOWN_XDG_DESKTOP_VALUES.includes(currentDesktop)) return true;
// ubuntu based or derived session (default ubuntu one, communitheme…) supports
// indicator too.
2020-03-20 20:28:31 +00:00
if (/ubuntu/ig.test(currentDesktop)) return true;
2020-03-20 20:28:31 +00:00
return false;
}
// Workaround for electron/electron#5050 and electron/electron#9046
process.env.ORIGINAL_XDG_CURRENT_DESKTOP = process.env.XDG_CURRENT_DESKTOP;
if (currentPlatformSupportsAppIndicator()) {
2020-03-20 20:28:31 +00:00
process.env.XDG_CURRENT_DESKTOP = 'Unity';
}
// Quit when all windows are closed and no other one is listening to this.
app.on('window-all-closed', () => {
if (app.listenerCount('window-all-closed') === 1) {
2020-03-20 20:28:31 +00:00
app.quit();
}
2020-03-20 20:28:31 +00:00
});
2020-03-20 20:28:31 +00:00
const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu');
// Create default menu.
//
// The |will-finish-launching| event is emitted before |ready| event, so default
// menu is set before any user window is created.
app.once('will-finish-launching', setDefaultApplicationMenu);
if (packagePath) {
// Finally load app's main.js and transfer control to C++.
2020-03-20 20:28:31 +00:00
process._firstFileName = Module._resolveFilename(path.join(packagePath, mainStartupScript), null, false);
Module._load(path.join(packagePath, mainStartupScript), Module, true);
} else {
2020-03-20 20:28:31 +00:00
console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)');
console.error('This normally means you\'ve damaged the Electron package somehow');
}