mirror of
https://github.com/microsoft/vscode.git
synced 2026-08-09 15:24:28 +01:00
window - try to improve focus handling (#206721)
This commit is contained in:
@@ -921,26 +921,6 @@ export function getActiveWindow(): CodeWindow {
|
||||
return (document.defaultView?.window ?? mainWindow) as CodeWindow;
|
||||
}
|
||||
|
||||
export function focusWindow(element: Node, options?: { force: boolean }): void {
|
||||
const window = getWindow(element);
|
||||
|
||||
// Force: always focus the element window
|
||||
if (options?.force) {
|
||||
window.focus();
|
||||
}
|
||||
|
||||
// Not forced: only focus the element window if another
|
||||
// window in the same workspace group has focus (when auxiliary
|
||||
// windows are opened).
|
||||
// This prevents stealing focus from another workspace window.
|
||||
else {
|
||||
const activeWindow = getActiveWindow();
|
||||
if (activeWindow.document.hasFocus() && activeWindow !== window) {
|
||||
window.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const globalStylesheets = new Map<HTMLStyleElement /* main stylesheet */, Set<HTMLStyleElement /* aux window clones that track the main stylesheet */>>();
|
||||
|
||||
export function isGlobalStylesheet(node: Node): boolean {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { IComposite, ICompositeControl } from 'vs/workbench/common/composite';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { IConstructorSignature, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { trackFocus, Dimension, IDomPosition, focusWindow } from 'vs/base/browser/dom';
|
||||
import { trackFocus, Dimension, IDomPosition } from 'vs/base/browser/dom';
|
||||
import { IStorageService } from 'vs/platform/storage/common/storage';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
@@ -149,13 +149,7 @@ export abstract class Composite extends Component implements IComposite {
|
||||
* Called when this composite should receive keyboard focus.
|
||||
*/
|
||||
focus(): void {
|
||||
const container = this.getContainer();
|
||||
if (container) {
|
||||
// Make sure to focus the window of the container
|
||||
// because it is possible that the composite is
|
||||
// opened in a auxiliary window that is not focused.
|
||||
focusWindow(container);
|
||||
}
|
||||
// Subclasses can implement
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { EventType, addDisposableListener, getClientArea, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, focusWindow, isActiveDocument, getWindow, getWindowId, getActiveElement } from 'vs/base/browser/dom';
|
||||
import { EventType, addDisposableListener, getClientArea, position, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, isActiveDocument, getWindow, getWindowId, getActiveElement } from 'vs/base/browser/dom';
|
||||
import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from 'vs/base/browser/browser';
|
||||
import { IWorkingCopyBackupService } from 'vs/workbench/services/workingCopy/common/workingCopyBackup';
|
||||
import { isWindows, isLinux, isMacintosh, isWeb, isIOS } from 'vs/base/common/platform';
|
||||
@@ -1124,9 +1124,6 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
||||
focusPart(part: SINGLE_WINDOW_PARTS): void;
|
||||
focusPart(part: Parts, targetWindow: Window = mainWindow): void {
|
||||
const container = this.getContainer(targetWindow, part) ?? this.mainContainer;
|
||||
if (container) {
|
||||
focusWindow(container);
|
||||
}
|
||||
|
||||
switch (part) {
|
||||
case Parts.EDITOR_PART:
|
||||
|
||||
@@ -11,7 +11,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput';
|
||||
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
|
||||
import { Emitter, Relay } from 'vs/base/common/event';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, focusWindow, getWindow, getActiveElement } from 'vs/base/browser/dom';
|
||||
import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, getWindow, getActiveElement } from 'vs/base/browser/dom';
|
||||
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
@@ -976,9 +976,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
focus(): void {
|
||||
|
||||
// Ensure window focus
|
||||
focusWindow(this.element);
|
||||
|
||||
// Pass focus to editor panes
|
||||
if (this.activeEditorPane) {
|
||||
this.activeEditorPane.focus();
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as nls from 'vs/nls';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { asCssVariable, foreground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
|
||||
import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl, Dimension, reset, asCssValueWithDefault, focusWindow } from 'vs/base/browser/dom';
|
||||
import { after, append, $, trackFocus, EventType, addDisposableListener, createCSSRule, asCSSUrl, Dimension, reset, asCssValueWithDefault } from 'vs/base/browser/dom';
|
||||
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Action, IAction, IActionRunner } from 'vs/base/common/actions';
|
||||
import { ActionsOrientation, IActionViewItem, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
@@ -630,8 +630,6 @@ export abstract class ViewPane extends Pane implements IView {
|
||||
}
|
||||
|
||||
focus(): void {
|
||||
focusWindow(this.element);
|
||||
|
||||
if (this.viewWelcomeController.enabled) {
|
||||
this.viewWelcomeController.focus();
|
||||
} else if (this.element) {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isSafari, setFullscreen } from 'vs/base/browser/browser';
|
||||
import { addDisposableListener, EventHelper, EventType, focusWindow, getWindowById, getWindows, getWindowsCount, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom';
|
||||
import { addDisposableListener, EventHelper, EventType, getActiveWindow, getWindow, getWindowById, getWindows, getWindowsCount, windowOpenNoOpener, windowOpenPopup, windowOpenWithSuccess } from 'vs/base/browser/dom';
|
||||
import { DomEmitter } from 'vs/base/browser/event';
|
||||
import { HidDeviceData, requestHidDevice, requestSerialPort, requestUsbDevice, SerialPortData, UsbDeviceData } from 'vs/base/browser/deviceAccess';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { matchesScheme, Schemas } from 'vs/base/common/network';
|
||||
import { isIOS, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isIOS, isMacintosh, isNative } from 'vs/base/common/platform';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -30,6 +30,7 @@ import { registerWindowDriver } from 'vs/workbench/services/driver/browser/drive
|
||||
import { CodeWindow, isAuxiliaryWindow, mainWindow } from 'vs/base/browser/window';
|
||||
import { createSingleCallFunction } from 'vs/base/common/functional';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
export abstract class BaseWindow extends Disposable {
|
||||
|
||||
@@ -39,11 +40,16 @@ export abstract class BaseWindow extends Disposable {
|
||||
constructor(
|
||||
targetWindow: CodeWindow,
|
||||
dom = { getWindowsCount, getWindows }, /* for testing */
|
||||
@IHostService protected readonly hostService: IHostService
|
||||
@IHostService protected readonly hostService: IHostService,
|
||||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super();
|
||||
|
||||
if (isNative) {
|
||||
this.enableNativeWindowFocus(targetWindow);
|
||||
}
|
||||
this.enableWindowFocusOnElementFocus(targetWindow);
|
||||
|
||||
this.enableMultiWindowAwareTimeout(targetWindow, dom);
|
||||
|
||||
this.registerFullScreenListeners(targetWindow.vscodeWindowId);
|
||||
@@ -51,13 +57,40 @@ export abstract class BaseWindow extends Disposable {
|
||||
|
||||
//#region focus handling in multi-window applications
|
||||
|
||||
protected enableNativeWindowFocus(targetWindow: CodeWindow): void {
|
||||
const originalWindowFocus = targetWindow.focus.bind(targetWindow);
|
||||
|
||||
const that = this;
|
||||
targetWindow.focus = function () {
|
||||
originalWindowFocus();
|
||||
|
||||
if (
|
||||
!that.environmentService.extensionTestsLocationURI && // never steal focus when running tests
|
||||
!targetWindow.document.hasFocus() // skip when already having focus
|
||||
) {
|
||||
// Enable `window.focus()` to work in Electron by
|
||||
// asking the main process to focus the window.
|
||||
// https://github.com/electron/electron/issues/25578
|
||||
that.hostService.focus(targetWindow);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected enableWindowFocusOnElementFocus(targetWindow: CodeWindow): void {
|
||||
const originalFocus = HTMLElement.prototype.focus;
|
||||
|
||||
targetWindow.HTMLElement.prototype.focus = function (this: HTMLElement, options?: FocusOptions | undefined): void {
|
||||
|
||||
// Ensure elements window is focused
|
||||
focusWindow(this);
|
||||
// If the active focused window is not the same as the
|
||||
// window of the element to focus, make sure to focus
|
||||
// that window first before focusing the element.
|
||||
const activeWindow = getActiveWindow();
|
||||
if (activeWindow.document.hasFocus()) {
|
||||
const elementWindow = getWindow(this);
|
||||
if (activeWindow !== elementWindow) {
|
||||
elementWindow.focus();
|
||||
}
|
||||
}
|
||||
|
||||
// Pass to original focus() method
|
||||
originalFocus.apply(this, [options]);
|
||||
@@ -178,12 +211,12 @@ export class BrowserWindow extends BaseWindow {
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@ILabelService private readonly labelService: ILabelService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService,
|
||||
@IBrowserWorkbenchEnvironmentService private readonly browserEnvironmentService: IBrowserWorkbenchEnvironmentService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(mainWindow, undefined, hostService);
|
||||
super(mainWindow, undefined, hostService, browserEnvironmentService);
|
||||
|
||||
this.registerListeners();
|
||||
this.create();
|
||||
@@ -280,8 +313,8 @@ export class BrowserWindow extends BaseWindow {
|
||||
this.openerService.setDefaultExternalOpener({
|
||||
openExternal: async (href: string) => {
|
||||
let isAllowedOpener = false;
|
||||
if (this.environmentService.options?.openerAllowedExternalUrlPrefixes) {
|
||||
for (const trustedPopupPrefix of this.environmentService.options.openerAllowedExternalUrlPrefixes) {
|
||||
if (this.browserEnvironmentService.options?.openerAllowedExternalUrlPrefixes) {
|
||||
for (const trustedPopupPrefix of this.browserEnvironmentService.options.openerAllowedExternalUrlPrefixes) {
|
||||
if (href.startsWith(trustedPopupPrefix)) {
|
||||
isAllowedOpener = true;
|
||||
break;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { equals } from 'vs/base/common/objects';
|
||||
import { EventType, EventHelper, addDisposableListener, ModifierKeyEmitter, getActiveElement, hasWindow, getWindow, getWindowById, getWindowId, getWindows } from 'vs/base/browser/dom';
|
||||
import { EventType, EventHelper, addDisposableListener, ModifierKeyEmitter, getActiveElement, hasWindow, getWindow, getWindowById, getWindows } from 'vs/base/browser/dom';
|
||||
import { Action, Separator, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { EditorResourceAccessor, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors, IResourceDiffEditorInput, IUntypedEditorInput, IEditorPane, isResourceEditorInput, IResourceMergeEditorInput } from 'vs/workbench/common/editor';
|
||||
@@ -107,7 +107,7 @@ export class NativeWindow extends BaseWindow {
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@ILifecycleService private readonly lifecycleService: ILifecycleService,
|
||||
@IIntegrityService private readonly integrityService: IIntegrityService,
|
||||
@INativeWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService,
|
||||
@INativeWorkbenchEnvironmentService private readonly nativeEnvironmentService: INativeWorkbenchEnvironmentService,
|
||||
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
|
||||
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
|
||||
@IOpenerService private readonly openerService: IOpenerService,
|
||||
@@ -131,7 +131,7 @@ export class NativeWindow extends BaseWindow {
|
||||
@IUtilityProcessWorkerWorkbenchService private readonly utilityProcessWorkerWorkbenchService: IUtilityProcessWorkerWorkbenchService,
|
||||
@IHostService hostService: IHostService
|
||||
) {
|
||||
super(mainWindow, undefined, hostService);
|
||||
super(mainWindow, undefined, hostService, nativeEnvironmentService);
|
||||
|
||||
this.mainPartEditorService = editorService.createScoped('main', this._store);
|
||||
|
||||
@@ -353,7 +353,7 @@ export class NativeWindow extends BaseWindow {
|
||||
this._register(Event.debounce(this.editorService.onDidVisibleEditorsChange, () => undefined, 0, undefined, undefined, undefined, this._store)(() => this.maybeCloseWindow()));
|
||||
|
||||
// Listen to editor closing (if we run with --wait)
|
||||
const filesToWait = this.environmentService.filesToWait;
|
||||
const filesToWait = this.nativeEnvironmentService.filesToWait;
|
||||
if (filesToWait) {
|
||||
this.trackClosedWaitFiles(filesToWait.waitMarkerFileUri, coalesce(filesToWait.paths.map(path => path.fileUri)));
|
||||
}
|
||||
@@ -412,7 +412,7 @@ export class NativeWindow extends BaseWindow {
|
||||
Event.map(Event.filter(this.nativeHostService.onDidMaximizeWindow, windowId => !!hasWindow(windowId)), windowId => ({ maximized: true, windowId })),
|
||||
Event.map(Event.filter(this.nativeHostService.onDidUnmaximizeWindow, windowId => !!hasWindow(windowId)), windowId => ({ maximized: false, windowId }))
|
||||
)(e => this.layoutService.updateWindowMaximizedState(getWindowById(e.windowId)!.window, e.maximized)));
|
||||
this.layoutService.updateWindowMaximizedState(mainWindow, this.environmentService.window.maximized ?? false);
|
||||
this.layoutService.updateWindowMaximizedState(mainWindow, this.nativeEnvironmentService.window.maximized ?? false);
|
||||
|
||||
// Detect panel position to determine minimum width
|
||||
this._register(this.layoutService.onDidChangePanelPosition(pos => this.onDidChangePanelPosition(positionFromString(pos))));
|
||||
@@ -582,7 +582,7 @@ export class NativeWindow extends BaseWindow {
|
||||
}
|
||||
|
||||
private maybeCloseWindow(): void {
|
||||
const closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty') || this.environmentService.args.wait;
|
||||
const closeWhenEmpty = this.configurationService.getValue('window.closeWhenEmpty') || this.nativeEnvironmentService.args.wait;
|
||||
if (!closeWhenEmpty) {
|
||||
return; // return early if configured to not close when empty
|
||||
}
|
||||
@@ -671,29 +671,6 @@ export class NativeWindow extends BaseWindow {
|
||||
if (this.environmentService.enableSmokeTestDriver) {
|
||||
this.setupDriver();
|
||||
}
|
||||
|
||||
// Patch methods that we need to work properly
|
||||
this.patchMethods();
|
||||
}
|
||||
|
||||
private patchMethods(): void {
|
||||
|
||||
// Enable `window.focus()` to work in Electron by
|
||||
// asking the main process to focus the window.
|
||||
// https://github.com/electron/electron/issues/25578
|
||||
const that = this;
|
||||
const originalWindowFocus = mainWindow.focus.bind(mainWindow);
|
||||
mainWindow.focus = function () {
|
||||
if (that.environmentService.extensionTestsLocationURI) {
|
||||
return; // no focus when we are running tests from CLI
|
||||
}
|
||||
|
||||
originalWindowFocus();
|
||||
|
||||
if (!mainWindow.document.hasFocus()) {
|
||||
that.nativeHostService.focusWindow({ targetWindowId: getWindowId(mainWindow) });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private async handleWarnings(): Promise<void> {
|
||||
@@ -731,11 +708,11 @@ export class NativeWindow extends BaseWindow {
|
||||
let installLocationUri: URI;
|
||||
if (isMacintosh) {
|
||||
// appRoot = /Applications/Visual Studio Code - Insiders.app/Contents/Resources/app
|
||||
installLocationUri = dirname(dirname(dirname(URI.file(this.environmentService.appRoot))));
|
||||
installLocationUri = dirname(dirname(dirname(URI.file(this.nativeEnvironmentService.appRoot))));
|
||||
} else {
|
||||
// appRoot = C:\Users\<name>\AppData\Local\Programs\Microsoft VS Code Insiders\resources\app
|
||||
// appRoot = /usr/share/code-insiders/resources/app
|
||||
installLocationUri = dirname(dirname(URI.file(this.environmentService.appRoot)));
|
||||
installLocationUri = dirname(dirname(URI.file(this.nativeEnvironmentService.appRoot)));
|
||||
}
|
||||
|
||||
for (const folder of this.contextService.getWorkspace().folders) {
|
||||
@@ -753,7 +730,7 @@ export class NativeWindow extends BaseWindow {
|
||||
|
||||
// macOS 10.13 and 10.14 warning
|
||||
if (isMacintosh) {
|
||||
const majorVersion = this.environmentService.os.release.split('.')[0];
|
||||
const majorVersion = this.nativeEnvironmentService.os.release.split('.')[0];
|
||||
const eolReleases = new Map<string, string>([
|
||||
['17', 'macOS High Sierra'],
|
||||
['18', 'macOS Mojave'],
|
||||
|
||||
@@ -22,6 +22,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
export const IAuxiliaryWindowService = createDecorator<IAuxiliaryWindowService>('auxiliaryWindowService');
|
||||
|
||||
@@ -84,9 +85,10 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow {
|
||||
readonly container: HTMLElement,
|
||||
stylesHaveLoaded: Barrier,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IHostService hostService: IHostService
|
||||
@IHostService hostService: IHostService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(window, undefined, hostService);
|
||||
super(window, undefined, hostService, environmentService);
|
||||
|
||||
this.whenStylesHaveLoaded = stylesHaveLoaded.wait().then(() => { });
|
||||
this.registerListeners();
|
||||
@@ -182,7 +184,8 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
@IHostService protected readonly hostService: IHostService
|
||||
@IHostService protected readonly hostService: IHostService,
|
||||
@IWorkbenchEnvironmentService protected readonly environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super();
|
||||
}
|
||||
@@ -237,7 +240,7 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
}
|
||||
|
||||
protected createAuxiliaryWindow(targetWindow: CodeWindow, container: HTMLElement, stylesLoaded: Barrier): AuxiliaryWindow {
|
||||
return new AuxiliaryWindow(targetWindow, container, stylesLoaded, this.configurationService, this.hostService);
|
||||
return new AuxiliaryWindow(targetWindow, container, stylesLoaded, this.configurationService, this.hostService, this.environmentService);
|
||||
}
|
||||
|
||||
private async openWindow(options?: IAuxiliaryWindowOpenOptions): Promise<Window | undefined> {
|
||||
@@ -293,24 +296,20 @@ export class BrowserAuxiliaryWindowService extends Disposable implements IAuxili
|
||||
}
|
||||
|
||||
protected createContainer(auxiliaryWindow: CodeWindow, disposables: DisposableStore, options?: IAuxiliaryWindowOpenOptions): { stylesLoaded: Barrier; container: HTMLElement } {
|
||||
this.patchMethods(auxiliaryWindow);
|
||||
auxiliaryWindow.document.createElement = function () {
|
||||
// Disallow `createElement` because it would create
|
||||
// HTML Elements in the "wrong" context and break
|
||||
// code that does "instanceof HTMLElement" etc.
|
||||
throw new Error('Not allowed to create elements in child window JavaScript context. Always use the main window so that "xyz instanceof HTMLElement" continues to work.');
|
||||
};
|
||||
|
||||
this.applyMeta(auxiliaryWindow);
|
||||
const { stylesLoaded } = this.applyCSS(auxiliaryWindow, disposables);
|
||||
const container = this.applyHTML(auxiliaryWindow, disposables);
|
||||
|
||||
return { stylesLoaded, container };
|
||||
}
|
||||
|
||||
protected patchMethods(auxiliaryWindow: CodeWindow): void {
|
||||
|
||||
// Disallow `createElement` because it would create
|
||||
// HTML Elements in the "wrong" context and break
|
||||
// code that does "instanceof HTMLElement" etc.
|
||||
auxiliaryWindow.document.createElement = function () {
|
||||
throw new Error('Not allowed to create elements in child window JavaScript context. Always use the main window so that "xyz instanceof HTMLElement" continues to work.');
|
||||
};
|
||||
}
|
||||
|
||||
private applyMeta(auxiliaryWindow: CodeWindow): void {
|
||||
for (const metaTag of ['meta[charset="utf-8"]', 'meta[http-equiv="Content-Security-Policy"]', 'meta[name="viewport"]', 'meta[name="theme-color"]']) {
|
||||
const metaElement = mainWindow.document.querySelector(metaTag);
|
||||
|
||||
+8
-28
@@ -17,11 +17,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
||||
import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { applyZoom } from 'vs/platform/window/electron-sandbox/window';
|
||||
import { getZoomLevel } from 'vs/base/browser/browser';
|
||||
import { getActiveWindow } from 'vs/base/browser/dom';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
||||
type NativeCodeWindow = CodeWindow & {
|
||||
readonly vscode: ISandboxGlobals;
|
||||
@@ -38,9 +38,10 @@ export class NativeAuxiliaryWindow extends AuxiliaryWindow {
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@INativeHostService private readonly nativeHostService: INativeHostService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IHostService hostService: IHostService
|
||||
@IHostService hostService: IHostService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(window, container, stylesHaveLoaded, configurationService, hostService);
|
||||
super(window, container, stylesHaveLoaded, configurationService, hostService, environmentService);
|
||||
}
|
||||
|
||||
protected override async confirmBeforeClose(e: BeforeUnloadEvent): Promise<void> {
|
||||
@@ -68,10 +69,10 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
|
||||
@IDialogService dialogService: IDialogService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IHostService hostService: IHostService
|
||||
@IHostService hostService: IHostService,
|
||||
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService
|
||||
) {
|
||||
super(layoutService, dialogService, configurationService, telemetryService, hostService);
|
||||
super(layoutService, dialogService, configurationService, telemetryService, hostService, environmentService);
|
||||
}
|
||||
|
||||
protected override async resolveWindowId(auxiliaryWindow: NativeCodeWindow): Promise<number> {
|
||||
@@ -97,29 +98,8 @@ export class NativeAuxiliaryWindowService extends BrowserAuxiliaryWindowService
|
||||
return super.createContainer(auxiliaryWindow, disposables);
|
||||
}
|
||||
|
||||
protected override patchMethods(auxiliaryWindow: NativeCodeWindow): void {
|
||||
super.patchMethods(auxiliaryWindow);
|
||||
|
||||
// Enable `window.focus()` to work in Electron by
|
||||
// asking the main process to focus the window.
|
||||
// https://github.com/electron/electron/issues/25578
|
||||
const that = this;
|
||||
const originalWindowFocus = auxiliaryWindow.focus.bind(auxiliaryWindow);
|
||||
auxiliaryWindow.focus = function () {
|
||||
if (that.environmentService.extensionTestsLocationURI) {
|
||||
return; // no focus when we are running tests from CLI
|
||||
}
|
||||
|
||||
originalWindowFocus();
|
||||
|
||||
if (!auxiliaryWindow.document.hasFocus()) {
|
||||
that.nativeHostService.focusWindow({ targetWindowId: auxiliaryWindow.vscodeWindowId });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected override createAuxiliaryWindow(targetWindow: CodeWindow, container: HTMLElement, stylesHaveLoaded: Barrier,): AuxiliaryWindow {
|
||||
return new NativeAuxiliaryWindow(targetWindow, container, stylesHaveLoaded, this.configurationService, this.nativeHostService, this.instantiationService, this.hostService);
|
||||
return new NativeAuxiliaryWindow(targetWindow, container, stylesHaveLoaded, this.configurationService, this.nativeHostService, this.instantiationService, this.hostService, this.environmentService);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler';
|
||||
import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils';
|
||||
import { BaseWindow } from 'vs/workbench/browser/window';
|
||||
import { TestHostService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
import { TestEnvironmentService, TestHostService } from 'vs/workbench/test/browser/workbenchTestServices';
|
||||
|
||||
suite('Window', () => {
|
||||
|
||||
@@ -19,9 +19,10 @@ suite('Window', () => {
|
||||
class TestWindow extends BaseWindow {
|
||||
|
||||
constructor(window: CodeWindow, dom: { getWindowsCount: () => number; getWindows: () => Iterable<IRegisteredCodeWindow> }) {
|
||||
super(window, dom, new TestHostService());
|
||||
super(window, dom, new TestHostService(), TestEnvironmentService);
|
||||
}
|
||||
|
||||
protected override enableNativeWindowFocus(): void { }
|
||||
protected override enableWindowFocusOnElementFocus(): void { }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user