Merge pull request #118418 from microsoft/ben/sandbox

Make more services available in electron sandbox
This commit is contained in:
Benjamin Pasero 2021-03-08 14:41:03 +01:00 committed by GitHub
commit c2a92a1ba6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 1439 additions and 375 deletions

View file

@ -84,6 +84,8 @@ import { TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { LocalPtyService } from 'vs/platform/terminal/electron-browser/localPtyService';
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';
import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncServiceIpc';
import { IChecksumService } from 'vs/platform/checksum/common/checksumService';
import { ChecksumService } from 'vs/platform/checksum/node/checksumService';
class SharedProcessMain extends Disposable {
@ -186,6 +188,9 @@ class SharedProcessMain extends Disposable {
// Request
services.set(IRequestService, new SyncDescriptor(RequestService));
// Checksum
services.set(IChecksumService, new SyncDescriptor(ChecksumService));
// Native Host
const nativeHostService = ProxyChannel.toService<INativeHostService>(mainProcessService.getChannel('nativeHost'), { context: this.configuration.windowId });
services.set(INativeHostService, nativeHostService);
@ -286,6 +291,10 @@ class SharedProcessMain extends Disposable {
const extensionTipsChannel = new ExtensionTipsChannel(accessor.get(IExtensionTipsService));
this.server.registerChannel('extensionTipsService', extensionTipsChannel);
// Checksum
const checksumChannel = ProxyChannel.fromService(accessor.get(IChecksumService));
this.server.registerChannel('checksum', checksumChannel);
// Settings Sync
const userDataSyncMachineChannel = new UserDataSyncMachinesServiceChannel(accessor.get(IUserDataSyncMachinesService));
this.server.registerChannel('userDataSyncMachines', userDataSyncMachineChannel);

View file

@ -86,6 +86,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance
import { IExtensionUrlTrustService } from 'vs/platform/extensionManagement/common/extensionUrlTrust';
import { ExtensionUrlTrustService } from 'vs/platform/extensionManagement/node/extensionUrlTrustService';
import { once } from 'vs/base/common/functional';
import { ISignService } from 'vs/platform/sign/common/sign';
/**
* The main VS Code application. There will only ever be one instance,
@ -625,6 +626,10 @@ export class CodeApplication extends Disposable {
const encryptionChannel = ProxyChannel.fromService(accessor.get(IEncryptionMainService));
mainProcessElectronServer.registerChannel('encryption', encryptionChannel);
// Signing
const signChannel = ProxyChannel.fromService(accessor.get(ISignService));
mainProcessElectronServer.registerChannel('sign', signChannel);
// Keyboard Layout
const keyboardLayoutChannel = ProxyChannel.fromService(accessor.get(IKeyboardLayoutMainService));
mainProcessElectronServer.registerChannel('keyboardLayout', keyboardLayoutChannel);

View file

@ -0,0 +1,19 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
export const IChecksumService = createDecorator<IChecksumService>('checksumService');
export interface IChecksumService {
readonly _serviceBrand: undefined;
/**
* Computes the checksum of the contents of the resource.
*/
checksum(resource: URI): Promise<string>;
}

View file

@ -0,0 +1,9 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerSharedProcessRemoteService } from 'vs/platform/ipc/electron-sandbox/services';
import { IChecksumService } from 'vs/platform/checksum/common/checksumService';
registerSharedProcessRemoteService(IChecksumService, 'checksum', { supportsDelayedInstantiation: true });

View file

@ -0,0 +1,30 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { createHash } from 'crypto';
import { listenStream } from 'vs/base/common/stream';
import { URI } from 'vs/base/common/uri';
import { IChecksumService } from 'vs/platform/checksum/common/checksumService';
import { IFileService } from 'vs/platform/files/common/files';
export class ChecksumService implements IChecksumService {
declare readonly _serviceBrand: undefined;
constructor(@IFileService private readonly fileService: IFileService) { }
checksum(resource: URI): Promise<string> {
return new Promise<string>(async (resolve, reject) => {
const hash = createHash('md5');
const stream = (await this.fileService.readFileStream(resource)).value;
listenStream(stream, {
onData: data => hash.update(data.buffer),
onError: error => reject(error),
onEnd: () => resolve(hash.digest('base64').replace(/=+$/, ''))
});
});
}
}

View file

@ -0,0 +1,38 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { ChecksumService } from 'vs/platform/checksum/node/checksumService';
import { IFileService } from 'vs/platform/files/common/files';
import { FileService } from 'vs/platform/files/common/fileService';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { NullLogService } from 'vs/platform/log/common/log';
suite('Checksum Service', () => {
let fileService: IFileService;
setup(() => {
const logService = new NullLogService();
fileService = new FileService(logService);
const diskFileSystemProvider = new DiskFileSystemProvider(logService);
fileService.registerProvider(Schemas.file, diskFileSystemProvider);
});
teardown(() => {
fileService.dispose();
});
test('checksum', async () => {
const checksumService = new ChecksumService(fileService);
const checksum = await checksumService.checksum(URI.file(getPathFromAmdModule(require, './fixtures/lorem.txt')));
assert.ok(checksum === '8mi5KF8kcb817zmlal1kZA' || checksum === 'DnUKbJ1bHPPNZoHgHV25sg'); // depends on line endings git config
});
});

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ export interface IKeyboardLayoutData {
keyboardMapping: IKeyboardMapping;
}
export interface IKeyboardLayoutMainService {
export interface INativeKeyboardLayoutService {
readonly _serviceBrand: undefined;
readonly onDidChangeKeyboardLayout: Event<IKeyboardLayoutData>;
getKeyboardLayoutData(): Promise<IKeyboardLayoutData>;

View file

@ -5,16 +5,16 @@
import * as nativeKeymap from 'native-keymap';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IKeyboardLayoutData, IKeyboardLayoutMainService as ICommonKeyboardLayoutMainService } from 'vs/platform/keyboardLayout/common/keyboardLayoutMainService';
import { IKeyboardLayoutData, INativeKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayoutService';
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
export const IKeyboardLayoutMainService = createDecorator<IKeyboardLayoutMainService>('keyboardLayoutMainService');
export interface IKeyboardLayoutMainService extends ICommonKeyboardLayoutMainService { }
export interface IKeyboardLayoutMainService extends INativeKeyboardLayoutService { }
export class KeyboardLayoutMainService extends Disposable implements ICommonKeyboardLayoutMainService {
export class KeyboardLayoutMainService extends Disposable implements INativeKeyboardLayoutService {
declare readonly _serviceBrand: undefined;

View file

@ -11,7 +11,7 @@ import { join } from 'vs/base/common/path';
import { rimraf, writeFile } from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils';
import { hashPath } from 'vs/workbench/services/backup/electron-browser/backupFileService';
import { hashPath } from 'vs/workbench/services/backup/common/backupFileService';
import { NativeBackupTracker } from 'vs/workbench/contrib/backup/electron-sandbox/backupTracker';
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';

View file

@ -36,7 +36,6 @@ import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider';
import { RemoteFileSystemProvider } from 'vs/workbench/services/remote/common/remoteAgentFileSystemChannel';
import { ConfigurationCache } from 'vs/workbench/services/configuration/electron-sandbox/configurationCache';
import { SignService } from 'vs/platform/sign/node/signService';
import { ISignService } from 'vs/platform/sign/common/sign';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { basename } from 'vs/base/common/path';
@ -51,6 +50,7 @@ import { KeyboardLayoutService } from 'vs/workbench/services/keybinding/electron
import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout';
import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
class DesktopMain extends Disposable {
@ -192,7 +192,7 @@ class DesktopMain extends Disposable {
// Sign
const signService = new SignService();
const signService = ProxyChannel.toService<ISignService>(mainProcessService.getChannel('sign'));
serviceCollection.set(ISignService, signService);
// Remote Agent
@ -251,7 +251,7 @@ class DesktopMain extends Disposable {
return service;
}),
this.createStorageService(payload, logService, mainProcessService).then(service => {
this.createStorageService(payload, mainProcessService).then(service => {
// Storage
serviceCollection.set(IStorageService, service);
@ -319,7 +319,7 @@ class DesktopMain extends Disposable {
}
}
private async createStorageService(payload: IWorkspaceInitializationPayload, logService: ILogService, mainProcessService: IMainProcessService): Promise<NativeStorageService> {
private async createStorageService(payload: IWorkspaceInitializationPayload, mainProcessService: IMainProcessService): Promise<NativeStorageService> {
const storageService = new NativeStorageService(payload, mainProcessService, this.environmentService);
try {

View file

@ -41,8 +41,9 @@ import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uri
import { KeyboardLayoutService } from 'vs/workbench/services/keybinding/electron-sandbox/nativeKeyboardLayout';
import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout';
import { ElectronIPCMainProcessService } from 'vs/platform/ipc/electron-sandbox/mainProcessService';
import { SimpleConfigurationService, simpleFileSystemProvider, SimpleSignService, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService, SimpleLogService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices';
import { SimpleConfigurationService, simpleFileSystemProvider, SimpleNativeWorkbenchEnvironmentService, SimpleWorkspaceService, SimpleLogService } from 'vs/workbench/electron-sandbox/sandbox.simpleservices';
import { LoggerChannelClient } from 'vs/platform/log/common/logIpc';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
class DesktopMain extends Disposable {
@ -176,7 +177,7 @@ class DesktopMain extends Disposable {
// Sign
const signService = new SimpleSignService();
const signService = ProxyChannel.toService<ISignService>(mainProcessService.getChannel('sign'));
serviceCollection.set(ISignService, signService);
// Remote Agent

View file

@ -6,29 +6,19 @@
/* eslint-disable code-no-standalone-editor */
/* eslint-disable code-import-patterns */
import { ISignService } from 'vs/platform/sign/common/sign';
import { URI } from 'vs/base/common/uri';
import { InMemoryFileSystemProvider } from 'vs/platform/files/common/inMemoryFilesystemProvider';
import { Event } from 'vs/base/common/event';
import { IAddressProvider } from 'vs/platform/remote/common/remoteAgentConnection';
import { ITelemetryData, ITelemetryInfo, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IExtension } from 'vs/platform/extensions/common/extensions';
import { SimpleConfigurationService as BaseSimpleConfigurationService } from 'vs/editor/standalone/browser/simpleServices';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IBackupFileService, IResolvedBackup } from 'vs/workbench/services/backup/common/backup';
import { ITextSnapshot } from 'vs/editor/common/model';
import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { ClassifiedEvent, GDPRClassification, StrictPropertyChecker } from 'vs/platform/telemetry/common/gdprTypings';
import { IKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayout';
import { isWindows } from 'vs/base/common/platform';
import { IWebviewService, WebviewContentOptions, WebviewElement, WebviewExtensionDescription, WebviewOptions, WebviewOverlay } from 'vs/workbench/contrib/webview/browser/webview';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { AbstractTextFileService } from 'vs/workbench/services/textfile/browser/textFileService';
import { IExtensionManagementServer, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ITunnelProvider, ITunnelService, RemoteTunnel, TunnelProviderFeatures } from 'vs/platform/remote/common/tunnel';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IManualSyncTask, IResourcePreview, ISyncResourceHandle, ISyncTask, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, IUserDataSyncStoreManagementService, SyncResource, SyncStatus, UserDataSyncStoreType } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncAccount, IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ITaskProvider, ITaskService, ITaskSummary, ProblemMatcherRunOptions, Task, TaskFilter, TaskTerminateResponse, WorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService';
import { Action } from 'vs/base/common/actions';
@ -36,18 +26,13 @@ import { LinkedMap } from 'vs/base/common/map';
import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder, WorkbenchState, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { CustomTask, ContributedTask, InMemoryTask, TaskRunSource, ConfiguringTask, TaskIdentifier, TaskSorter } from 'vs/workbench/contrib/tasks/common/tasks';
import { TaskSystemInfo } from 'vs/workbench/contrib/tasks/common/taskSystem';
import { IExtensionTipsService, IConfigBasedExtensionTip, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement';
import { IWorkspaceTagsService, Tags } from 'vs/workbench/contrib/tags/common/workspaceTags';
import { AbstractOutputChannelModelService, IOutputChannelModelService } from 'vs/workbench/contrib/output/common/outputChannelModel';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity';
import { INativeWorkbenchConfiguration, INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService';
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
import { IExtensionHostDebugParams } from 'vs/platform/environment/common/environment';
import type { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
import { Schemas } from 'vs/base/common/network';
import { BrowserKeyboardLayoutService } from 'vs/workbench/services/keybinding/browser/keyboardLayoutService';
import { TerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminalInstanceService';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration';
@ -185,17 +170,6 @@ export class SimpleConfigurationService extends BaseSimpleConfigurationService i
//#endregion
//#region Signing
export class SimpleSignService implements ISignService {
declare readonly _serviceBrand: undefined;
async sign(value: string): Promise<string> { return value; }
}
//#endregion
//#region Logger
export class SimpleLogService extends LogService {
@ -206,6 +180,8 @@ export class SimpleLogService extends LogService {
}
//#endregion
//#region Files
@ -408,29 +384,6 @@ module.exports = testRunner;`);
//#endregion
//#region Backup File
class SimpleBackupFileService implements IBackupFileService {
declare readonly _serviceBrand: undefined;
async hasBackups(): Promise<boolean> { return false; }
async discardResourceBackup(resource: URI): Promise<void> { }
async discardAllWorkspaceBackups(): Promise<void> { }
toBackupResource(resource: URI): URI { return resource; }
hasBackupSync(resource: URI, versionId?: number): boolean { return false; }
async getBackups(): Promise<URI[]> { return []; }
async resolve<T extends object>(resource: URI): Promise<IResolvedBackup<T> | undefined> { return undefined; }
async backup<T extends object>(resource: URI, content?: ITextSnapshot, versionId?: number, meta?: T): Promise<void> { }
async discardBackup(resource: URI): Promise<void> { }
async discardBackups(): Promise<void> { }
}
registerSingleton(IBackupFileService, SimpleBackupFileService);
//#endregion
//#region Extensions
class SimpleExtensionService extends NullExtensionService { }
@ -440,45 +393,6 @@ registerSingleton(IExtensionService, SimpleExtensionService);
//#endregion
//#region Telemetry
class SimpleTelemetryService implements ITelemetryService {
declare readonly _serviceBrand: undefined;
readonly sendErrorTelemetry = false;
readonly isOptedIn = false;
async publicLog(eventName: string, data?: ITelemetryData, anonymizeFilePaths?: boolean): Promise<void> { }
async publicLog2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyChecker<E, ClassifiedEvent<T>, 'Type of classified event does not match event properties'>, anonymizeFilePaths?: boolean): Promise<void> { }
async publicLogError(errorEventName: string, data?: ITelemetryData): Promise<void> { }
async publicLogError2<E extends ClassifiedEvent<T> = never, T extends GDPRClassification<T> = never>(eventName: string, data?: StrictPropertyChecker<E, ClassifiedEvent<T>, 'Type of classified event does not match event properties'>): Promise<void> { }
setEnabled(value: boolean): void { }
setExperimentProperty(name: string, value: string): void { }
async getTelemetryInfo(): Promise<ITelemetryInfo> {
return {
instanceId: 'someValue.instanceId',
sessionId: 'someValue.sessionId',
machineId: 'someValue.machineId',
firstSessionDate: 'someValue.firstSessionDate'
};
}
}
registerSingleton(ITelemetryService, SimpleTelemetryService);
//#endregion
//#region Keymap Service (borrowed from browser for now to enable keyboard access)
class SimpleKeyboardLayoutService extends BrowserKeyboardLayoutService { }
registerSingleton(IKeyboardLayoutService, SimpleKeyboardLayoutService);
//#endregion
//#region Webview
class SimpleWebviewService implements IWebviewService {
@ -506,24 +420,6 @@ registerSingleton(ITextFileService, SimpleTextFileService);
//#endregion
//#region extensions management
class SimpleExtensionManagementServerService implements IExtensionManagementServerService {
declare readonly _serviceBrand: undefined;
readonly localExtensionManagementServer = null;
readonly remoteExtensionManagementServer = null;
readonly webExtensionManagementServer = null;
getExtensionManagementServer(extension: IExtension): IExtensionManagementServer | null { return null; }
}
registerSingleton(IExtensionManagementServerService, SimpleExtensionManagementServerService);
//#endregion
//#region Tunnel
class SimpleTunnelService implements ITunnelService {
@ -548,105 +444,6 @@ registerSingleton(ITunnelService, SimpleTunnelService);
//#endregion
//#region User Data Sync
class SimpleUserDataSyncService implements IUserDataSyncService {
declare readonly _serviceBrand: undefined;
onDidChangeStatus = Event.None;
onDidChangeConflicts = Event.None;
onDidChangeLocal = Event.None;
onSyncErrors = Event.None;
onDidChangeLastSyncTime = Event.None;
onDidResetRemote = Event.None;
onDidResetLocal = Event.None;
status: SyncStatus = SyncStatus.Idle;
conflicts: [SyncResource, IResourcePreview[]][] = [];
lastSyncTime = undefined;
createSyncTask(): Promise<ISyncTask> { throw new Error('Method not implemented.'); }
createManualSyncTask(): Promise<IManualSyncTask> { throw new Error('Method not implemented.'); }
async replace(uri: URI): Promise<void> { }
async reset(): Promise<void> { }
async resetRemote(): Promise<void> { }
async resetLocal(): Promise<void> { }
async hasLocalData(): Promise<boolean> { return false; }
async hasPreviouslySynced(): Promise<boolean> { return false; }
async resolveContent(resource: URI): Promise<string | null> { return null; }
async accept(resource: SyncResource, conflictResource: URI, content: string | null | undefined, apply: boolean): Promise<void> { }
async getLocalSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> { return []; }
async getRemoteSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> { return []; }
async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI; comparableResource: URI; }[]> { return []; }
async getMachineId(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<string | undefined> { return undefined; }
}
registerSingleton(IUserDataSyncService, SimpleUserDataSyncService);
//#endregion
//#region User Data Sync Account
class SimpleUserDataSyncAccountService implements IUserDataSyncAccountService {
declare readonly _serviceBrand: undefined;
onTokenFailed = Event.None;
onDidChangeAccount = Event.None;
account: IUserDataSyncAccount | undefined = undefined;
async updateAccount(account: IUserDataSyncAccount | undefined): Promise<void> { }
}
registerSingleton(IUserDataSyncAccountService, SimpleUserDataSyncAccountService);
//#endregion
//#region User Data Auto Sync Account
class SimpleUserDataAutoSyncAccountService implements IUserDataAutoSyncService {
declare readonly _serviceBrand: undefined;
onError = Event.None;
onDidChangeEnablement = Event.None;
isEnabled(): boolean { return false; }
canToggleEnablement(): boolean { return false; }
async turnOn(): Promise<void> { }
async turnOff(everywhere: boolean): Promise<void> { }
async triggerSync(sources: string[], hasToLimitSync: boolean, disableCache: boolean): Promise<void> { }
}
registerSingleton(IUserDataAutoSyncService, SimpleUserDataAutoSyncAccountService);
//#endregion
//#region User Data Sync Store Management
class SimpleUserDataSyncStoreManagementService implements IUserDataSyncStoreManagementService {
declare readonly _serviceBrand: undefined;
onDidChangeUserDataSyncStore = Event.None;
userDataSyncStore: IUserDataSyncStore | undefined = undefined;
async switch(type: UserDataSyncStoreType): Promise<void> { }
async getPreviousUserDataSyncStore(): Promise<IUserDataSyncStore | undefined> { return undefined; }
}
registerSingleton(IUserDataSyncStoreManagementService, SimpleUserDataSyncStoreManagementService);
//#endregion
//#region Task
class SimpleTaskService implements ITaskService {
@ -694,67 +491,6 @@ registerSingleton(ITaskService, SimpleTaskService);
//#endregion
//#region Extension Tips
class SimpleExtensionTipsService implements IExtensionTipsService {
declare readonly _serviceBrand: undefined;
onRecommendationChange = Event.None;
async getConfigBasedTips(folder: URI): Promise<IConfigBasedExtensionTip[]> { return []; }
async getImportantExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> { return []; }
async getOtherExecutableBasedTips(): Promise<IExecutableBasedExtensionTip[]> { return []; }
async getAllWorkspacesTips(): Promise<IWorkspaceTips[]> { return []; }
}
registerSingleton(IExtensionTipsService, SimpleExtensionTipsService);
//#endregion
//#region Workspace Tags
class SimpleWorkspaceTagsService implements IWorkspaceTagsService {
declare readonly _serviceBrand: undefined;
async getTags(): Promise<Tags> { return Object.create(null); }
async getTelemetryWorkspaceId(workspace: IWorkspace, state: WorkbenchState): Promise<string | undefined> { return undefined; }
async getHashedRemotesFromUri(workspaceUri: URI, stripEndingDotGit?: boolean): Promise<string[]> { return []; }
}
registerSingleton(IWorkspaceTagsService, SimpleWorkspaceTagsService);
//#endregion
//#region Output Channel
class SimpleOutputChannelModelService extends AbstractOutputChannelModelService {
declare readonly _serviceBrand: undefined;
}
registerSingleton(IOutputChannelModelService, SimpleOutputChannelModelService);
//#endregion
//#region Integrity
class SimpleIntegrityService implements IIntegrityService {
declare readonly _serviceBrand: undefined;
async isPure(): Promise<IntegrityTestResult> {
return { isPure: true, proof: [] };
}
}
registerSingleton(IIntegrityService, SimpleIntegrityService);
//#endregion
//#region Terminal Instance
class SimpleTerminalInstanceService extends TerminalInstanceService { }

View file

@ -7,9 +7,6 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ILogService } from 'vs/platform/log/common/log';
import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService';
import { hash } from 'vs/base/common/hash';
import { Schemas } from 'vs/base/common/network';
import { URI } from 'vs/base/common/uri';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { joinPath } from 'vs/base/common/resources';
@ -27,12 +24,6 @@ export class BrowserBackupFileService extends BackupFileService {
) {
super(joinPath(environmentService.userRoamingDataHome, 'Backups', contextService.getWorkspace().id), fileService, logService);
}
protected hashPath(resource: URI): string {
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
return hash(str).toString(16);
}
}
registerSingleton(IBackupFileService, BrowserBackupFileService);

View file

@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { join } from 'vs/base/common/path';
import { joinPath } from 'vs/base/common/resources';
import { basename, isEqual, joinPath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { coalesce } from 'vs/base/common/arrays';
import { equals, deepClone } from 'vs/base/common/objects';
@ -19,14 +19,19 @@ import { TextSnapshotReadable, stringToSnapshot } from 'vs/workbench/services/te
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService } from 'vs/platform/log/common/log';
import { CancellationToken } from 'vs/base/common/cancellation';
import { Schemas } from 'vs/base/common/network';
import { hash } from 'vs/base/common/hash';
export interface IBackupFilesModel {
resolve(backupRoot: URI): Promise<IBackupFilesModel>;
resolve(backupRoot: URI): Promise<void>;
get(): URI[];
has(resource: URI, versionId?: number, meta?: object): boolean;
add(resource: URI, versionId?: number, meta?: object): void;
has(resource: URI, versionId?: number, meta?: object): boolean;
get(): URI[];
remove(resource: URI): void;
move(source: URI, target: URI): void;
count(): number;
clear(): void;
@ -43,7 +48,7 @@ export class BackupFilesModel implements IBackupFilesModel {
constructor(private fileService: IFileService) { }
async resolve(backupRoot: URI): Promise<IBackupFilesModel> {
async resolve(backupRoot: URI): Promise<void> {
try {
const backupRootStat = await this.fileService.resolve(backupRoot);
if (backupRootStat.children) {
@ -63,8 +68,6 @@ export class BackupFilesModel implements IBackupFilesModel {
} catch (error) {
// ignore any errors
}
return this;
}
add(resource: URI, versionId = 0, meta?: object): void {
@ -100,6 +103,14 @@ export class BackupFilesModel implements IBackupFilesModel {
this.cache.delete(resource);
}
move(source: URI, target: URI): void {
const entry = this.cache.get(source);
if (entry) {
this.cache.delete(source);
this.cache.set(target, entry);
}
}
clear(): void {
this.cache.clear();
}
@ -119,7 +130,9 @@ export abstract class BackupFileService implements IBackupFileService {
this.impl = this.initialize(backupWorkspaceHome);
}
protected abstract hashPath(resource: URI): string;
private hashPath(resource: URI): string {
return hashPath(resource);
}
private initialize(backupWorkspaceHome: URI | undefined): BackupFileServiceImpl | InMemoryBackupFileService {
if (backupWorkspaceHome) {
@ -206,10 +219,46 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService {
this.ready = this.doInitialize();
}
private doInitialize(): Promise<IBackupFilesModel> {
private async doInitialize(): Promise<IBackupFilesModel> {
this.model = new BackupFilesModel(this.fileService);
return this.model.resolve(this.backupWorkspacePath);
// Resolve backup model
await this.model.resolve(this.backupWorkspacePath);
// Migrate hashes as needed. We used to hash with a MD5
// sum of the path but switched to our own simpler hash
// to avoid a node.js dependency. We still want to
// support the older hash so we:
// - iterate over all backups
// - detect if the file name length is 32 (MD5 length)
// - read the backup's target file path
// - rename the backup to the new hash
// - update the backup in our model
//
// TODO@bpasero remove me eventually
for (const backupResource of this.model.get()) {
if (basename(backupResource).length !== 32) {
continue; // not a MD5 hash, already uses new hash function
}
try {
const resource = await this.readUri(backupResource);
if (!resource) {
this.logService.warn(`Backup: Unable to read target URI of backup ${backupResource} for migration to new hash.`);
continue;
}
const expectedBackupResource = this.toBackupResource(resource);
if (!isEqual(expectedBackupResource, backupResource)) {
await this.fileService.move(backupResource, expectedBackupResource, true);
this.model.move(backupResource, expectedBackupResource);
}
} catch (error) {
this.logService.error(`Backup: Unable to migrate backup ${backupResource} to new hash.`);
}
}
return this.model;
}
async hasBackups(): Promise<boolean> {
@ -300,29 +349,31 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService {
async getBackups(): Promise<URI[]> {
const model = await this.ready;
const backups = await Promise.all(model.get().map(async backupResource => {
const backupPreamble = await this.readToMatchingString(backupResource, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH);
if (!backupPreamble) {
return undefined;
}
// Preamble with metadata: URI + META-START + Meta + END
const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR);
if (metaStartIndex > 0) {
return URI.parse(backupPreamble.substring(0, metaStartIndex));
}
// Preamble without metadata: URI + END
else {
return URI.parse(backupPreamble);
}
}));
const backups = await Promise.all(model.get().map(backupResource => this.readUri(backupResource)));
return coalesce(backups);
}
private async readToMatchingString(file: URI, matchingString: string, maximumBytesToRead: number): Promise<string | undefined> {
const contents = (await this.fileService.readFile(file, { length: maximumBytesToRead })).value.toString();
private async readUri(backupResource: URI): Promise<URI | undefined> {
const backupPreamble = await this.readToMatchingString(backupResource, BackupFileServiceImpl.PREAMBLE_END_MARKER, BackupFileServiceImpl.PREAMBLE_MAX_LENGTH);
if (!backupPreamble) {
return undefined;
}
// Preamble with metadata: URI + META-START + Meta + END
const metaStartIndex = backupPreamble.indexOf(BackupFileServiceImpl.PREAMBLE_META_SEPARATOR);
if (metaStartIndex > 0) {
return URI.parse(backupPreamble.substring(0, metaStartIndex));
}
// Preamble without metadata: URI + END
else {
return URI.parse(backupPreamble);
}
}
private async readToMatchingString(backupResource: URI, matchingString: string, maximumBytesToRead: number): Promise<string | undefined> {
const contents = (await this.fileService.readFile(backupResource, { length: maximumBytesToRead })).value.toString();
const matchingStringIndex = contents.indexOf(matchingString);
if (matchingStringIndex >= 0) {
@ -449,3 +500,12 @@ export class InMemoryBackupFileService implements IBackupFileService {
return URI.file(join(resource.scheme, this.hashPath(resource)));
}
}
/*
* Exported only for testing
*/
export function hashPath(resource: URI): string {
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
return hash(str).toString(16);
}

View file

@ -5,8 +5,6 @@
import { BackupFileService } from 'vs/workbench/services/backup/common/backupFileService';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import * as crypto from 'crypto';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IFileService } from 'vs/platform/files/common/files';
@ -22,19 +20,6 @@ export class NativeBackupFileService extends BackupFileService {
) {
super(environmentService.configuration.backupPath ? URI.file(environmentService.configuration.backupPath).with({ scheme: environmentService.userRoamingDataHome.scheme }) : undefined, fileService, logService);
}
protected hashPath(resource: URI): string {
return hashPath(resource);
}
}
/*
* Exported only for testing
*/
export function hashPath(resource: URI): string {
const str = resource.scheme === Schemas.file || resource.scheme === Schemas.untitled ? resource.fsPath : resource.toString();
return crypto.createHash('md5').update(str).digest('hex');
}
registerSingleton(IBackupFileService, NativeBackupFileService);

View file

@ -5,13 +5,12 @@
import * as assert from 'assert';
import { isWindows } from 'vs/base/common/platform';
import { createHash } from 'crypto';
import { tmpdir } from 'os';
import { promises, existsSync, readFileSync, writeFileSync } from 'fs';
import { promises, existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
import { dirname, join } from 'vs/base/common/path';
import { readdirSync, rimraf, writeFile } from 'vs/base/node/pfs';
import { URI } from 'vs/base/common/uri';
import { BackupFilesModel } from 'vs/workbench/services/backup/common/backupFileService';
import { BackupFilesModel, hashPath } from 'vs/workbench/services/backup/common/backupFileService';
import { createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { createTextModel } from 'vs/editor/test/common/editorTestUtils';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
@ -23,13 +22,15 @@ import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemPro
import { NativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService';
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
import { IFileService } from 'vs/platform/files/common/files';
import { hashPath, NativeBackupFileService } from 'vs/workbench/services/backup/electron-browser/backupFileService';
import { NativeBackupFileService } from 'vs/workbench/services/backup/electron-sandbox/backupFileService';
import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider';
import { VSBuffer } from 'vs/base/common/buffer';
import { TestWorkbenchConfiguration } from 'vs/workbench/test/electron-browser/workbenchTestServices';
import { TestProductService } from 'vs/workbench/test/browser/workbenchTestServices';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { insert } from 'vs/base/common/arrays';
import { hash } from 'vs/base/common/hash';
import { isEqual } from 'vs/base/common/resources';
class TestWorkbenchEnvironmentService extends NativeWorkbenchEnvironmentService {
@ -118,6 +119,7 @@ suite('BackupFileService', () => {
let fooBackupPath: string;
let barBackupPath: string;
let untitledBackupPath: string;
let customFileBackupPath: string;
let service: NodeTestBackupFileService;
@ -134,9 +136,10 @@ suite('BackupFileService', () => {
backupHome = join(testDir, 'Backups');
workspacesJsonPath = join(backupHome, 'workspaces.json');
workspaceBackupPath = join(backupHome, hashPath(workspaceResource));
fooBackupPath = join(workspaceBackupPath, 'file', hashPath(fooFile));
barBackupPath = join(workspaceBackupPath, 'file', hashPath(barFile));
untitledBackupPath = join(workspaceBackupPath, 'untitled', hashPath(untitledFile));
fooBackupPath = join(workspaceBackupPath, fooFile.scheme, hashPath(fooFile));
barBackupPath = join(workspaceBackupPath, barFile.scheme, hashPath(barFile));
untitledBackupPath = join(workspaceBackupPath, untitledFile.scheme, hashPath(untitledFile));
customFileBackupPath = join(workspaceBackupPath, customFile.scheme, hashPath(customFile));
service = new NodeTestBackupFileService(testDir, workspaceBackupPath);
@ -157,8 +160,8 @@ suite('BackupFileService', () => {
});
const actual = hashPath(uri);
// If these hashes change people will lose their backed up files!
assert.strictEqual(actual, '13264068d108c6901b3592ea654fcd57');
assert.strictEqual(actual, createHash('md5').update(uri.fsPath).digest('hex'));
assert.strictEqual(actual, '-7f9c1a2e');
assert.strictEqual(actual, hash(uri.fsPath).toString(16));
});
test('should correctly hash the path for file scheme URIs', () => {
@ -166,11 +169,22 @@ suite('BackupFileService', () => {
const actual = hashPath(uri);
// If these hashes change people will lose their backed up files!
if (isWindows) {
assert.strictEqual(actual, 'dec1a583f52468a020bd120c3f01d812');
assert.strictEqual(actual, '20ffaa13');
} else {
assert.strictEqual(actual, '1effb2475fcfba4f9e8b8a1dbc8f3caf');
assert.strictEqual(actual, '20eb3560');
}
assert.strictEqual(actual, createHash('md5').update(uri.fsPath).digest('hex'));
assert.strictEqual(actual, hash(uri.fsPath).toString(16));
});
test('should correctly hash the path for custom scheme URIs', () => {
const uri = URI.from({
scheme: 'vscode-custom',
path: 'somePath'
});
const actual = hashPath(uri);
// If these hashes change people will lose their backed up files!
assert.strictEqual(actual, '-44972d98');
assert.strictEqual(actual, hash(uri.toString()).toString(16));
});
});
@ -636,6 +650,11 @@ suite('BackupFileService', () => {
assert.strictEqual(model.has(resource4), true);
assert.strictEqual(model.has(resource4, undefined, { foo: 'bar' }), true);
assert.strictEqual(model.has(resource4, undefined, { bar: 'foo' }), false);
const resource5 = URI.file('test4.html');
model.move(resource4, resource5);
assert.strictEqual(model.has(resource4), false);
assert.strictEqual(model.has(resource5), true);
});
test('resolve', async () => {
@ -643,8 +662,8 @@ suite('BackupFileService', () => {
writeFileSync(fooBackupPath, 'foo');
const model = new BackupFilesModel(service.fileService);
const resolvedModel = await model.resolve(URI.file(workspaceBackupPath));
assert.strictEqual(resolvedModel.has(URI.file(fooBackupPath)), true);
await model.resolve(URI.file(workspaceBackupPath));
assert.strictEqual(model.has(URI.file(fooBackupPath)), true);
});
test('get', () => {
@ -664,4 +683,40 @@ suite('BackupFileService', () => {
});
});
suite('Hash migration', () => {
test('works', async () => {
// Prepare backups of the old MD5 hash format
mkdirSync(join(workspaceBackupPath, fooFile.scheme), { recursive: true });
mkdirSync(join(workspaceBackupPath, untitledFile.scheme), { recursive: true });
mkdirSync(join(workspaceBackupPath, customFile.scheme), { recursive: true });
writeFileSync(join(workspaceBackupPath, fooFile.scheme, '8a8589a2f1c9444b89add38166f50229'), `${fooFile.toString()}\ntest file`);
writeFileSync(join(workspaceBackupPath, untitledFile.scheme, '13264068d108c6901b3592ea654fcd57'), `${untitledFile.toString()}\ntest untitled`);
writeFileSync(join(workspaceBackupPath, customFile.scheme, 'bf018572af7b38746b502893bd0adf6c'), `${customFile.toString()}\ntest custom`);
service.reinitialize(URI.file(workspaceBackupPath));
const backups = await service.getBackups();
assert.strictEqual(backups.length, 3);
assert.ok(backups.some(backup => isEqual(backup, fooFile)));
assert.ok(backups.some(backup => isEqual(backup, untitledFile)));
assert.ok(backups.some(backup => isEqual(backup, customFile)));
assert.strictEqual(readdirSync(join(workspaceBackupPath, fooFile.scheme)).length, 1);
assert.strictEqual(existsSync(fooBackupPath), true);
assert.strictEqual(readFileSync(fooBackupPath).toString(), `${fooFile.toString()}\ntest file`);
assert.ok(service.hasBackupSync(fooFile));
assert.strictEqual(readdirSync(join(workspaceBackupPath, untitledFile.scheme)).length, 1);
assert.strictEqual(existsSync(untitledBackupPath), true);
assert.strictEqual(readFileSync(untitledBackupPath).toString(), `${untitledFile.toString()}\ntest untitled`);
assert.ok(service.hasBackupSync(untitledFile));
assert.strictEqual(readdirSync(join(workspaceBackupPath, customFile.scheme)).length, 1);
assert.strictEqual(existsSync(customFileBackupPath), true);
assert.strictEqual(readFileSync(customFileBackupPath).toString(), `${customFile.toString()}\ntest custom`);
assert.ok(service.hasBackupSync(customFile));
});
});
});

View file

@ -3,9 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import * as crypto from 'crypto';
import * as fs from 'fs';
import { localize } from 'vs/nls';
import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { ChecksumPair, IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity';
@ -16,6 +14,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { FileAccess } from 'vs/base/common/network';
import { IChecksumService } from 'vs/platform/checksum/common/checksumService';
interface IStorageData {
dontShowPrompt: boolean;
@ -67,7 +66,8 @@ export class IntegrityServiceImpl implements IIntegrityService {
@IStorageService storageService: IStorageService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
@IOpenerService private readonly openerService: IOpenerService,
@IProductService private readonly productService: IProductService
@IProductService private readonly productService: IProductService,
@IChecksumService private readonly checksumService: IChecksumService
) {
this._storage = new IntegrityStorage(storageService);
@ -89,18 +89,18 @@ export class IntegrityServiceImpl implements IIntegrityService {
}
const checksumFailMoreInfoUrl = this.productService.checksumFailMoreInfoUrl;
const message = nls.localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", this.productService.nameShort);
const message = localize('integrity.prompt', "Your {0} installation appears to be corrupt. Please reinstall.", this.productService.nameShort);
if (checksumFailMoreInfoUrl) {
this.notificationService.prompt(
Severity.Warning,
message,
[
{
label: nls.localize('integrity.moreInformation', "More Information"),
label: localize('integrity.moreInformation', "More Information"),
run: () => this.openerService.open(URI.parse(checksumFailMoreInfoUrl))
},
{
label: nls.localize('integrity.dontShowAgain', "Don't Show Again"),
label: localize('integrity.dontShowAgain', "Don't Show Again"),
isSecondary: true,
run: () => this._storage.set({ dontShowPrompt: true, commit: this.productService.commit })
}
@ -141,26 +141,16 @@ export class IntegrityServiceImpl implements IIntegrityService {
};
}
private _resolve(filename: string, expected: string): Promise<ChecksumPair> {
private async _resolve(filename: string, expected: string): Promise<ChecksumPair> {
const fileUri = FileAccess.asFileUri(filename, require);
return new Promise<ChecksumPair>((resolve, reject) => {
fs.readFile(fileUri.fsPath, (err, buff) => {
if (err) {
return resolve(IntegrityServiceImpl._createChecksumPair(fileUri, '', expected));
}
resolve(IntegrityServiceImpl._createChecksumPair(fileUri, this._computeChecksum(buff), expected));
});
});
}
private _computeChecksum(buff: Buffer): string {
let hash = crypto
.createHash('md5')
.update(buff)
.digest('base64')
.replace(/=+$/, '');
try {
const checksum = await this.checksumService.checksum(fileUri);
return hash;
return IntegrityServiceImpl._createChecksumPair(fileUri, checksum, expected);
} catch (error) {
return IntegrityServiceImpl._createChecksumPair(fileUri, '', expected);
}
}
private static _createChecksumPair(uri: URI, actual: string, expected: string): ChecksumPair {

View file

@ -14,7 +14,7 @@ import { MacLinuxKeyboardMapper } from 'vs/workbench/services/keybinding/common/
import { DispatchConfig } from 'vs/platform/keyboardLayout/common/dispatchConfig';
import { IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
import { IMainProcessService } from 'vs/platform/ipc/electron-sandbox/services';
import { IKeyboardLayoutMainService } from 'vs/platform/keyboardLayout/common/keyboardLayoutMainService';
import { INativeKeyboardLayoutService } from 'vs/platform/keyboardLayout/common/keyboardLayoutService';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
export class KeyboardLayoutService extends Disposable implements IKeyboardLayoutService {
@ -24,7 +24,7 @@ export class KeyboardLayoutService extends Disposable implements IKeyboardLayout
private readonly _onDidChangeKeyboardLayout = this._register(new Emitter<void>());
readonly onDidChangeKeyboardLayout = this._onDidChangeKeyboardLayout.event;
private readonly _keyboardLayoutMainService: IKeyboardLayoutMainService;
private readonly _keyboardLayoutService: INativeKeyboardLayoutService;
private _initPromise: Promise<void> | null;
private _keyboardMapping: IKeyboardMapping | null;
private _keyboardLayoutInfo: IKeyboardLayoutInfo | null;
@ -34,13 +34,13 @@ export class KeyboardLayoutService extends Disposable implements IKeyboardLayout
@IMainProcessService mainProcessService: IMainProcessService
) {
super();
this._keyboardLayoutMainService = ProxyChannel.toService<IKeyboardLayoutMainService>(mainProcessService.getChannel('keyboardLayout'));
this._keyboardLayoutService = ProxyChannel.toService<INativeKeyboardLayoutService>(mainProcessService.getChannel('keyboardLayout'));
this._initPromise = null;
this._keyboardMapping = null;
this._keyboardLayoutInfo = null;
this._keyboardMapper = new MacLinuxFallbackKeyboardMapper(OS);
this._register(this._keyboardLayoutMainService.onDidChangeKeyboardLayout(async ({ keyboardLayoutInfo, keyboardMapping }) => {
this._register(this._keyboardLayoutService.onDidChangeKeyboardLayout(async ({ keyboardLayoutInfo, keyboardMapping }) => {
await this.initialize();
if (keyboardMappingEquals(this._keyboardMapping, keyboardMapping)) {
// the mappings are equal
@ -62,7 +62,7 @@ export class KeyboardLayoutService extends Disposable implements IKeyboardLayout
}
private async _doInitialize(): Promise<void> {
const keyboardLayoutData = await this._keyboardLayoutMainService.getKeyboardLayoutData();
const keyboardLayoutData = await this._keyboardLayoutService.getKeyboardLayoutData();
const { keyboardLayoutInfo, keyboardMapping } = keyboardLayoutData;
this._keyboardMapping = keyboardMapping;
this._keyboardLayoutInfo = keyboardLayoutInfo;

View file

@ -57,11 +57,9 @@ import 'vs/workbench/electron-browser/desktop.main';
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
import 'vs/workbench/services/integrity/node/integrityService';
import 'vs/workbench/services/search/electron-browser/searchService';
import 'vs/workbench/services/textfile/electron-browser/nativeTextFileService';
import 'vs/workbench/services/extensions/electron-browser/extensionService';
import 'vs/workbench/services/backup/electron-browser/backupFileService';
import 'vs/workbench/services/remote/electron-browser/tunnelServiceImpl';

View file

@ -62,6 +62,9 @@ import 'vs/workbench/services/userDataSync/electron-sandbox/userDataAutoSyncServ
import 'vs/workbench/services/ipc/electron-sandbox/sharedProcessService';
import 'vs/workbench/services/timer/electron-sandbox/timerService';
import 'vs/workbench/services/environment/electron-sandbox/shellEnvironmentService';
import 'vs/workbench/services/integrity/electron-sandbox/integrityService';
import 'vs/platform/checksum/electron-sandbox/checksumService';
import 'vs/workbench/services/backup/electron-sandbox/backupFileService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IUserDataInitializationService, UserDataInitializationService } from 'vs/workbench/services/userData/browser/userDataInit';