Skip to content

Commit 4b8ead0

Browse files
committed
call url handler even when all windows are closed
fixes microsoft#48532
1 parent 0b0b0f8 commit 4b8ead0

4 files changed

Lines changed: 41 additions & 36 deletions

File tree

src/vs/code/electron-main/app.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,7 +396,7 @@ export class CodeApplication {
396396
this.lifecycleService.ready();
397397

398398
// Propagate to clients
399-
this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
399+
const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); // TODO@Joao: unfold this
400400

401401
const args = this.environmentService.args;
402402

@@ -405,6 +405,25 @@ export class CodeApplication {
405405
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { route: () => activeWindowManager.activeClientId });
406406
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
407407

408+
// On Mac, Code can be running without any open windows, so we must create a window to handle urls,
409+
// if there is none
410+
if (platform.isMacintosh) {
411+
const environmentService = accessor.get(IEnvironmentService);
412+
413+
urlService.registerHandler({
414+
async handleURL(uri: URI): TPromise<boolean> {
415+
if (windowsMainService.getWindowCount() === 0) {
416+
const cli = { ...environmentService.args, goto: true };
417+
const [window] = windowsMainService.open({ context: OpenContext.API, cli, forceEmpty: true });
418+
419+
return window.ready().then(() => urlService.open(uri));
420+
}
421+
422+
return false;
423+
}
424+
});
425+
}
426+
408427
// Register the multiple URL handker
409428
urlService.registerHandler(multiplexURLHandler);
410429

src/vs/platform/url/electron-main/electronUrlListener.ts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
'use strict';
77

8-
import { mapEvent, fromNodeEventEmitter, filterEvent } from 'vs/base/common/event';
8+
import { mapEvent, fromNodeEventEmitter, filterEvent, once } from 'vs/base/common/event';
99
import { IURLService } from 'vs/platform/url/common/url';
1010
import product from 'vs/platform/node/product';
1111
import { app } from 'electron';
@@ -24,21 +24,21 @@ function uriFromRawUrl(url: string): URI | null {
2424

2525
export class ElectronURLListener {
2626

27-
private buffer: URI[] = [];
2827
private disposables: IDisposable[] = [];
2928

3029
constructor(
3130
initial: string | string[],
3231
@IURLService private urlService: IURLService,
33-
@IWindowsMainService private windowsService: IWindowsMainService
32+
@IWindowsMainService windowsService: IWindowsMainService
3433
) {
3534
const globalBuffer = ((<any>global).getOpenUrls() || []) as string[];
3635
const rawBuffer = [
3736
...(typeof initial === 'string' ? [initial] : initial),
3837
...globalBuffer
3938
];
4039

41-
this.buffer = rawBuffer.map(uriFromRawUrl).filter(uri => !!uri);
40+
const buffer = rawBuffer.map(uriFromRawUrl).filter(uri => !!uri);
41+
const flush = () => buffer.forEach(uri => urlService.open(uri));
4242

4343
app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']);
4444

@@ -51,34 +51,16 @@ export class ElectronURLListener {
5151
});
5252

5353
const onOpenUrl = filterEvent(mapEvent(onOpenElectronUrl, uriFromRawUrl), uri => !!uri);
54-
onOpenUrl(this.open, this, this.disposables);
54+
onOpenUrl(this.urlService.open, this.urlService, this.disposables);
5555

56-
this.windowsService.onWindowReady(this.flushBuffer, this, this.disposables);
57-
this.flushBuffer();
58-
}
59-
60-
private open(uri: URI): void {
61-
const shouldBuffer = this.windowsService.getWindows()
56+
const isWindowReady = windowsService.getWindows()
6257
.filter(w => w.readyState === ReadyState.READY)
63-
.length === 0;
58+
.length > 0;
6459

65-
if (shouldBuffer) {
66-
this.buffer.push(uri);
60+
if (isWindowReady) {
61+
flush();
6762
} else {
68-
this.urlService.open(uri).then(handled => {
69-
if (!handled) {
70-
this.buffer.push(uri);
71-
}
72-
});
73-
}
74-
}
75-
76-
private flushBuffer(): void {
77-
const buffer = this.buffer;
78-
this.buffer = [];
79-
80-
for (const uri of buffer) {
81-
this.open(uri);
63+
once(windowsService.onWindowReady)(flush);
8264
}
8365
}
8466

src/vs/platform/windows/common/windows.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import { TPromise } from 'vs/base/common/winjs.base';
99
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
10-
import { Event } from 'vs/base/common/event';
10+
import { Event, latch, anyEvent } from 'vs/base/common/event';
1111
import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry';
1212
import { IProcessEnvironment } from 'vs/base/common/platform';
1313
import { ParsedArgs } from 'vs/platform/environment/common/environment';
@@ -343,8 +343,8 @@ export class ActiveWindowManager implements IDisposable {
343343
private _activeWindowId: number;
344344

345345
constructor(@IWindowsService windowsService: IWindowsService) {
346-
windowsService.onWindowOpen(this.setActiveWindow, this, this.disposables);
347-
windowsService.onWindowFocus(this.setActiveWindow, this, this.disposables);
346+
const onActiveWindowChange = latch(anyEvent(windowsService.onWindowOpen, windowsService.onWindowFocus));
347+
onActiveWindowChange(this.setActiveWindow, this, this.disposables);
348348
}
349349

350350
private setActiveWindow(windowId: number) {

src/vs/platform/windows/electron-main/windowsService.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import product from 'vs/platform/node/product';
1414
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult } from 'vs/platform/windows/common/windows';
1515
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
1616
import { shell, crashReporter, app, Menu, clipboard } from 'electron';
17-
import { Event, fromNodeEventEmitter } from 'vs/base/common/event';
17+
import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent } from 'vs/base/common/event';
1818
import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
1919
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
2020
import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows';
@@ -32,9 +32,13 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
3232

3333
private disposables: IDisposable[] = [];
3434

35-
readonly onWindowOpen: Event<number> = fromNodeEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id);
36-
readonly onWindowFocus: Event<number> = fromNodeEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id);
37-
readonly onWindowBlur: Event<number> = fromNodeEventEmitter(app, 'browser-window-blur', (_, w: Electron.BrowserWindow) => w.id);
35+
readonly onWindowOpen: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
36+
readonly onWindowFocus: Event<number> = anyEvent(
37+
mapEvent(filterEvent(mapEvent(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), w => !!w), w => w.id),
38+
filterEvent(fromNodeEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id))
39+
);
40+
41+
readonly onWindowBlur: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-blur', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
3842

3943
constructor(
4044
private sharedProcess: ISharedProcess,

0 commit comments

Comments
 (0)