Merge pull request #120002 from microsoft/tyriar/terminal_sandbox

electron-sandbox refactors for terminal
This commit is contained in:
Daniel Imms
2021-03-29 16:47:00 -07:00
committed by GitHub
6 changed files with 90 additions and 85 deletions
@@ -23,7 +23,7 @@ import { assertNoRpc } from '../utils';
// Disable exit alerts as tests may trigger then and we're not testing the notifications
await config.update('showExitAlert', false, ConfigurationTarget.Global);
// Canvas may cause problems when running in a container
await config.update('gpuAcceleration', 'off', ConfigurationTarget.Global);
await config.update('rendererType', 'off', ConfigurationTarget.Global);
// Disable env var relaunch for tests to prevent terminals relaunching themselves
await config.update('environmentChangesRelaunch', false, ConfigurationTarget.Global);
});
@@ -10,11 +10,9 @@ import type { SearchAddon as XTermSearchAddon } from 'xterm-addon-search';
import type { Unicode11Addon as XTermUnicode11Addon } from 'xterm-addon-unicode11';
import type { WebglAddon as XTermWebglAddon } from 'xterm-addon-webgl';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { Emitter, Event } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Disposable } from 'vs/base/common/lifecycle';
import { ITerminalsLayoutInfoById, ITerminalsLayoutInfo, ITerminalChildProcess } from 'vs/platform/terminal/common/terminal';
import { IGetTerminalLayoutInfoArgs } from 'vs/platform/terminal/common/terminalProcess';
let Terminal: typeof XTermTerminal;
let SearchAddon: typeof XTermSearchAddon;
@@ -24,11 +22,7 @@ let WebglAddon: typeof XTermWebglAddon;
export class TerminalInstanceService extends Disposable implements ITerminalInstanceService {
public _serviceBrand: undefined;
readonly onPtyHostExit = Event.None;
readonly onPtyHostUnresponsive = Event.None;
readonly onPtyHostResponsive = Event.None;
readonly onPtyHostRestart = Event.None;
private readonly _onRequestDefaultShellAndArgs = this._register(new Emitter<IDefaultShellAndArgsRequest>());
protected readonly _onRequestDefaultShellAndArgs = this._register(new Emitter<IDefaultShellAndArgsRequest>());
readonly onRequestDefaultShellAndArgs = this._onRequestDefaultShellAndArgs.event;
public async getXtermConstructor(): Promise<typeof XTermTerminal> {
@@ -59,10 +53,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
return WebglAddon;
}
public createTerminalProcess(): Promise<ITerminalChildProcess> {
throw new Error('Not implemented');
}
public getDefaultShellAndArgs(useAutomationShell: boolean,): Promise<{ shell: string, args: string[] | string | undefined }> {
return new Promise(r => this._onRequestDefaultShellAndArgs.fire({
useAutomationShell,
@@ -73,22 +63,6 @@ export class TerminalInstanceService extends Disposable implements ITerminalInst
public async getMainProcessParentEnv(): Promise<IProcessEnvironment> {
return {};
}
getWorkspaceId(): string {
return '';
}
setTerminalLayoutInfo(layout?: ITerminalsLayoutInfoById, id?: string): Promise<void> {
throw new Error('Method not implemented.');
}
getTerminalLayoutInfo(args?: IGetTerminalLayoutInfoArgs): Promise<ITerminalsLayoutInfo | undefined> {
throw new Error('Method not implemented.');
}
getTerminalLayouts(): Map<string, ITerminalsLayoutInfo> {
return new Map<string, ITerminalsLayoutInfo>();
}
attachToProcess(id: number): Promise<ITerminalChildProcess> {
throw new Error('Method not implemented.');
}
}
registerSingleton(ITerminalInstanceService, TerminalInstanceService, true);
@@ -3,14 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { INativeOpenFileRequest } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { execFile } from 'child_process';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/electron-browser/terminalRemote';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -23,7 +18,6 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench
public _serviceBrand: undefined;
constructor(
@IFileService private readonly _fileService: IFileService,
@ITerminalService private readonly _terminalService: ITerminalService,
@IInstantiationService readonly instantiationService: IInstantiationService,
@IRemoteAgentService readonly remoteAgentService: IRemoteAgentService,
@@ -31,55 +25,11 @@ export class TerminalNativeContribution extends Disposable implements IWorkbench
) {
super();
ipcRenderer.on('vscode:openFiles', (_: unknown, request: INativeOpenFileRequest) => this._onOpenFileRequest(request));
this._register(nativeHostService.onDidResumeOS(() => this._onOsResume()));
this._terminalService.setLinuxDistro(linuxDistro);
this._terminalService.setNativeWindowsDelegate({
getWslPath: this._getWslPath.bind(this),
getWindowsBuildNumber: this._getWindowsBuildNumber.bind(this)
});
const connection = remoteAgentService.getConnection();
if (connection && connection.remoteAuthority) {
registerRemoteContributions();
}
}
private _onOsResume(): void {
this._terminalService.terminalInstances.forEach(instance => instance.forceRedraw());
}
private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise<void> {
// if the request to open files is coming in from the integrated terminal (identified though
// the termProgram variable) and we are instructed to wait for editors close, wait for the
// marker file to get deleted and then focus back to the integrated terminal.
if (request.termProgram === 'vscode' && request.filesToWait) {
const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri);
await this._whenFileDeleted(waitMarkerFileUri);
// Focus active terminal
this._terminalService.getActiveInstance()?.focus();
}
}
private _whenFileDeleted(path: URI): Promise<void> {
// Complete when wait marker file is deleted
return new Promise<void>(resolve => {
let running = false;
const interval = setInterval(async () => {
if (!running) {
running = true;
const exists = await this._fileService.exists(path);
running = false;
if (!exists) {
clearInterval(interval);
resolve(undefined);
}
}
}, 1000);
});
}
/**
@@ -5,10 +5,17 @@
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services';
import { Registry } from 'vs/platform/registry/common/platform';
import { ILocalTerminalService, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LocalTerminalService } from 'vs/workbench/contrib/terminal/electron-sandbox/localTerminalService';
import { TerminalNativeContribution } from 'vs/workbench/contrib/terminal/electron-sandbox/terminalNativeContribution';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
registerSharedProcessRemoteService(ILocalPtyService, TerminalIpcChannels.LocalPty, { supportsDelayedInstantiation: true });
registerSingleton(ILocalTerminalService, LocalTerminalService, true);
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(TerminalNativeContribution, LifecyclePhase.Ready);
@@ -0,0 +1,74 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals';
import { INativeOpenFileRequest } from 'vs/platform/windows/common/windows';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { registerRemoteContributions } from 'vs/workbench/contrib/terminal/electron-sandbox/terminalRemote';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { Disposable } from 'vs/base/common/lifecycle';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
export class TerminalNativeContribution extends Disposable implements IWorkbenchContribution {
public _serviceBrand: undefined;
constructor(
@IFileService private readonly _fileService: IFileService,
@ITerminalService private readonly _terminalService: ITerminalService,
@IInstantiationService readonly instantiationService: IInstantiationService,
@IRemoteAgentService readonly remoteAgentService: IRemoteAgentService,
@INativeHostService readonly nativeHostService: INativeHostService
) {
super();
ipcRenderer.on('vscode:openFiles', (_: unknown, request: INativeOpenFileRequest) => this._onOpenFileRequest(request));
this._register(nativeHostService.onDidResumeOS(() => this._onOsResume()));
const connection = remoteAgentService.getConnection();
if (connection && connection.remoteAuthority) {
registerRemoteContributions();
}
}
private _onOsResume(): void {
this._terminalService.terminalInstances.forEach(instance => instance.forceRedraw());
}
private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise<void> {
// if the request to open files is coming in from the integrated terminal (identified though
// the termProgram variable) and we are instructed to wait for editors close, wait for the
// marker file to get deleted and then focus back to the integrated terminal.
if (request.termProgram === 'vscode' && request.filesToWait) {
const waitMarkerFileUri = URI.revive(request.filesToWait.waitMarkerFileUri);
await this._whenFileDeleted(waitMarkerFileUri);
// Focus active terminal
this._terminalService.getActiveInstance()?.focus();
}
}
private _whenFileDeleted(path: URI): Promise<void> {
// Complete when wait marker file is deleted
return new Promise<void>(resolve => {
let running = false;
const interval = setInterval(async () => {
if (!running) {
running = true;
const exists = await this._fileService.exists(path);
running = false;
if (!exists) {
clearInterval(interval);
resolve(undefined);
}
}
}, 1000);
});
}
}
@@ -9,9 +9,8 @@ import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/wor
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { TERMINAL_ACTION_CATEGORY, TitleEventSource, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal';
import { Action } from 'vs/base/common/actions';
import { URI } from 'vs/base/common/uri';
import { homedir } from 'os';
import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { INativeEnvironmentService } from 'vs/platform/environment/common/environment';
export function registerRemoteContributions() {
const actionRegistry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
@@ -24,13 +23,14 @@ export class CreateNewLocalTerminalAction extends Action {
constructor(
id: string, label: string,
@ITerminalService private readonly terminalService: ITerminalService
@ITerminalService private readonly _terminalService: ITerminalService,
@INativeEnvironmentService private readonly _nativeEnvironmentService: INativeEnvironmentService
) {
super(id, label);
}
public run(): Promise<any> {
const instance = this.terminalService.createTerminal({ cwd: URI.file(homedir()) });
const instance = this._terminalService.createTerminal({ cwd: this._nativeEnvironmentService.userHome });
if (!instance) {
return Promise.resolve(undefined);
}
@@ -43,7 +43,7 @@ export class CreateNewLocalTerminalAction extends Action {
}
});
this.terminalService.setActiveInstance(instance);
return this.terminalService.showPanel(true);
this._terminalService.setActiveInstance(instance);
return this._terminalService.showPanel(true);
}
}