mirror of
https://github.com/coder/code-server.git
synced 2026-05-09 13:57:26 +02:00
These conflicts will be resolved in the following commits. We do it this way so that PR review is possible.
162 lines
6.3 KiB
TypeScript
162 lines
6.3 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import { setFullscreen } from 'vs/base/browser/browser';
|
|
import { addDisposableListener, addDisposableThrottledListener, detectFullscreen, EventHelper, EventType, windowOpenNoOpener } from 'vs/base/browser/dom';
|
|
import { domEvent } from 'vs/base/browser/event';
|
|
import { timeout } from 'vs/base/common/async';
|
|
import { Event } from 'vs/base/common/event';
|
|
import { Disposable } from 'vs/base/common/lifecycle';
|
|
import { Schemas } from 'vs/base/common/network';
|
|
import { isIOS, isMacintosh } from 'vs/base/common/platform';
|
|
import Severity from 'vs/base/common/severity';
|
|
import { localize } from 'vs/nls';
|
|
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
|
|
import { ILabelService } from 'vs/platform/label/common/label';
|
|
import { ILogService } from 'vs/platform/log/common/log';
|
|
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
|
|
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
|
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
|
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
|
|
import { BrowserLifecycleService } from 'vs/workbench/services/lifecycle/browser/lifecycleService';
|
|
import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle';
|
|
|
|
export class BrowserWindow extends Disposable {
|
|
|
|
constructor(
|
|
@IOpenerService private readonly openerService: IOpenerService,
|
|
@ILifecycleService private readonly lifecycleService: BrowserLifecycleService,
|
|
@IDialogService private readonly dialogService: IDialogService,
|
|
@IHostService private readonly hostService: IHostService,
|
|
@ILabelService private readonly labelService: ILabelService,
|
|
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
|
|
@ILogService private readonly logService: ILogService,
|
|
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
|
) {
|
|
super();
|
|
|
|
this.registerListeners();
|
|
this.create();
|
|
}
|
|
|
|
private registerListeners(): void {
|
|
|
|
// Lifecycle
|
|
this._register(this.lifecycleService.onWillShutdown(() => this.onWillShutdown()));
|
|
|
|
// Layout
|
|
const viewport = isIOS && window.visualViewport ? window.visualViewport /** Visual viewport */ : window /** Layout viewport */;
|
|
this._register(addDisposableListener(viewport, EventType.RESIZE, () => this.onWindowResize()));
|
|
|
|
// Prevent the back/forward gestures in macOS
|
|
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.WHEEL, e => e.preventDefault(), { passive: false }));
|
|
|
|
// Prevent native context menus in web
|
|
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.CONTEXT_MENU, e => EventHelper.stop(e, true)));
|
|
|
|
// Prevent default navigation on drop
|
|
this._register(addDisposableListener(this.layoutService.getWorkbenchContainer(), EventType.DROP, e => EventHelper.stop(e, true)));
|
|
|
|
// Fullscreen (Browser)
|
|
[EventType.FULLSCREEN_CHANGE, EventType.WK_FULLSCREEN_CHANGE].forEach(event => {
|
|
this._register(addDisposableListener(document, event, () => setFullscreen(!!detectFullscreen())));
|
|
});
|
|
|
|
// Fullscreen (Native)
|
|
this._register(addDisposableThrottledListener(viewport, EventType.RESIZE, () => {
|
|
setFullscreen(!!detectFullscreen());
|
|
}, undefined, isMacintosh ? 2000 /* adjust for macOS animation */ : 800 /* can be throttled */));
|
|
}
|
|
|
|
private onWindowResize(): void {
|
|
this.logService.trace(`web.main#${isIOS && window.visualViewport ? 'visualViewport' : 'window'}Resize`);
|
|
|
|
this.layoutService.layout();
|
|
}
|
|
|
|
private onWillShutdown(): void {
|
|
|
|
// Try to detect some user interaction with the workbench
|
|
// when shutdown has happened to not show the dialog e.g.
|
|
// when navigation takes a longer time.
|
|
Event.toPromise(Event.any(
|
|
Event.once(domEvent(document.body, EventType.KEY_DOWN, true)),
|
|
Event.once(domEvent(document.body, EventType.MOUSE_DOWN, true))
|
|
)).then(async () => {
|
|
|
|
// Delay the dialog in case the user interacted
|
|
// with the page before it transitioned away
|
|
await timeout(3000);
|
|
|
|
// This should normally not happen, but if for some reason
|
|
// the workbench was shutdown while the page is still there,
|
|
// inform the user that only a reload can bring back a working
|
|
// state.
|
|
const res = await this.dialogService.show(
|
|
Severity.Error,
|
|
localize('shutdownError', "An unexpected error occurred that requires a reload of this page."),
|
|
[
|
|
localize('reload', "Reload")
|
|
],
|
|
{
|
|
detail: localize('shutdownErrorDetail', "The workbench was unexpectedly disposed while running.")
|
|
}
|
|
);
|
|
|
|
if (res.choice === 0) {
|
|
this.hostService.reload();
|
|
}
|
|
});
|
|
}
|
|
|
|
private create(): void {
|
|
|
|
// Driver
|
|
if (this.environmentService.options?.driver) {
|
|
(async () => this._register(await registerWindowDriver()))();
|
|
}
|
|
|
|
// Handle open calls
|
|
this.setupOpenHandlers();
|
|
|
|
// Label formatting
|
|
this.registerLabelFormatters();
|
|
}
|
|
|
|
private setupOpenHandlers(): void {
|
|
|
|
// We need to ignore the `beforeunload` event while
|
|
// we handle external links to open specifically for
|
|
// the case of application protocols that e.g. invoke
|
|
// vscode itself. We do not want to open these links
|
|
// in a new window because that would leave a blank
|
|
// window to the user, but using `window.location.href`
|
|
// will trigger the `beforeunload`.
|
|
this.openerService.setDefaultExternalOpener({
|
|
openExternal: async (href: string) => {
|
|
if (matchesScheme(href, Schemas.http) || matchesScheme(href, Schemas.https)) {
|
|
windowOpenNoOpener(href);
|
|
} else {
|
|
this.lifecycleService.withExpectedUnload(() => window.location.href = href);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
});
|
|
}
|
|
|
|
private registerLabelFormatters() {
|
|
this.labelService.registerFormatter({
|
|
scheme: Schemas.userData,
|
|
priority: true,
|
|
formatting: {
|
|
label: '${scheme}:${path}',
|
|
separator: '/',
|
|
}
|
|
});
|
|
}
|
|
}
|