From 330901dd24f58447c2d5d697878507c5fa41b76d Mon Sep 17 00:00:00 2001 From: Joao Moreno Date: Wed, 1 Mar 2017 17:20:45 +0100 Subject: [PATCH] run shared process in a browser window --- .../code/electron-browser/sharedProcess.html | 32 ++++++ src/vs/code/electron-browser/sharedProcess.js | 107 ++++++++++++++++++ .../sharedProcessMain.ts | 35 ++---- src/vs/code/electron-main/main.ts | 19 +--- src/vs/code/electron-main/sharedProcess.ts | 42 +++++++ src/vs/code/node/sharedProcess.ts | 77 ------------- 6 files changed, 193 insertions(+), 119 deletions(-) create mode 100644 src/vs/code/electron-browser/sharedProcess.html create mode 100644 src/vs/code/electron-browser/sharedProcess.js rename src/vs/code/{node => electron-browser}/sharedProcessMain.ts (91%) create mode 100644 src/vs/code/electron-main/sharedProcess.ts delete mode 100644 src/vs/code/node/sharedProcess.ts diff --git a/src/vs/code/electron-browser/sharedProcess.html b/src/vs/code/electron-browser/sharedProcess.html new file mode 100644 index 00000000000..869131118cd --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess.html @@ -0,0 +1,32 @@ + + + + + + + + + + + + Shared Process + + + + + + \ No newline at end of file diff --git a/src/vs/code/electron-browser/sharedProcess.js b/src/vs/code/electron-browser/sharedProcess.js new file mode 100644 index 00000000000..b28716a7cea --- /dev/null +++ b/src/vs/code/electron-browser/sharedProcess.js @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// Warning: Do not use the `let` declarator in this file, it breaks our minification + +'use strict'; + +/*global window,document,define*/ + +const path = require('path'); +const electron = require('electron'); +const remote = electron.remote; +const ipc = electron.ipcRenderer; + +function assign(destination, source) { + return Object.keys(source) + .reduce(function (r, key) { r[key] = source[key]; return r; }, destination); +} + +function parseURLQueryArgs() { + const search = window.location.search || ''; + + return search.split(/[?&]/) + .filter(function (param) { return !!param; }) + .map(function (param) { return param.split('='); }) + .filter(function (param) { return param.length === 2; }) + .reduce(function (r, param) { r[param[0]] = decodeURIComponent(param[1]); return r; }, {}); +} + +function createScript(src, onload) { + const script = document.createElement('script'); + script.src = src; + script.addEventListener('load', onload); + + const head = document.getElementsByTagName('head')[0]; + head.insertBefore(script, head.lastChild); +} + +function uriFromPath(_path) { + var pathName = path.resolve(_path).replace(/\\/g, '/'); + if (pathName.length > 0 && pathName.charAt(0) !== '/') { + pathName = '/' + pathName; + } + + return encodeURI('file://' + pathName); +} + +function main() { + const args = parseURLQueryArgs(); + const configuration = JSON.parse(args['config'] || '{}') || {}; + + // Correctly inherit the parent's environment + assign(process.env, configuration.userEnv); + + // Get the nls configuration into the process.env as early as possible. + var nlsConfig = { availableLanguages: {} }; + const config = process.env['VSCODE_NLS_CONFIG']; + if (config) { + process.env['VSCODE_NLS_CONFIG'] = config; + try { + nlsConfig = JSON.parse(config); + } catch (e) { /*noop*/ } + } + + var locale = nlsConfig.availableLanguages['*'] || 'en'; + if (locale === 'zh-tw') { + locale = 'zh-Hant'; + } else if (locale === 'zh-cn') { + locale = 'zh-Hans'; + } + + window.document.documentElement.setAttribute('lang', locale); + + // Load the loader and start loading the workbench + const rootUrl = uriFromPath(configuration.appRoot) + '/out'; + + // In the bundled version the nls plugin is packaged with the loader so the NLS Plugins + // loads as soon as the loader loads. To be able to have pseudo translation + createScript(rootUrl + '/vs/loader.js', function () { + define('fs', ['original-fs'], function (originalFS) { return originalFS; }); // replace the patched electron fs with the original node fs for all AMD code + + window.MonacoEnvironment = {}; + + const nodeCachedDataErrors = window.MonacoEnvironment.nodeCachedDataErrors = []; + require.config({ + baseUrl: rootUrl, + 'vs/nls': nlsConfig, + nodeCachedDataDir: configuration.nodeCachedDataDir, + onNodeCachedDataError: function (err) { nodeCachedDataErrors.push(err) }, + nodeModules: [/*BUILD->INSERT_NODE_MODULES*/] + }); + + if (nlsConfig.pseudo) { + require(['vs/nls'], function (nlsPlugin) { + nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); + }); + } + + require(['vs/code/electron-browser/sharedProcessMain'], function () { + + }); + }); +} + +main(); diff --git a/src/vs/code/node/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcessMain.ts similarity index 91% rename from src/vs/code/node/sharedProcessMain.ts rename to src/vs/code/electron-browser/sharedProcessMain.ts index d6ca3fa783c..5bef18daefb 100644 --- a/src/vs/code/node/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcessMain.ts @@ -28,32 +28,16 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper import { TelemetryAppenderChannel } from 'vs/platform/telemetry/common/telemetryIpc'; import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender'; -import { ISharedProcessInitData } from './sharedProcess'; +// import { ISharedProcessInitData } from './sharedProcess'; import { IChoiceService } from 'vs/platform/message/common/message'; import { ChoiceChannelClient } from 'vs/platform/message/common/messageIpc'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsChannelClient } from 'vs/platform/windows/common/windowsIpc'; import { ActiveWindowManager } from 'vs/code/common/windows'; +import { ipcRenderer } from 'electron'; -function quit(err?: Error) { - if (err) { - console.error(err.stack || err); - } - - process.exit(err ? 1 : 0); -} - -/** - * Plan B is to kill oneself if one's parent dies. Much drama. - */ -function setupPlanB(parentPid: number): void { - setInterval(function () { - try { - process.kill(parentPid, 0); // throws an exception if the main process doesn't exist anymore. - } catch (e) { - process.exit(); - } - }, 5000); +interface ISharedProcessInitData { + args: any; } const eventPrefix = 'monacoworkbench'; @@ -161,14 +145,13 @@ function setupIPC(hook: string): TPromise { function handshake(): TPromise { return new TPromise((c, e) => { - process.once('message', c); - process.once('error', e); - process.send('hello'); + ipcRenderer.once('handshake', (_, r) => c(r)); + ipcRenderer.send('handshake'); }); } setupIPC(process.env['VSCODE_SHARED_IPC_HOOK']) .then(server => handshake() - .then(data => main(server, data)) - .then(() => setupPlanB(process.env['VSCODE_PID'])) - .done(null, quit)); \ No newline at end of file + .then(data => main(server, data))); + // .then(() => setupPlanB(process.env['VSCODE_PID'])) + // .done(null, quit)); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index ba8f2436591..9912806456f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -27,9 +27,8 @@ import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { TPromise } from 'vs/base/common/winjs.base'; import { AskpassChannel } from 'vs/workbench/parts/git/common/gitIpc'; import { GitAskpassService } from 'vs/workbench/parts/git/electron-main/askpassService'; -import { spawnSharedProcess } from 'vs/code/node/sharedProcess'; +import { spawnSharedProcess } from 'vs/code/electron-main/sharedProcess'; import { Mutex } from 'windows-mutex'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { LaunchService, ILaunchChannel, LaunchChannel, LaunchChannelClient, ILaunchService } from './launch'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; @@ -144,17 +143,9 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo // Spawn shared process const initData = { args: environmentService.args }; - const options = { - allowOutput: !environmentService.isBuilt || environmentService.verbose, - debugPort: environmentService.isBuilt ? null : 5871 - }; - let sharedProcessDisposable: IDisposable; - - const sharedProcess = spawnSharedProcess(initData, options).then(disposable => { - sharedProcessDisposable = disposable; - return connect(environmentService.sharedIPCHandle, 'main'); - }); + const sharedProcess = spawnSharedProcess(initData) + .then(disposable => connect(environmentService.sharedIPCHandle, 'main')); // Create a new service collection, because the telemetry service // requires a connection to shared process, which was only established @@ -220,10 +211,6 @@ function main(accessor: ServicesAccessor, mainIpcServer: Server, userEnv: platfo mainIpcServer = null; } - if (sharedProcessDisposable) { - sharedProcessDisposable.dispose(); - } - if (windowsMutex) { windowsMutex.release(); } diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts new file mode 100644 index 00000000000..66c7a783cf2 --- /dev/null +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assign } from 'vs/base/common/objects'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { TPromise } from 'vs/base/common/winjs.base'; +import { BrowserWindow, ipcMain } from 'electron'; + +export interface ISharedProcessInitData { + args: ParsedArgs; +} + +export function spawnSharedProcess(initData: ISharedProcessInitData): TPromise { + const window = new BrowserWindow(); + const config = assign({ + userEnv: {}, + appRoot: '', + nodeCachedDataDir: '' + }); + + const url = `${require.toUrl('vs/code/electron-browser/sharedProcess.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; + window.loadURL(url); + // window.hide(); + window.webContents.openDevTools(); + + // Prevent the window from dying + window.on('close', e => { + if (window.isVisible()) { + e.preventDefault(); + window.hide(); + } + }); + + return new TPromise((c, e) => { + ipcMain.once('handshake', ({ sender }) => { + sender.send('handshake', initData); + c(null); + }); + }); +} \ No newline at end of file diff --git a/src/vs/code/node/sharedProcess.ts b/src/vs/code/node/sharedProcess.ts deleted file mode 100644 index 46f0f1ea9b5..00000000000 --- a/src/vs/code/node/sharedProcess.ts +++ /dev/null @@ -1,77 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as cp from 'child_process'; -import URI from 'vs/base/common/uri'; -import { IDisposable } from 'vs/base/common/lifecycle'; -import { assign } from 'vs/base/common/objects'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import { TPromise } from 'vs/base/common/winjs.base'; - -export interface ISharedProcessInitData { - args: ParsedArgs; -} - -export interface ISharedProcessOptions { - allowOutput?: boolean; - debugPort?: number; -} - -const boostrapPath = URI.parse(require.toUrl('bootstrap')).fsPath; - -function _spawnSharedProcess(initData: ISharedProcessInitData, options: ISharedProcessOptions): cp.ChildProcess { - const execArgv: string[] = []; - const env = assign({}, process.env, { - AMD_ENTRYPOINT: 'vs/code/node/sharedProcessMain', - ELECTRON_NO_ASAR: '1' - }); - - if (options.allowOutput) { - env['VSCODE_ALLOW_IO'] = 'true'; - } - - if (options.debugPort) { - execArgv.push(`--debug=${options.debugPort}`); - } - - const result = cp.fork(boostrapPath, ['--type=SharedProcess'], { env, execArgv }); - - return result; -} - -export function spawnSharedProcess(initData: ISharedProcessInitData, options: ISharedProcessOptions = {}): TPromise { - let spawnCount = 0; - let child: cp.ChildProcess; - - let promise: TPromise; - - const spawn = () => { - if (++spawnCount > 10) { - return; - } - - child = _spawnSharedProcess(initData, options); - promise = new TPromise((c, e) => { - // handshake - child.once('message', () => { - child.send(initData); - c({ - dispose: () => { - if (child) { - child.removeListener('exit', spawn); - child.kill(); - child = null; - } - } - }); - }); - }); - child.on('exit', spawn); - }; - - spawn(); - - return promise; -} \ No newline at end of file -- GitLab