mirror of
https://github.com/Microsoft/vscode
synced 2024-09-18 01:58:27 +00:00
Merge pull request #118418 from microsoft/ben/sandbox
Make more services available in electron sandbox
This commit is contained in:
commit
c2a92a1ba6
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
19
src/vs/platform/checksum/common/checksumService.ts
Normal file
19
src/vs/platform/checksum/common/checksumService.ts
Normal 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>;
|
||||
}
|
|
@ -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 });
|
30
src/vs/platform/checksum/node/checksumService.ts
Normal file
30
src/vs/platform/checksum/node/checksumService.ts
Normal 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(/=+$/, ''))
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
38
src/vs/platform/checksum/test/node/checksumService.test.ts
Normal file
38
src/vs/platform/checksum/test/node/checksumService.test.ts
Normal 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
|
||||
});
|
||||
});
|
1135
src/vs/platform/checksum/test/node/fixtures/lorem.txt
Normal file
1135
src/vs/platform/checksum/test/node/fixtures/lorem.txt
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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>;
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 { }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,7 +349,12 @@ 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 backups = await Promise.all(model.get().map(backupResource => this.readUri(backupResource)));
|
||||
|
||||
return coalesce(backups);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -316,13 +370,10 @@ class BackupFileServiceImpl extends Disposable implements IBackupFileService {
|
|||
else {
|
||||
return URI.parse(backupPreamble);
|
||||
}
|
||||
}));
|
||||
|
||||
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 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);
|
||||
}
|
||||
|
|
|
@ -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);
|
|
@ -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));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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 {
|
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
|
||||
|
||||
|
|
|
@ -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';
|
||||
|
|
Loading…
Reference in a new issue