web.main.ts 8.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { mark } from 'vs/base/common/performance';
import { domContentLoaded, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
11
import { SimpleLogService, SimpleProductService, SimpleWorkbenchEnvironmentService } from 'vs/workbench/browser/web.simpleservices';
12
import { Workbench } from 'vs/workbench/browser/workbench';
A
Alex Dima 已提交
13 14
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel';
B
Benjamin Pasero 已提交
15 16
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IProductService } from 'vs/platform/product/common/product';
A
Alex Dima 已提交
17
import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentServiceImpl';
B
Benjamin Pasero 已提交
18 19
import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService';
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
A
Alex Dima 已提交
20
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
B
Benjamin Pasero 已提交
21
import { IFileService } from 'vs/platform/files/common/files';
B
Benjamin Pasero 已提交
22
import { FileService } from 'vs/workbench/services/files/common/fileService';
A
Alex Dima 已提交
23
import { Schemas } from 'vs/base/common/network';
24 25 26 27 28 29 30 31
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { onUnexpectedError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri';
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache';
import { ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
32
import { WebResources } from 'vs/workbench/browser/web.resources';
33 34 35 36 37 38 39

interface IWindowConfiguration {
	settingsUri: URI;
	remoteAuthority: string;
	folderUri?: URI;
	workspaceUri?: URI;
}
40 41 42 43 44

class CodeRendererMain extends Disposable {

	private workbench: Workbench;

45 46 47 48
	constructor(private readonly configuration: IWindowConfiguration) {
		super();
	}

49
	async open(): Promise<void> {
50
		const services = await this.initServices();
51

52 53
		await domContentLoaded();
		mark('willStartWorkbench');
54

55 56 57 58 59 60
		// Create Workbench
		this.workbench = new Workbench(
			document.body,
			services.serviceCollection,
			services.logService
		);
61

62 63
		// Layout
		this._register(addDisposableListener(window, EventType.RESIZE, () => this.workbench.layout()));
64

65 66 67
		// Resource Loading
		this._register(new WebResources(<IFileService>services.serviceCollection.get(IFileService)));

68 69
		// Workbench Lifecycle
		this._register(this.workbench.onShutdown(() => this.dispose()));
70

71 72
		// Startup
		this.workbench.startup();
73 74
	}

75
	private async initServices(): Promise<{ serviceCollection: ServiceCollection, logService: ILogService }> {
76 77
		const serviceCollection = new ServiceCollection();

78 79 80 81 82
		// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
		// NOTE: DO NOT ADD ANY OTHER SERVICE INTO THE COLLECTION HERE.
		// CONTRIBUTE IT VIA WORKBENCH.MAIN.TS AND registerSingleton().
		// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

B
Benjamin Pasero 已提交
83 84 85 86 87
		// Log
		const logService = new SimpleLogService();
		serviceCollection.set(ILogService, logService);

		// Environment
88
		const environmentService = this.createEnvironmentService();
B
Benjamin Pasero 已提交
89 90 91 92 93 94 95 96 97 98
		serviceCollection.set(IWorkbenchEnvironmentService, environmentService);

		// Product
		const productService = new SimpleProductService();
		serviceCollection.set(IProductService, productService);

		// Remote
		const remoteAuthorityResolverService = new RemoteAuthorityResolverService();
		serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService);

A
Alex Dima 已提交
99 100 101
		const remoteAgentService = this._register(new RemoteAgentService(environmentService, productService, remoteAuthorityResolverService));
		serviceCollection.set(IRemoteAgentService, remoteAgentService);

B
Benjamin Pasero 已提交
102
		// Files
B
Benjamin Pasero 已提交
103
		const fileService = this._register(new FileService(logService));
B
Benjamin Pasero 已提交
104 105
		serviceCollection.set(IFileService, fileService);

A
Alex Dima 已提交
106 107 108 109
		const connection = remoteAgentService.getConnection();
		if (connection) {
			const channel = connection.getChannel<IChannel>(REMOTE_FILE_SYSTEM_CHANNEL_NAME);
			const remoteFileSystemProvider = this._register(new RemoteExtensionsFileSystemProvider(channel, remoteAgentService.getEnvironment()));
A
Alex Dima 已提交
110
			fileService.registerProvider(Schemas.vscodeRemote, remoteFileSystemProvider);
A
Alex Dima 已提交
111 112
		}

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
		const payload = await this.resolveWorkspaceInitializationPayload();

		await Promise.all([
			this.createWorkspaceService(payload, fileService, remoteAgentService, logService).then(service => {

				// Workspace
				serviceCollection.set(IWorkspaceContextService, service);

				// Configuration
				serviceCollection.set(IConfigurationService, service);

				return service;
			}),
		]);

128 129
		return { serviceCollection, logService };
	}
130

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
	private createEnvironmentService(): IWorkbenchEnvironmentService {
		const environmentService = new SimpleWorkbenchEnvironmentService();
		environmentService.appRoot = '/web/';
		environmentService.args = { _: [] };
		environmentService.appSettingsHome = '/web/settings';
		environmentService.appSettingsPath = this.configuration.settingsUri.path;
		environmentService.appKeybindingsPath = '/web/settings/keybindings.json';
		environmentService.logsPath = '/web/logs';
		environmentService.debugExtensionHost = {
			port: null,
			break: false
		};
		return environmentService;
	}

146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
	private async createWorkspaceService(payload: IWorkspaceInitializationPayload, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise<WorkspaceService> {

		const workspaceService = new WorkspaceService({ userSettingsResource: this.configuration.settingsUri, remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache() }, new ConfigurationFileService(fileService), remoteAgentService);

		try {
			await workspaceService.initialize(payload);

			return workspaceService;
		} catch (error) {
			onUnexpectedError(error);
			logService.error(error);

			return workspaceService;
		}
	}

	private async resolveWorkspaceInitializationPayload(): Promise<IWorkspaceInitializationPayload> {

		const hash = (uri: URI) => {
			return crypto.subtle.digest('SHA-1', new TextEncoder().encode(uri.toString())).then(buffer => {
				// https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string
				return Array.prototype.map.call(new Uint8Array(buffer), (value: number) => `00${value.toString(16)}`.slice(-2)).join('');
			});
		};

		// Multi-root workspace
		if (this.configuration.workspaceUri) {
			const id = await hash(this.configuration.workspaceUri);
			return { id, configPath: this.configuration.workspaceUri };
		}

		// Single-folder workspace
		if (this.configuration.folderUri) {
			const id = await hash(this.configuration.folderUri);
			return { id, folder: this.configuration.folderUri };
		}

		return { id: 'empty-window' };
	}
185 186
}

187 188 189 190 191
export interface IWindowConfigurationContents {
	settingsPath: string;
	folderPath?: string;
	workspacePath?: string;
}
192

193 194 195 196 197 198 199 200
export function main(windowConfigurationContents: IWindowConfigurationContents): Promise<void> {
	const windowConfiguration: IWindowConfiguration = {
		settingsUri: toResource(windowConfigurationContents.settingsPath),
		folderUri: windowConfigurationContents.folderPath ? toResource(windowConfigurationContents.folderPath) : undefined,
		workspaceUri: windowConfigurationContents.workspacePath ? toResource(windowConfigurationContents.workspacePath) : undefined,
		remoteAuthority: document.location.host
	};
	const renderer = new CodeRendererMain(windowConfiguration);
201 202
	return renderer.open();
}
203 204 205 206 207 208 209

function toResource(path: string): URI {
	return URI.from({
		scheme: Schemas.vscodeRemote,
		authority: document.location.host,
		path
	});
210
}