提交 d4b310ea 编写于 作者: J Joao Moreno

fix UI calls from shared process

fix #54070
上级 ed9171b2
...@@ -94,8 +94,8 @@ export interface IChannelClient { ...@@ -94,8 +94,8 @@ export interface IChannelClient {
* channels (each from a separate client) to pick from. * channels (each from a separate client) to pick from.
*/ */
export interface IClientRouter { export interface IClientRouter {
routeCall(command: string, arg: any): string; routeCall(command: string, arg: any): TPromise<string>;
routeEvent(event: string, arg: any): string; routeEvent(event: string, arg: any): TPromise<string>;
} }
/** /**
...@@ -433,24 +433,20 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos ...@@ -433,24 +433,20 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos
getChannel<T extends IChannel>(channelName: string, router: IClientRouter): T { getChannel<T extends IChannel>(channelName: string, router: IClientRouter): T {
const call = (command: string, arg: any) => { const call = (command: string, arg: any) => {
const id = router.routeCall(command, arg); const channelPromise = router.routeCall(command, arg)
.then(id => this.getClient(id))
.then(client => client.getChannel(channelName));
if (!id) { return getDelayedChannel(channelPromise)
return TPromise.wrapError(new Error('Client id should be provided'));
}
return getDelayedChannel(this.getClient(id).then(client => client.getChannel(channelName)))
.call(command, arg); .call(command, arg);
}; };
const listen = (event: string, arg: any) => { const listen = (event: string, arg: any) => {
const id = router.routeEvent(event, arg); const channelPromise = router.routeEvent(event, arg)
.then(id => this.getClient(id))
if (!id) { .then(client => client.getChannel(channelName));
return TPromise.wrapError(new Error('Client id should be provided'));
}
return getDelayedChannel(this.getClient(id).then(client => client.getChannel(channelName))) return getDelayedChannel(channelPromise)
.listen(event, arg); .listen(event, arg);
}; };
...@@ -462,6 +458,10 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos ...@@ -462,6 +458,10 @@ export class IPCServer implements IChannelServer, IRoutingChannelClient, IDispos
} }
private getClient(clientId: string): TPromise<IChannelClient> { private getClient(clientId: string): TPromise<IChannelClient> {
if (!clientId) {
return TPromise.wrapError(new Error('Client id should be provided'));
}
const client = this.channelClients[clientId]; const client = this.channelClients[clientId];
if (client) { if (client) {
......
...@@ -67,7 +67,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I ...@@ -67,7 +67,8 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
process.once('exit', () => dispose(disposables)); process.once('exit', () => dispose(disposables));
const environmentService = new EnvironmentService(initData.args, process.execPath); const environmentService = new EnvironmentService(initData.args, process.execPath);
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { routeCall: () => 'main', routeEvent: () => 'main' })); const mainRoute = () => TPromise.as('main');
const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', { routeCall: mainRoute, routeEvent: mainRoute }));
const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath)); const logService = new FollowerLogService(logLevelClient, createSpdLogService('sharedprocess', initData.logLevel, environmentService.logsPath));
disposables.push(logService); disposables.push(logService);
...@@ -78,21 +79,13 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I ...@@ -78,21 +79,13 @@ function main(server: Server, initData: ISharedProcessInitData, configuration: I
services.set(IConfigurationService, new SyncDescriptor(ConfigurationService)); services.set(IConfigurationService, new SyncDescriptor(ConfigurationService));
services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(IRequestService, new SyncDescriptor(RequestService));
const windowsChannel = server.getChannel('windows', { routeCall: () => 'main', routeEvent: () => 'main' }); const windowsChannel = server.getChannel('windows', { routeCall: mainRoute, routeEvent: mainRoute });
const windowsService = new WindowsChannelClient(windowsChannel); const windowsService = new WindowsChannelClient(windowsChannel);
services.set(IWindowsService, windowsService); services.set(IWindowsService, windowsService);
const activeWindowManager = new ActiveWindowManager(windowsService); const activeWindowManager = new ActiveWindowManager(windowsService);
const dialogChannel = server.getChannel('dialog', { const route = () => activeWindowManager.getActiveClientId();
routeCall: () => { const dialogChannel = server.getChannel('dialog', { routeCall: route, routeEvent: route });
logService.info('Routing dialog call request to the client', activeWindowManager.activeClientId);
return activeWindowManager.activeClientId;
},
routeEvent: () => {
logService.info('Routing dialog listen request to the client', activeWindowManager.activeClientId);
return activeWindowManager.activeClientId;
}
});
services.set(IDialogService, new DialogChannelClient(dialogChannel)); services.set(IDialogService, new DialogChannelClient(dialogChannel));
const instantiationService = new InstantiationService(services); const instantiationService = new InstantiationService(services);
......
...@@ -426,7 +426,7 @@ export class CodeApplication { ...@@ -426,7 +426,7 @@ export class CodeApplication {
// Create a URL handler which forwards to the last active window // Create a URL handler which forwards to the last active window
const activeWindowManager = new ActiveWindowManager(windowsService); const activeWindowManager = new ActiveWindowManager(windowsService);
const route = () => activeWindowManager.activeClientId; const route = () => activeWindowManager.getActiveClientId();
const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { routeCall: route, routeEvent: route }); const urlHandlerChannel = this.electronIpcServer.getChannel('urlHandler', { routeCall: route, routeEvent: route });
const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel); const multiplexURLHandler = new URLHandlerChannelClient(urlHandlerChannel);
......
...@@ -27,12 +27,12 @@ class WindowRouter implements IClientRouter { ...@@ -27,12 +27,12 @@ class WindowRouter implements IClientRouter {
constructor(private windowId: number) { } constructor(private windowId: number) { }
routeCall(): string { routeCall(): TPromise<string> {
return `window:${this.windowId}`; return TPromise.as(`window:${this.windowId}`);
} }
routeEvent(): string { routeEvent(): TPromise<string> {
return `window:${this.windowId}`; return TPromise.as(`window:${this.windowId}`);
} }
} }
......
...@@ -162,6 +162,7 @@ export interface IWindowsService { ...@@ -162,6 +162,7 @@ export interface IWindowsService {
getWindowCount(): TPromise<number>; getWindowCount(): TPromise<number>;
log(severity: string, ...messages: string[]): TPromise<void>; log(severity: string, ...messages: string[]): TPromise<void>;
showItemInFolder(path: string): TPromise<void>; showItemInFolder(path: string): TPromise<void>;
getActiveWindowId(): TPromise<number | undefined>;
// This needs to be handled from browser process to prevent // This needs to be handled from browser process to prevent
// foreground ordering issues on Windows // foreground ordering issues on Windows
...@@ -357,19 +358,31 @@ export interface IRunActionInWindowRequest { ...@@ -357,19 +358,31 @@ export interface IRunActionInWindowRequest {
export class ActiveWindowManager implements IDisposable { export class ActiveWindowManager implements IDisposable {
private disposables: IDisposable[] = []; private disposables: IDisposable[] = [];
private _activeWindowId: number; private firstActiveWindowIdPromise: TPromise<any> | null;
private _activeWindowId: number | undefined;
constructor(@IWindowsService windowsService: IWindowsService) { constructor(@IWindowsService windowsService: IWindowsService) {
const onActiveWindowChange = latch(anyEvent(windowsService.onWindowOpen, windowsService.onWindowFocus)); const onActiveWindowChange = latch(anyEvent(windowsService.onWindowOpen, windowsService.onWindowFocus));
onActiveWindowChange(this.setActiveWindow, this, this.disposables); onActiveWindowChange(this.setActiveWindow, this, this.disposables);
this.firstActiveWindowIdPromise = windowsService.getActiveWindowId()
.then(id => (typeof this._activeWindowId === 'undefined') && this.setActiveWindow(id));
} }
private setActiveWindow(windowId: number) { private setActiveWindow(windowId: number) {
if (this.firstActiveWindowIdPromise) {
this.firstActiveWindowIdPromise = null;
}
this._activeWindowId = windowId; this._activeWindowId = windowId;
} }
get activeClientId(): string { getActiveClientId(): TPromise<string> {
return `window:${this._activeWindowId}`; if (this.firstActiveWindowIdPromise) {
return this.firstActiveWindowIdPromise;
}
return TPromise.as(`window:${this._activeWindowId}`);
} }
dispose() { dispose() {
......
...@@ -68,6 +68,7 @@ export interface IWindowsChannel extends IChannel { ...@@ -68,6 +68,7 @@ export interface IWindowsChannel extends IChannel {
call(command: 'toggleSharedProcess'): TPromise<void>; call(command: 'toggleSharedProcess'): TPromise<void>;
call(command: 'log', arg: [string, string[]]): TPromise<void>; call(command: 'log', arg: [string, string[]]): TPromise<void>;
call(command: 'showItemInFolder', arg: string): TPromise<void>; call(command: 'showItemInFolder', arg: string): TPromise<void>;
call(command: 'getActiveWindowId'): TPromise<number>;
call(command: 'openExternal', arg: string): TPromise<boolean>; call(command: 'openExternal', arg: string): TPromise<boolean>;
call(command: 'startCrashReporter', arg: CrashReporterStartOptions): TPromise<void>; call(command: 'startCrashReporter', arg: CrashReporterStartOptions): TPromise<void>;
call(command: 'openAccessibilityOptions'): TPromise<void>; call(command: 'openAccessibilityOptions'): TPromise<void>;
...@@ -166,6 +167,7 @@ export class WindowsChannel implements IWindowsChannel { ...@@ -166,6 +167,7 @@ export class WindowsChannel implements IWindowsChannel {
case 'quit': return this.service.quit(); case 'quit': return this.service.quit();
case 'log': return this.service.log(arg[0], arg[1]); case 'log': return this.service.log(arg[0], arg[1]);
case 'showItemInFolder': return this.service.showItemInFolder(arg); case 'showItemInFolder': return this.service.showItemInFolder(arg);
case 'getActiveWindowId': return this.service.getActiveWindowId();
case 'openExternal': return this.service.openExternal(arg); case 'openExternal': return this.service.openExternal(arg);
case 'startCrashReporter': return this.service.startCrashReporter(arg); case 'startCrashReporter': return this.service.startCrashReporter(arg);
case 'openAccessibilityOptions': return this.service.openAccessibilityOptions(); case 'openAccessibilityOptions': return this.service.openAccessibilityOptions();
...@@ -364,6 +366,10 @@ export class WindowsChannelClient implements IWindowsService { ...@@ -364,6 +366,10 @@ export class WindowsChannelClient implements IWindowsService {
return this.channel.call('showItemInFolder', path); return this.channel.call('showItemInFolder', path);
} }
getActiveWindowId(): TPromise<number | undefined> {
return this.channel.call('getActiveWindowId');
}
openExternal(url: string): TPromise<boolean> { openExternal(url: string): TPromise<boolean> {
return this.channel.call('openExternal', url); return this.channel.call('openExternal', url);
} }
......
...@@ -14,7 +14,7 @@ import product from 'vs/platform/node/product'; ...@@ -14,7 +14,7 @@ import product from 'vs/platform/node/product';
import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions } from 'vs/platform/windows/common/windows'; import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions } from 'vs/platform/windows/common/windows';
import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment';
import { shell, crashReporter, app, Menu, clipboard, BrowserWindow } from 'electron'; import { shell, crashReporter, app, Menu, clipboard, BrowserWindow } from 'electron';
import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent } from 'vs/base/common/event'; import { Event, fromNodeEventEmitter, mapEvent, filterEvent, anyEvent, latch } from 'vs/base/common/event';
import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url';
import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain';
import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows'; import { IWindowsMainService, ISharedProcess } from 'vs/platform/windows/electron-main/windows';
...@@ -32,6 +32,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable ...@@ -32,6 +32,8 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
private disposables: IDisposable[] = []; private disposables: IDisposable[] = [];
private _activeWindowId: number | undefined;
readonly onWindowOpen: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id)); readonly onWindowOpen: Event<number> = filterEvent(fromNodeEventEmitter(app, 'browser-window-created', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id));
readonly onWindowFocus: Event<number> = anyEvent( readonly onWindowFocus: Event<number> = anyEvent(
mapEvent(filterEvent(mapEvent(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), w => !!w), w => w.id), mapEvent(filterEvent(mapEvent(this.windowsMainService.onWindowsCountChanged, () => this.windowsMainService.getLastActiveWindow()), w => !!w), w => w.id),
...@@ -54,6 +56,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable ...@@ -54,6 +56,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
@ILogService private logService: ILogService @ILogService private logService: ILogService
) { ) {
urlService.registerHandler(this); urlService.registerHandler(this);
// remember last active window id
latch(anyEvent(this.onWindowOpen, this.onWindowFocus))
(id => this._activeWindowId = id, null, this.disposables);
} }
pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> { pickFileFolderAndOpen(options: INativeOpenDialogOptions): TPromise<void> {
...@@ -435,6 +441,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable ...@@ -435,6 +441,10 @@ export class WindowsService implements IWindowsService, IURLHandler, IDisposable
return TPromise.as(null); return TPromise.as(null);
} }
getActiveWindowId(): TPromise<number | undefined> {
return TPromise.as(this._activeWindowId);
}
openExternal(url: string): TPromise<boolean> { openExternal(url: string): TPromise<boolean> {
this.logService.trace('windowsService#openExternal'); this.logService.trace('windowsService#openExternal');
return TPromise.as(shell.openExternal(url)); return TPromise.as(shell.openExternal(url));
......
...@@ -13,6 +13,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform'; ...@@ -13,6 +13,7 @@ import { isLinux, isWindows } from 'vs/base/common/platform';
import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWindowService } from 'vs/platform/windows/common/windows';
import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { mnemonicButtonLabel } from 'vs/base/common/labels';
import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions } from 'vs/platform/dialogs/common/dialogs';
import { ILogService } from 'vs/platform/log/common/log';
interface IMassagedMessageBoxOptions { interface IMassagedMessageBoxOptions {
...@@ -34,10 +35,13 @@ export class DialogService implements IDialogService { ...@@ -34,10 +35,13 @@ export class DialogService implements IDialogService {
_serviceBrand: any; _serviceBrand: any;
constructor( constructor(
@IWindowService private windowService: IWindowService @IWindowService private windowService: IWindowService,
@ILogService private logService: ILogService
) { } ) { }
confirm(confirmation: IConfirmation): TPromise<IConfirmationResult> { confirm(confirmation: IConfirmation): TPromise<IConfirmationResult> {
this.logService.trace('DialogService#confirm', confirmation.message);
const { options, buttonIndexMap } = this.massageMessageBoxOptions(this.getConfirmOptions(confirmation)); const { options, buttonIndexMap } = this.massageMessageBoxOptions(this.getConfirmOptions(confirmation));
return this.windowService.showMessageBox(options).then(result => { return this.windowService.showMessageBox(options).then(result => {
...@@ -86,6 +90,8 @@ export class DialogService implements IDialogService { ...@@ -86,6 +90,8 @@ export class DialogService implements IDialogService {
} }
show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): TPromise<number> { show(severity: Severity, message: string, buttons: string[], dialogOptions?: IDialogOptions): TPromise<number> {
this.logService.trace('DialogService#show', message);
const { options, buttonIndexMap } = this.massageMessageBoxOptions({ const { options, buttonIndexMap } = this.massageMessageBoxOptions({
message, message,
buttons, buttons,
......
...@@ -1295,6 +1295,10 @@ export class TestWindowsService implements IWindowsService { ...@@ -1295,6 +1295,10 @@ export class TestWindowsService implements IWindowsService {
return TPromise.as(void 0); return TPromise.as(void 0);
} }
getActiveWindowId(): TPromise<number | undefined> {
return TPromise.as(undefined);
}
// This needs to be handled from browser process to prevent // This needs to be handled from browser process to prevent
// foreground ordering issues on Windows // foreground ordering issues on Windows
openExternal(url: string): TPromise<boolean> { openExternal(url: string): TPromise<boolean> {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册