web - register external opener to prevent unload on expected href changes
This commit is contained in:
parent
039f15a0f9
commit
6c9dab1259
|
@ -61,6 +61,7 @@ import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
|||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IUriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentity';
|
||||
import { UriIdentityService } from 'vs/workbench/services/uriIdentity/common/uriIdentityService';
|
||||
import { BrowserWindow } from 'vs/workbench/browser/window';
|
||||
|
||||
class BrowserMain extends Disposable {
|
||||
|
||||
|
@ -103,6 +104,12 @@ class BrowserMain extends Disposable {
|
|||
// Startup
|
||||
const instantiationService = workbench.startup();
|
||||
|
||||
// Window
|
||||
this._register(instantiationService.createInstance(BrowserWindow));
|
||||
|
||||
// Logging
|
||||
services.logService.trace('workbench configuration', JSON.stringify(this.configuration));
|
||||
|
||||
// Return API Facade
|
||||
return instantiationService.invokeFunction(accessor => {
|
||||
const commandService = accessor.get(ICommandService);
|
||||
|
|
56
src/vs/workbench/browser/window.ts
Normal file
56
src/vs/workbench/browser/window.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { windowOpenNoOpener } from 'vs/base/browser/dom';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
|
||||
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
|
||||
) {
|
||||
super();
|
||||
|
||||
this.create();
|
||||
}
|
||||
|
||||
private create(): void {
|
||||
|
||||
// Handle open calls
|
||||
this.setupOpenHandlers();
|
||||
}
|
||||
|
||||
private setupOpenHandlers(): void {
|
||||
|
||||
// Block window.open() calls
|
||||
window.open = function (): Window | null {
|
||||
throw new Error('Prevented call to window.open(). Use IOpenerService instead!');
|
||||
};
|
||||
|
||||
// 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.setExternalOpener({
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -125,7 +125,7 @@ export class NativeWindow extends Disposable {
|
|||
// React to editor input changes
|
||||
this._register(this.editorService.onDidActiveEditorChange(() => this.updateTouchbarMenu()));
|
||||
|
||||
// prevent opening a real URL inside the shell
|
||||
// prevent opening a real URL inside the window
|
||||
[EventType.DRAG_OVER, EventType.DROP].forEach(event => {
|
||||
window.document.body.addEventListener(event, (e: DragEvent) => {
|
||||
EventHelper.stop(e);
|
||||
|
|
|
@ -16,6 +16,7 @@ export class BrowserLifecycleService extends AbstractLifecycleService {
|
|||
declare readonly _serviceBrand: undefined;
|
||||
|
||||
private beforeUnloadDisposable: IDisposable | undefined = undefined;
|
||||
private expectedUnload = false;
|
||||
|
||||
constructor(
|
||||
@ILogService readonly logService: ILogService
|
||||
|
@ -32,6 +33,13 @@ export class BrowserLifecycleService extends AbstractLifecycleService {
|
|||
}
|
||||
|
||||
private onBeforeUnload(event: BeforeUnloadEvent): void {
|
||||
if (this.expectedUnload) {
|
||||
this.logService.info('[lifecycle] onBeforeUnload expected, ignoring once');
|
||||
|
||||
this.expectedUnload = false;
|
||||
return; // ignore expected unload only once
|
||||
}
|
||||
|
||||
this.logService.info('[lifecycle] onBeforeUnload triggered');
|
||||
|
||||
this.doShutdown(() => {
|
||||
|
@ -41,6 +49,15 @@ export class BrowserLifecycleService extends AbstractLifecycleService {
|
|||
});
|
||||
}
|
||||
|
||||
withExpectedUnload(callback: Function): void {
|
||||
this.expectedUnload = true;
|
||||
try {
|
||||
callback();
|
||||
} finally {
|
||||
this.expectedUnload = false;
|
||||
}
|
||||
}
|
||||
|
||||
shutdown(): void {
|
||||
this.logService.info('[lifecycle] shutdown triggered');
|
||||
|
||||
|
|
Loading…
Reference in a new issue