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

fix UI calls from shared process

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