mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-04 01:25:38 -05:00
Merge from vscode e3c4990c67c40213af168300d1cfeb71d680f877 (#16569)
This commit is contained in:
@@ -56,8 +56,7 @@ export interface IOpenedWindow {
|
||||
readonly dirty: boolean;
|
||||
}
|
||||
|
||||
export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions {
|
||||
}
|
||||
export interface IOpenEmptyWindowOptions extends IBaseOpenWindowsOptions { }
|
||||
|
||||
export type IWindowOpenable = IWorkspaceToOpen | IFolderToOpen | IFileToOpen;
|
||||
|
||||
@@ -233,6 +232,31 @@ export interface IOSConfiguration {
|
||||
readonly hostname: string;
|
||||
}
|
||||
|
||||
export interface IPartsSplash {
|
||||
baseTheme: string;
|
||||
colorInfo: {
|
||||
background: string;
|
||||
foreground: string | undefined;
|
||||
editorBackground: string | undefined;
|
||||
titleBarBackground: string | undefined;
|
||||
activityBarBackground: string | undefined;
|
||||
sideBarBackground: string | undefined;
|
||||
statusBarBackground: string | undefined;
|
||||
statusBarNoFolderBackground: string | undefined;
|
||||
windowBorder: string | undefined;
|
||||
}
|
||||
layoutInfo: {
|
||||
sideBarSide: string;
|
||||
editorPartMinWidth: number;
|
||||
titleBarHeight: number;
|
||||
activityBarWidth: number;
|
||||
sideBarWidth: number;
|
||||
statusBarHeight: number;
|
||||
windowBorder: boolean;
|
||||
windowBorderRadius: string | undefined;
|
||||
} | undefined
|
||||
}
|
||||
|
||||
export interface INativeWindowConfiguration extends IWindowConfiguration, NativeParsedArgs, ISandboxConfiguration {
|
||||
mainPid: number;
|
||||
|
||||
@@ -245,7 +269,7 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
|
||||
tmpDir: string;
|
||||
userDataDir: string;
|
||||
|
||||
partsSplashPath: string;
|
||||
partsSplash?: IPartsSplash;
|
||||
|
||||
workspace?: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
|
||||
import { getMarks, mark } from 'vs/base/common/performance';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, Event, RenderProcessGoneDetails, WebFrameMain } from 'electron';
|
||||
import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, Event, WebFrameMain } from 'electron';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
@@ -186,7 +186,6 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
[`--vscode-window-config=${this.configObjectUrl.resource.toString()}`],
|
||||
v8CacheOptions: browserCodeLoadingCacheStrategy,
|
||||
enableWebSQL: false,
|
||||
enableRemoteModule: false,
|
||||
spellcheck: false,
|
||||
nativeWindowOpen: true,
|
||||
webviewTag: true,
|
||||
@@ -207,9 +206,9 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
};
|
||||
|
||||
if (browserCodeLoadingCacheStrategy) {
|
||||
this.logService.info(`window#ctor: using vscode-file:// protocol and V8 cache options: ${browserCodeLoadingCacheStrategy}`);
|
||||
this.logService.info(`window: using vscode-file:// protocol and V8 cache options: ${browserCodeLoadingCacheStrategy}`);
|
||||
} else {
|
||||
this.logService.trace(`window#ctor: vscode-file:// protocol is explicitly disabled`);
|
||||
this.logService.info(`window: vscode-file:// protocol is explicitly disabled`);
|
||||
}
|
||||
|
||||
// Apply icon to window
|
||||
@@ -420,7 +419,16 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
// Crashes & Unresponsive & Failed to load
|
||||
this._win.on('unresponsive', () => this.onWindowError(WindowError.UNRESPONSIVE));
|
||||
this._win.webContents.on('render-process-gone', (event, details) => this.onWindowError(WindowError.CRASHED, details));
|
||||
this._win.webContents.on('did-fail-load', (event, errorCode, errorDescription) => this.onWindowError(WindowError.LOAD, errorDescription));
|
||||
this._win.webContents.on('did-fail-load', (event, exitCode, reason) => this.onWindowError(WindowError.LOAD, { reason, exitCode }));
|
||||
|
||||
// Prevent windows/iframes from blocking the unload
|
||||
// through DOM events. We have our own logic for
|
||||
// unloading a window that should not be confused
|
||||
// with the DOM way.
|
||||
// (https://github.com/microsoft/vscode/issues/122736)
|
||||
this._win.webContents.on('will-prevent-unload', event => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
// Window close
|
||||
this._win.on('closed', () => {
|
||||
@@ -430,7 +438,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
});
|
||||
|
||||
// Block all SVG requests from unsupported origins
|
||||
const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, 'devtools']); // TODO: handle webview origin
|
||||
const supportedSvgSchemes = new Set([Schemas.file, Schemas.vscodeFileResource, Schemas.vscodeRemoteResource, 'devtools']); // TODO@mjbvz: handle webview origin
|
||||
|
||||
// But allow them if the are made from inside an webview
|
||||
const isSafeFrame = (requestFrame: WebFrameMain | undefined): boolean => {
|
||||
@@ -442,13 +450,16 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
return false;
|
||||
};
|
||||
|
||||
const isRequestFromSafeContext = (details: Electron.OnBeforeRequestListenerDetails | Electron.OnHeadersReceivedListenerDetails): boolean => {
|
||||
return details.resourceType === 'xhr' || isSafeFrame(details.frame);
|
||||
};
|
||||
|
||||
this._win.webContents.session.webRequest.onBeforeRequest((details, callback) => {
|
||||
const uri = URI.parse(details.url);
|
||||
if (uri.path.endsWith('.svg')) {
|
||||
const isSafeResourceUrl = supportedSvgSchemes.has(uri.scheme) || uri.path.includes(Schemas.vscodeRemoteResource);
|
||||
if (!isSafeResourceUrl) {
|
||||
const isSafeContext = isSafeFrame(details.frame);
|
||||
return callback({ cancel: !isSafeContext });
|
||||
return callback({ cancel: !isRequestFromSafeContext(details) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -474,8 +485,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
// remote extension schemes have the following format
|
||||
// http://127.0.0.1:<port>/vscode-remote-resource?path=
|
||||
if (!uri.path.includes(Schemas.vscodeRemoteResource) && contentTypes.some(contentType => contentType.toLowerCase().includes('image/svg'))) {
|
||||
const isSafeContext = isSafeFrame(details.frame);
|
||||
return callback({ cancel: !isSafeContext });
|
||||
return callback({ cancel: !isRequestFromSafeContext(details) });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -541,19 +551,19 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
}
|
||||
|
||||
private async onWindowError(error: WindowError.UNRESPONSIVE): Promise<void>;
|
||||
private async onWindowError(error: WindowError.CRASHED, details: RenderProcessGoneDetails): Promise<void>;
|
||||
private async onWindowError(error: WindowError.LOAD, details: string): Promise<void>;
|
||||
private async onWindowError(type: WindowError, details?: string | RenderProcessGoneDetails): Promise<void> {
|
||||
private async onWindowError(error: WindowError.CRASHED, details: { reason: string, exitCode: number }): Promise<void>;
|
||||
private async onWindowError(error: WindowError.LOAD, details: { reason: string, exitCode: number }): Promise<void>;
|
||||
private async onWindowError(type: WindowError, details?: { reason: string, exitCode: number }): Promise<void> {
|
||||
|
||||
switch (type) {
|
||||
case WindowError.CRASHED:
|
||||
this.logService.error(`CodeWindow: renderer process crashed (detail: ${typeof details === 'string' ? details : details?.reason})`);
|
||||
this.logService.error(`CodeWindow: renderer process crashed (reason: ${details?.reason || '<unknown>'}, code: ${details?.exitCode || '<unknown>'})`);
|
||||
break;
|
||||
case WindowError.UNRESPONSIVE:
|
||||
this.logService.error('CodeWindow: detected unresponsive');
|
||||
break;
|
||||
case WindowError.LOAD:
|
||||
this.logService.error(`CodeWindow: failed to load workbench window: ${typeof details === 'string' ? details : details?.reason}`);
|
||||
this.logService.error(`CodeWindow: failed to load workbench window (reason: ${details?.reason || '<unknown>'}, code: ${details?.exitCode || '<unknown>'})`);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -569,12 +579,14 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
type WindowErrorClassification = {
|
||||
type: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
|
||||
reason: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
|
||||
code: { classification: 'SystemMetaData', purpose: 'PerformanceAndHealth', isMeasurement: true };
|
||||
};
|
||||
type WindowErrorEvent = {
|
||||
type: WindowError;
|
||||
reason: string | undefined;
|
||||
code: number | undefined;
|
||||
};
|
||||
this.telemetryService.publicLog2<WindowErrorEvent, WindowErrorClassification>('windowerror', { type, reason: typeof details !== 'string' ? details?.reason : undefined });
|
||||
this.telemetryService.publicLog2<WindowErrorEvent, WindowErrorClassification>('windowerror', { type, reason: details?.reason, code: details?.exitCode });
|
||||
|
||||
// Unresponsive
|
||||
if (type === WindowError.UNRESPONSIVE) {
|
||||
@@ -613,10 +625,10 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
// Crashed
|
||||
else if (type === WindowError.CRASHED) {
|
||||
let message: string;
|
||||
if (typeof details === 'string' || !details) {
|
||||
if (!details) {
|
||||
message = localize('appCrashed', "The window has crashed");
|
||||
} else {
|
||||
message = localize('appCrashedDetails', "The window has crashed (reason: '{0}')", details.reason);
|
||||
message = localize('appCrashedDetails', "The window has crashed (reason: '{0}', code: '{1}')", details.reason, details.exitCode ?? '<unknown>');
|
||||
}
|
||||
|
||||
const result = await this.dialogMainService.showMessageBox({
|
||||
@@ -641,8 +653,8 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
}
|
||||
|
||||
private destroyWindow(): void {
|
||||
this._onDidDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event
|
||||
this._win.destroy(); // make sure to destroy the window as it has crashed
|
||||
this._onDidDestroy.fire(); // 'close' event will not be fired on destroy(), so signal crash via explicit event
|
||||
this._win.destroy(); // make sure to destroy the window as it has crashed
|
||||
}
|
||||
|
||||
private onDidDeleteUntitledWorkspace(workspace: IWorkspaceIdentifier): void {
|
||||
@@ -779,6 +791,7 @@ export class CodeWindow extends Disposable implements ICodeWindow {
|
||||
// Update window related properties
|
||||
configuration.fullscreen = this.isFullScreen;
|
||||
configuration.maximized = this._win.isMaximized();
|
||||
configuration.partsSplash = this.themeMainService.getWindowSplash();
|
||||
|
||||
// Update with latest perf marks
|
||||
mark('code/willOpenNewWindow');
|
||||
|
||||
@@ -8,7 +8,7 @@ import { IWorkspaceIdentifier, IResolvedWorkspace, isWorkspaceIdentifier, isSing
|
||||
import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources';
|
||||
import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
|
||||
export function findWindowOnFile(windows: ICodeWindow[], fileUri: URI, localWorkspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | null): ICodeWindow | undefined {
|
||||
export function findWindowOnFile(windows: ICodeWindow[], fileUri: URI, localWorkspaceResolver: (workspace: IWorkspaceIdentifier) => IResolvedWorkspace | undefined): ICodeWindow | undefined {
|
||||
|
||||
// First check for windows with workspaces that have a parent folder of the provided path opened
|
||||
for (const window of windows) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import { IBackupMainService } from 'vs/platform/backup/electron-main/backup';
|
||||
import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup';
|
||||
import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService';
|
||||
import { NativeParsedArgs } from 'vs/platform/environment/common/argv';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { IStateMainService } from 'vs/platform/state/electron-main/state';
|
||||
import { CodeWindow } from 'vs/platform/windows/electron-main/window';
|
||||
import { app, BrowserWindow, MessageBoxOptions, nativeTheme, WebContents } from 'electron';
|
||||
import { ILifecycleMainService, UnloadReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
@@ -139,13 +139,13 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
private readonly _onDidChangeWindowsCount = this._register(new Emitter<IWindowsCountChangedEvent>());
|
||||
readonly onDidChangeWindowsCount = this._onDidChangeWindowsCount.event;
|
||||
|
||||
private readonly windowsStateHandler = this._register(new WindowsStateHandler(this, this.stateService, this.lifecycleMainService, this.logService, this.configurationService));
|
||||
private readonly windowsStateHandler = this._register(new WindowsStateHandler(this, this.stateMainService, this.lifecycleMainService, this.logService, this.configurationService));
|
||||
|
||||
constructor(
|
||||
private readonly machineId: string,
|
||||
private readonly initialUserEnv: IProcessEnvironment,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@IStateMainService private readonly stateMainService: IStateMainService,
|
||||
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
|
||||
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
|
||||
@IBackupMainService private readonly backupMainService: IBackupMainService,
|
||||
@@ -409,7 +409,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
let windowToUseForFiles: ICodeWindow | undefined = undefined;
|
||||
if (fileToCheck?.fileUri && !openFilesInNewWindow) {
|
||||
if (openConfig.context === OpenContext.DESKTOP || openConfig.context === OpenContext.CLI || openConfig.context === OpenContext.DOCK) {
|
||||
windowToUseForFiles = findWindowOnFile(windows, fileToCheck.fileUri, workspace => workspace.configPath.scheme === Schemas.file ? this.workspacesManagementMainService.resolveLocalWorkspaceSync(workspace.configPath) : null);
|
||||
windowToUseForFiles = findWindowOnFile(windows, fileToCheck.fileUri, workspace => workspace.configPath.scheme === Schemas.file ? this.workspacesManagementMainService.resolveLocalWorkspaceSync(workspace.configPath) : undefined);
|
||||
}
|
||||
|
||||
if (!windowToUseForFiles) {
|
||||
@@ -856,6 +856,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
if (isFileToOpen(openable)) {
|
||||
options = { ...options, forceOpenWorkspaceAsFile: true };
|
||||
}
|
||||
|
||||
return this.doResolveFilePath(uri.fsPath, options);
|
||||
}
|
||||
|
||||
@@ -1168,8 +1169,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
appRoot: this.environmentMainService.appRoot,
|
||||
execPath: process.execPath,
|
||||
nodeCachedDataDir: this.environmentMainService.nodeCachedDataDir,
|
||||
partsSplashPath: join(this.environmentMainService.userDataPath, 'rapid_render.json'),
|
||||
codeCachePath: this.environmentMainService.codeCachePath,
|
||||
// If we know the backup folder upfront (for empty windows to restore), we can set it
|
||||
// directly here which helps for restoring UI state associated with that window.
|
||||
// For all other cases we first call into registerEmptyWindowBackupSync() to set it before
|
||||
|
||||
@@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IStateService } from 'vs/platform/state/node/state';
|
||||
import { IStateMainService } from 'vs/platform/state/electron-main/state';
|
||||
import { INativeWindowConfiguration, IWindowSettings } from 'vs/platform/windows/common/windows';
|
||||
import { defaultWindowState, ICodeWindow, IWindowsMainService, IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
@@ -53,7 +53,7 @@ export class WindowsStateHandler extends Disposable {
|
||||
private static readonly windowsStateStorageKey = 'windowsState';
|
||||
|
||||
get state() { return this._state; }
|
||||
private readonly _state = restoreWindowsState(this.stateService.getItem<ISerializedWindowsState>(WindowsStateHandler.windowsStateStorageKey));
|
||||
private readonly _state = restoreWindowsState(this.stateMainService.getItem<ISerializedWindowsState>(WindowsStateHandler.windowsStateStorageKey));
|
||||
|
||||
private lastClosedState: IWindowState | undefined = undefined;
|
||||
|
||||
@@ -61,7 +61,7 @@ export class WindowsStateHandler extends Disposable {
|
||||
|
||||
constructor(
|
||||
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
|
||||
@IStateService private readonly stateService: IStateService,
|
||||
@IStateMainService private readonly stateMainService: IStateMainService,
|
||||
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
@@ -177,7 +177,7 @@ export class WindowsStateHandler extends Disposable {
|
||||
|
||||
// Persist
|
||||
const state = getWindowsStateStoreData(currentWindowsState);
|
||||
this.stateService.setItem(WindowsStateHandler.windowsStateStorageKey, state);
|
||||
this.stateMainService.setItem(WindowsStateHandler.windowsStateStorageKey, state);
|
||||
|
||||
if (this.shuttingDown) {
|
||||
this.logService.trace('[WindowsStateHandler] onBeforeShutdown', state);
|
||||
@@ -232,9 +232,11 @@ export class WindowsStateHandler extends Disposable {
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
|
||||
// Window state is not from a previous session: only allow fullscreen if we inherit it or user wants fullscreen
|
||||
// or to address a Electron issue on macOS (https://github.com/microsoft/vscode/issues/125122)
|
||||
let allowFullscreen: boolean;
|
||||
if (state.hasDefaultState) {
|
||||
allowFullscreen = !!(windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0);
|
||||
const configAllowsFullScreen = !!(windowConfig?.newWindowDimensions && ['fullscreen', 'inherit', 'offset'].indexOf(windowConfig.newWindowDimensions) >= 0);
|
||||
allowFullscreen = configAllowsFullScreen || (isMacintosh && windowConfig?.nativeFullScreen !== false);
|
||||
}
|
||||
|
||||
// Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore
|
||||
@@ -337,14 +339,22 @@ export class WindowsStateHandler extends Disposable {
|
||||
// Compute x/y based on display bounds
|
||||
// Note: important to use Math.round() because Electron does not seem to be too happy about
|
||||
// display coordinates that are not absolute numbers.
|
||||
let state = defaultWindowState();
|
||||
let state: INewWindowState = defaultWindowState();
|
||||
state.x = Math.round(displayToUse.bounds.x + (displayToUse.bounds.width / 2) - (state.width! / 2));
|
||||
state.y = Math.round(displayToUse.bounds.y + (displayToUse.bounds.height / 2) - (state.height! / 2));
|
||||
|
||||
// Check for newWindowDimensions setting and adjust accordingly
|
||||
const windowConfig = this.configurationService.getValue<IWindowSettings | undefined>('window');
|
||||
let ensureNoOverlap = true;
|
||||
if (windowConfig?.newWindowDimensions) {
|
||||
|
||||
// TODO@electron macOS: if the current window is fullscreen and native fullscreen
|
||||
// is not disabled, always open a new window in fullscreen. This is a workaround
|
||||
// for regression https://github.com/microsoft/vscode/issues/125122
|
||||
if (isMacintosh && windowConfig?.nativeFullScreen !== false && lastActive?.isFullScreen) {
|
||||
state.mode = WindowMode.Fullscreen;
|
||||
}
|
||||
|
||||
// Adjust according to `newWindowDimensions` user setting
|
||||
else if (windowConfig?.newWindowDimensions) {
|
||||
if (windowConfig.newWindowDimensions === 'maximized') {
|
||||
state.mode = WindowMode.Maximized;
|
||||
ensureNoOverlap = false;
|
||||
@@ -367,7 +377,7 @@ export class WindowsStateHandler extends Disposable {
|
||||
state = this.ensureNoOverlap(state);
|
||||
}
|
||||
|
||||
(state as INewWindowState).hasDefaultState = true; // flag as default state
|
||||
state.hasDefaultState = true; // flag as default state
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ suite('WindowsFinder', () => {
|
||||
};
|
||||
|
||||
const testWorkspaceFolders = toWorkspaceFolders([{ path: join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: join(fixturesFolder, 'vscode_workspace_2_folder') }], testWorkspace.configPath, extUriBiasedIgnorePathCase);
|
||||
const localWorkspaceResolver = (workspace: any) => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: testWorkspaceFolders } : null; };
|
||||
const localWorkspaceResolver = (workspace: any) => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: testWorkspaceFolders } : undefined; };
|
||||
|
||||
function createTestCodeWindow(options: { lastFocusTime: number, openedFolderUri?: URI, openedWorkspace?: IWorkspaceIdentifier }): ICodeWindow {
|
||||
return new class implements ICodeWindow {
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as assert from 'assert';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { restoreWindowsState, getWindowsStateStoreData, IWindowsState, IWindowState } from 'vs/platform/windows/electron-main/windowsStateHandler';
|
||||
import { IWindowState as IWindowUIState, WindowMode } from 'vs/platform/windows/electron-main/windows';
|
||||
import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
suite('Windows State Storing', () => {
|
||||
|
||||
function getUIState(): IWindowUIState {
|
||||
return {
|
||||
x: 0,
|
||||
y: 10,
|
||||
width: 100,
|
||||
height: 200,
|
||||
mode: 0
|
||||
};
|
||||
}
|
||||
|
||||
function toWorkspace(uri: URI): IWorkspaceIdentifier {
|
||||
return {
|
||||
id: '1234',
|
||||
configPath: uri
|
||||
};
|
||||
}
|
||||
function assertEqualURI(u1: URI | undefined, u2: URI | undefined, message?: string): void {
|
||||
assert.strictEqual(u1 && u1.toString(), u2 && u2.toString(), message);
|
||||
}
|
||||
|
||||
function assertEqualWorkspace(w1: IWorkspaceIdentifier | undefined, w2: IWorkspaceIdentifier | undefined, message?: string): void {
|
||||
if (!w1 || !w2) {
|
||||
assert.strictEqual(w1, w2, message);
|
||||
return;
|
||||
}
|
||||
assert.strictEqual(w1.id, w2.id, message);
|
||||
assertEqualURI(w1.configPath, w2.configPath, message);
|
||||
}
|
||||
|
||||
function assertEqualWindowState(expected: IWindowState | undefined, actual: IWindowState | undefined, message?: string) {
|
||||
if (!expected || !actual) {
|
||||
assert.deepStrictEqual(expected, actual, message);
|
||||
return;
|
||||
}
|
||||
assert.strictEqual(expected.backupPath, actual.backupPath, message);
|
||||
assertEqualURI(expected.folderUri, actual.folderUri, message);
|
||||
assert.strictEqual(expected.remoteAuthority, actual.remoteAuthority, message);
|
||||
assertEqualWorkspace(expected.workspace, actual.workspace, message);
|
||||
assert.deepStrictEqual(expected.uiState, actual.uiState, message);
|
||||
}
|
||||
|
||||
function assertEqualWindowsState(expected: IWindowsState, actual: IWindowsState, message?: string) {
|
||||
assertEqualWindowState(expected.lastPluginDevelopmentHostWindow, actual.lastPluginDevelopmentHostWindow, message);
|
||||
assertEqualWindowState(expected.lastActiveWindow, actual.lastActiveWindow, message);
|
||||
assert.strictEqual(expected.openedWindows.length, actual.openedWindows.length, message);
|
||||
for (let i = 0; i < expected.openedWindows.length; i++) {
|
||||
assertEqualWindowState(expected.openedWindows[i], actual.openedWindows[i], message);
|
||||
}
|
||||
}
|
||||
|
||||
function assertRestoring(state: IWindowsState, message?: string) {
|
||||
const stored = getWindowsStateStoreData(state);
|
||||
const restored = restoreWindowsState(stored);
|
||||
assertEqualWindowsState(state, restored, message);
|
||||
}
|
||||
|
||||
const testBackupPath1 = join(tmpdir(), 'windowStateTest', 'backupFolder1');
|
||||
const testBackupPath2 = join(tmpdir(), 'windowStateTest', 'backupFolder2');
|
||||
|
||||
const testWSPath = URI.file(join(tmpdir(), 'windowStateTest', 'test.code-workspace'));
|
||||
const testFolderURI = URI.file(join(tmpdir(), 'windowStateTest', 'testFolder'));
|
||||
|
||||
const testRemoteFolderURI = URI.parse('foo://bar/c/d');
|
||||
|
||||
test('storing and restoring', () => {
|
||||
let windowState: IWindowsState;
|
||||
windowState = {
|
||||
openedWindows: []
|
||||
};
|
||||
assertRestoring(windowState, 'no windows');
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState() }]
|
||||
};
|
||||
assertRestoring(windowState, 'empty workspace');
|
||||
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState(), workspace: toWorkspace(testWSPath) }]
|
||||
};
|
||||
assertRestoring(windowState, 'workspace');
|
||||
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI }]
|
||||
};
|
||||
assertRestoring(windowState, 'folder');
|
||||
|
||||
windowState = {
|
||||
openedWindows: [{ backupPath: testBackupPath1, uiState: getUIState(), folderUri: testFolderURI }, { backupPath: testBackupPath1, uiState: getUIState(), folderUri: testRemoteFolderURI, remoteAuthority: 'bar' }]
|
||||
};
|
||||
assertRestoring(windowState, 'multiple windows');
|
||||
|
||||
windowState = {
|
||||
lastActiveWindow: { backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI },
|
||||
openedWindows: []
|
||||
};
|
||||
assertRestoring(windowState, 'lastActiveWindow');
|
||||
|
||||
windowState = {
|
||||
lastPluginDevelopmentHostWindow: { backupPath: testBackupPath2, uiState: getUIState(), folderUri: testFolderURI },
|
||||
openedWindows: []
|
||||
};
|
||||
assertRestoring(windowState, 'lastPluginDevelopmentHostWindow');
|
||||
});
|
||||
|
||||
test('open 1_32', () => {
|
||||
const v1_32_workspace = `{
|
||||
"openedWindows": [],
|
||||
"lastActiveWindow": {
|
||||
"workspaceIdentifier": {
|
||||
"id": "53b714b46ef1a2d4346568b4f591028c",
|
||||
"configURIPath": "file:///home/user/workspaces/testing/custom.code-workspace"
|
||||
},
|
||||
"backupPath": "/home/user/.config/code-oss-dev/Backups/53b714b46ef1a2d4346568b4f591028c",
|
||||
"uiState": {
|
||||
"mode": 0,
|
||||
"x": 0,
|
||||
"y": 27,
|
||||
"width": 2560,
|
||||
"height": 1364
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
let windowsState = restoreWindowsState(JSON.parse(v1_32_workspace));
|
||||
let expected: IWindowsState = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/code-oss-dev/Backups/53b714b46ef1a2d4346568b4f591028c',
|
||||
uiState: { mode: WindowMode.Maximized, x: 0, y: 27, width: 2560, height: 1364 },
|
||||
workspace: { id: '53b714b46ef1a2d4346568b4f591028c', configPath: URI.parse('file:///home/user/workspaces/testing/custom.code-workspace') }
|
||||
}
|
||||
};
|
||||
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_32_workspace');
|
||||
|
||||
const v1_32_folder = `{
|
||||
"openedWindows": [],
|
||||
"lastActiveWindow": {
|
||||
"folder": "file:///home/user/workspaces/testing/folding",
|
||||
"backupPath": "/home/user/.config/code-oss-dev/Backups/1daac1621c6c06f9e916ac8062e5a1b5",
|
||||
"uiState": {
|
||||
"mode": 1,
|
||||
"x": 625,
|
||||
"y": 263,
|
||||
"width": 1718,
|
||||
"height": 953
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
windowsState = restoreWindowsState(JSON.parse(v1_32_folder));
|
||||
expected = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/code-oss-dev/Backups/1daac1621c6c06f9e916ac8062e5a1b5',
|
||||
uiState: { mode: WindowMode.Normal, x: 625, y: 263, width: 1718, height: 953 },
|
||||
folderUri: URI.parse('file:///home/user/workspaces/testing/folding')
|
||||
}
|
||||
};
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_32_folder');
|
||||
|
||||
const v1_32_empty_window = ` {
|
||||
"openedWindows": [
|
||||
],
|
||||
"lastActiveWindow": {
|
||||
"backupPath": "/home/user/.config/code-oss-dev/Backups/1549539668998",
|
||||
"uiState": {
|
||||
"mode": 1,
|
||||
"x": 768,
|
||||
"y": 336,
|
||||
"width": 1024,
|
||||
"height": 768
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
windowsState = restoreWindowsState(JSON.parse(v1_32_empty_window));
|
||||
expected = {
|
||||
openedWindows: [],
|
||||
lastActiveWindow: {
|
||||
backupPath: '/home/user/.config/code-oss-dev/Backups/1549539668998',
|
||||
uiState: { mode: WindowMode.Normal, x: 768, y: 336, width: 1024, height: 768 }
|
||||
}
|
||||
};
|
||||
assertEqualWindowsState(expected, windowsState, 'v1_32_empty_window');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user