From 9222dcf9fa632643f73c007ea56ad9f26b01112a Mon Sep 17 00:00:00 2001 From: Simon Graband Date: Tue, 18 Jan 2022 12:01:26 +0100 Subject: [PATCH] Add --fastStartup flag When flag is enabled the plugin deployment will be delayed until a frontend is connected. This way the deployment of the plugins cannot slow down the fronend loading time. When the flag is not enabled the performance is not affected. Contributed on behalf of STMicroelectronics Signed-off-by: Simon Graband --- CHANGELOG.md | 2 + .../browser/frontend-application-module.ts | 7 +++- .../core/src/browser/frontend-application.ts | 8 ++-- .../core/src/common/connection-notifier.ts | 37 +++++++++++++++++++ packages/core/src/common/index.ts | 1 + .../src/node/backend-application-module.ts | 6 ++- packages/core/src/node/backend-application.ts | 4 ++ .../main/node/plugin-deployer-contribution.ts | 21 ++++++++--- 8 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 packages/core/src/common/connection-notifier.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a3129b2bc79d..a4ec6233eff31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ [1.22.0 Milestone](https://github.com/eclipse-theia/theia/milestone/30) +- [plugin-ext] add --fastStartup flag to wait for frontend start to deploy the plugins. [#](https://github.com/eclipse-theia/theia/pull/) - Contributed on behalf of STMicroelectronics + [Breaking Changes:](#breaking_changes_1.22.0) - [core] Removed `MarkdownRenderer` class [#10589](https://github.com/eclipse-theia/theia/pull/10589) - [core] `ContextKeyService` is now an interface. Extenders should extend `ContextKeyServiceDummyImpl` [#10546](https://github.com/eclipse-theia/theia/pull/10546) diff --git a/packages/core/src/browser/frontend-application-module.ts b/packages/core/src/browser/frontend-application-module.ts index fb2a485fb0d56..b42dcbd7a62af 100644 --- a/packages/core/src/browser/frontend-application-module.ts +++ b/packages/core/src/browser/frontend-application-module.ts @@ -30,7 +30,8 @@ import { MessageClient, InMemoryResources, messageServicePath, - InMemoryTextResourceResolver + InMemoryTextResourceResolver, + ClientConnectionNotifier } from '../common'; import { KeybindingRegistry, KeybindingContext, KeybindingContribution } from './keybinding'; import { FrontendApplication, FrontendApplicationContribution, DefaultFrontendApplicationContribution } from './frontend-application'; @@ -390,4 +391,8 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo child.bind(Coordinate).toConstantValue(position); return child.get(BreadcrumbPopupContainer); }); + bind(ClientConnectionNotifier).toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + return connection.createProxy('clientConnected'); + }).inSingletonScope(); }); diff --git a/packages/core/src/browser/frontend-application.ts b/packages/core/src/browser/frontend-application.ts index 9d2e4ad62f6cc..d0c0fee855d39 100644 --- a/packages/core/src/browser/frontend-application.ts +++ b/packages/core/src/browser/frontend-application.ts @@ -15,7 +15,7 @@ ********************************************************************************/ import { inject, injectable, named } from 'inversify'; -import { ContributionProvider, CommandRegistry, MenuModelRegistry, isOSX } from '../common'; +import { ContributionProvider, CommandRegistry, MenuModelRegistry, isOSX, ClientConnectionNotifier } from '../common'; import { MaybePromise } from '../common/types'; import { KeybindingRegistry } from './keybinding'; import { Widget } from './widgets'; @@ -129,7 +129,8 @@ export class FrontendApplication { @inject(ContributionProvider) @named(FrontendApplicationContribution) protected readonly contributions: ContributionProvider, @inject(ApplicationShell) protected readonly _shell: ApplicationShell, - @inject(FrontendApplicationStateService) protected readonly stateService: FrontendApplicationStateService + @inject(FrontendApplicationStateService) protected readonly stateService: FrontendApplicationStateService, + @inject(ClientConnectionNotifier) protected readonly connectionNotifier: ClientConnectionNotifier ) { } get shell(): ApplicationShell { @@ -258,9 +259,10 @@ export class FrontendApplication { const startupElem = this.getStartupIndicator(host); if (startupElem) { return new Promise(resolve => { - window.requestAnimationFrame(() => { + window.requestAnimationFrame(async () => { startupElem.classList.add('theia-hidden'); console.log(`Finished loading frontend application after ${(performance.now() / 1000).toFixed(3)} seconds`); + await this.connectionNotifier.clientConnected(); const preloadStyle = window.getComputedStyle(startupElem); const transitionDuration = parseCssTime(preloadStyle.transitionDuration, 0); window.setTimeout(() => { diff --git a/packages/core/src/common/connection-notifier.ts b/packages/core/src/common/connection-notifier.ts new file mode 100644 index 0000000000000..ce6af4e8eb701 --- /dev/null +++ b/packages/core/src/common/connection-notifier.ts @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (C) 2022 STMicroelectronics and others. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the Eclipse + * Public License v. 2.0 are satisfied: GNU General Public License, version 2 + * with the GNU Classpath Exception which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + ********************************************************************************/ +import { injectable } from 'inversify'; +import * as events from 'events'; + +export const ConnectionTimeout = Symbol('ConnectionTimeout'); + +@injectable() +export class ClientConnectionNotifier { + + static readonly CLIENT_CONNECTED = 'clientConnected'; + + readonly connectionEvent = new events.EventEmitter(); + + currentlyConnected = false; + + async clientConnected(): Promise { + if (!this.currentlyConnected) { + this.currentlyConnected = true; + this.connectionEvent.emit(ClientConnectionNotifier.CLIENT_CONNECTED); + } + } + +} diff --git a/packages/core/src/common/index.ts b/packages/core/src/common/index.ts index 3d041d03e68d9..2341edd7ca775 100644 --- a/packages/core/src/common/index.ts +++ b/packages/core/src/common/index.ts @@ -40,6 +40,7 @@ export * from './lsp-types'; export * from './contribution-filter'; export * from './nls'; export * from './numbers'; +export * from './connection-notifier'; import { environment } from '@theia/application-package/lib/environment'; export { environment }; diff --git a/packages/core/src/node/backend-application-module.ts b/packages/core/src/node/backend-application-module.ts index 555d6974d4c25..f12188490ccbb 100644 --- a/packages/core/src/node/backend-application-module.ts +++ b/packages/core/src/node/backend-application-module.ts @@ -18,7 +18,7 @@ import { ContainerModule, decorate, injectable } from 'inversify'; import { ApplicationPackage } from '@theia/application-package'; import { bindContributionProvider, MessageService, MessageClient, ConnectionHandler, JsonRpcConnectionHandler, - CommandService, commandServicePath, messageServicePath + CommandService, commandServicePath, messageServicePath, ClientConnectionNotifier } from '../common'; import { BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution, BackendApplicationServer } from './backend-application'; import { CliManager, CliContribution } from './cli'; @@ -109,4 +109,8 @@ export const backendApplicationModule = new ContainerModule(bind => { bind(EnvironmentUtils).toSelf().inSingletonScope(); bind(ProcessUtils).toSelf().inSingletonScope(); + bind(ConnectionHandler).toDynamicValue(({container}) => + new JsonRpcConnectionHandler('clientConnected', () => container.get(ClientConnectionNotifier)) + ).inSingletonScope(); + bind(ClientConnectionNotifier).toSelf().inSingletonScope(); }); diff --git a/packages/core/src/node/backend-application.ts b/packages/core/src/node/backend-application.ts index 8e50774a19181..83e8a0d8f638c 100644 --- a/packages/core/src/node/backend-application.ts +++ b/packages/core/src/node/backend-application.ts @@ -37,6 +37,7 @@ const TIMER_WARNING_THRESHOLD = 50; const DEFAULT_PORT = environment.electron.is() ? 0 : 3000; const DEFAULT_HOST = 'localhost'; const DEFAULT_SSL = false; +const DEFAULT_FAST_STARTUP = false; export const BackendApplicationServer = Symbol('BackendApplicationServer'); /** @@ -107,6 +108,7 @@ export class BackendApplicationCliContribution implements CliContribution { cert: string | undefined; certkey: string | undefined; projectPath: string; + fastStartup: boolean; configure(conf: yargs.Argv): void { conf.option('port', { alias: 'p', description: 'The port the backend server listens on.', type: 'number', default: DEFAULT_PORT }); @@ -115,6 +117,7 @@ export class BackendApplicationCliContribution implements CliContribution { conf.option('cert', { description: 'Path to SSL certificate.', type: 'string' }); conf.option('certkey', { description: 'Path to SSL certificate key.', type: 'string' }); conf.option(APP_PROJECT_PATH, { description: 'Sets the application project directory', default: this.appProjectPath() }); + conf.option('fastStartup', {description: 'delay plugin deployment to decrease startup time', type: 'boolean', default: DEFAULT_FAST_STARTUP}); } setArguments(args: yargs.Arguments): void { @@ -124,6 +127,7 @@ export class BackendApplicationCliContribution implements CliContribution { this.cert = args.cert as string; this.certkey = args.certkey as string; this.projectPath = args[APP_PROJECT_PATH] as string; + this.fastStartup = args['fastStartup'] as boolean; } protected appProjectPath(): string { diff --git a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts index 1a0adc3f22377..6a53ff74df2b7 100644 --- a/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts +++ b/packages/plugin-ext/src/main/node/plugin-deployer-contribution.ts @@ -14,21 +14,30 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { BackendApplicationContribution } from '@theia/core/lib/node'; +import { BackendApplicationCliContribution, BackendApplicationContribution } from '@theia/core/lib/node'; +import { ClientConnectionNotifier } from '@theia/core/lib/common'; import { injectable, inject } from '@theia/core/shared/inversify'; import { PluginDeployer } from '../../common/plugin-protocol'; -import { ILogger } from '@theia/core'; @injectable() export class PluginDeployerContribution implements BackendApplicationContribution { - @inject(ILogger) - protected readonly logger: ILogger; - @inject(PluginDeployer) protected pluginDeployer: PluginDeployer; + @inject(ClientConnectionNotifier) + protected readonly connectionNotifier: ClientConnectionNotifier; + + @inject(BackendApplicationCliContribution) + protected readonly cliParams: BackendApplicationCliContribution; + initialize(): void { - this.pluginDeployer.start(); + if (this.cliParams.fastStartup) { + this.connectionNotifier.connectionEvent.on(ClientConnectionNotifier.CLIENT_CONNECTED, async () => { + this.pluginDeployer.start(); + }); + } else { + this.pluginDeployer.start(); + } } }