Skip to content

Commit

Permalink
Merge pull request #21879 from Microsoft/joao/shared-process
Browse files Browse the repository at this point in the history
Move shared process to a browser window
  • Loading branch information
joaomoreno authored Mar 7, 2017
2 parents 56c54ef + f216573 commit a3ad921
Show file tree
Hide file tree
Showing 18 changed files with 327 additions and 152 deletions.
6 changes: 2 additions & 4 deletions build/gulpfile.vscode.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,24 +57,22 @@ const vscodeResources = [
'out-build/bootstrap.js',
'out-build/bootstrap-amd.js',
'out-build/paths.js',
'out-build/vs/**/*.{svg,png,cur}',
'out-build/vs/**/*.{svg,png,cur,html}',
'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh}',
'out-build/vs/base/browser/ui/octiconLabel/octicons/**',
'out-build/vs/workbench/browser/media/*-theme.css',
'out-build/vs/workbench/electron-browser/bootstrap/**',
'out-build/vs/workbench/parts/debug/**/*.json',
'out-build/vs/workbench/parts/execution/**/*.scpt',
'out-build/vs/workbench/parts/git/**/*.html',
'out-build/vs/workbench/parts/git/**/*.sh',
'out-build/vs/workbench/parts/html/browser/webview.html',
'out-build/vs/workbench/parts/html/browser/webview-pre.js',
'out-build/vs/**/markdown.css',
'out-build/vs/workbench/parts/tasks/**/*.json',
'out-build/vs/workbench/parts/terminal/electron-browser/terminalProcess.js',
'out-build/vs/workbench/parts/welcome/walkThrough/**/*.md',
'out-build/vs/workbench/parts/welcome/page/**/*.html',
'out-build/vs/workbench/services/files/**/*.exe',
'out-build/vs/workbench/services/files/**/*.md',
'out-build/vs/code/electron-browser/sharedProcess.js',
'!**/test/**'
];

Expand Down
6 changes: 3 additions & 3 deletions src/typings/electron.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3370,15 +3370,15 @@ declare namespace Electron {
/**
* The URL associated with the PAC file.
*/
pacScript: string;
pacScript?: string;
/**
* Rules indicating which proxies to use.
*/
proxyRules: string;
proxyRules?: string;
/**
* Rules indicating which URLs should bypass the proxy settings.
*/
proxyBypassRules: string;
proxyBypassRules?: string;
}

interface NetworkEmulationOptions {
Expand Down
14 changes: 13 additions & 1 deletion src/vs/base/node/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { createGunzip } from 'zlib';

export type Agent = any;

export interface IRawRequestFunction {
(options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
}

export interface IRequestOptions {
type?: string;
url?: string;
Expand All @@ -28,6 +32,7 @@ export interface IRequestOptions {
agent?: Agent;
followRedirects?: number;
strictSSL?: boolean;
getRawRequest?(options: IRequestOptions): IRawRequestFunction;
}

export interface IRequestContext {
Expand All @@ -44,15 +49,22 @@ export interface IRequestFunction {
(options: IRequestOptions): TPromise<IRequestContext>;
}

function getNodeRequest(options: IRequestOptions): IRawRequestFunction {
const endpoint = parseUrl(options.url);
return endpoint.protocol === 'https:' ? https.request : http.request;
}

export function request(options: IRequestOptions): TPromise<IRequestContext> {
let req: http.ClientRequest;

return new TPromise<IRequestContext>((c, e) => {
const endpoint = parseUrl(options.url);
const rawRequest = endpoint.protocol === 'https:' ? https.request : http.request;
const getRawRequest = options.getRawRequest || getNodeRequest;
const rawRequest = getRawRequest(options);
const opts: https.RequestOptions = {
hostname: endpoint.hostname,
port: endpoint.port ? parseInt(endpoint.port) : (endpoint.protocol === 'https:' ? 443 : 80),
protocol: endpoint.protocol,
path: endpoint.path,
method: options.type || 'GET',
headers: options.headers,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/code/buildfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ exports.collectModules= function() {
createModuleDescription('vs/code/electron-main/main', []),
createModuleDescription('vs/code/node/cli', []),
createModuleDescription('vs/code/node/cliProcessMain', ['vs/code/node/cli']),
createModuleDescription('vs/code/node/sharedProcessMain', [])
createModuleDescription('vs/code/electron-browser/sharedProcessMain', [])
];
};
16 changes: 16 additions & 0 deletions src/vs/code/electron-browser/sharedProcess.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
</head>

<body aria-label="">
Shared Process
</body>

<!-- Startup via index.js -->
<script src="sharedProcess.js"></script>

</html>
105 changes: 105 additions & 0 deletions src/vs/code/electron-browser/sharedProcess.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*---------------------------------------------------------------------------------------------
* 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();
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { EnvironmentService } from 'vs/platform/environment/node/environmentService';
import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc';
import { IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
Expand All @@ -21,39 +21,23 @@ import { ExtensionGalleryService } from 'vs/platform/extensionManagement/node/ex
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ConfigurationService } from 'vs/platform/configuration/node/configurationService';
import { IRequestService } from 'vs/platform/request/node/request';
import { RequestService } from 'vs/platform/request/node/requestService';
import { RequestService } from 'vs/platform/request/electron-browser/requestService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { combinedAppender, NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties';
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 { 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 {
sharedIPCHandle: string;
args: ParsedArgs;
}

const eventPrefix = 'monacoworkbench';
Expand Down Expand Up @@ -159,16 +143,17 @@ function setupIPC(hook: string): TPromise<Server> {
return setup(true);
}

function handshake(): TPromise<ISharedProcessInitData> {
function startHandshake(): TPromise<ISharedProcessInitData> {
return new TPromise<ISharedProcessInitData>((c, e) => {
process.once('message', c);
process.once('error', e);
process.send('hello');
ipcRenderer.once('handshake:hey there', (_, r) => c(r));
ipcRenderer.send('handshake:hello');
});
}

setupIPC(process.env['VSCODE_SHARED_IPC_HOOK'])
.then(server => handshake()
.then(data => main(server, data))
.then(() => setupPlanB(process.env['VSCODE_PID']))
.done(null, quit));
function handshake(): TPromise<void> {
return startHandshake()
.then((data) => setupIPC(data.sharedIPCHandle).then(server => main(server, data)))
.then(() => ipcRenderer.send('handshake:im ready'));
}

handshake();
Loading

0 comments on commit a3ad921

Please sign in to comment.