From 3a3213200a503926e75a816ba79a51cd0b02b379 Mon Sep 17 00:00:00 2001 From: Tao Zhou Date: Wed, 23 Oct 2019 09:45:00 +0200 Subject: [PATCH] Move plugin loading related methods to PluginLoader. Fixed all tests and added more tests to plugin loader. This also adds https://gerrit-review.googlesource.com/c/gerrit/+/243179. Change-Id: I97cb3779117d9b29b40e1107e39d6d1c953fcfdf (cherry picked from commit fd41e5566f416159da661eea2398e27f04e64dc7) --- .../gr-change-metadata-it_test.html | 3 +- .../gr-change-metadata_test.html | 2 +- .../gr-change-view/gr-change-view_test.html | 2 +- polygerrit-ui/app/elements/gr-app-element.js | 3 +- .../gr-admin-api/gr-admin-api_test.html | 2 +- .../gr-endpoint-decorator_test.html | 2 +- .../plugins/gr-plugin-host/gr-plugin-host.js | 84 +-- .../gr-plugin-host/gr-plugin-host_test.html | 176 +----- .../plugins/gr-repo-api/gr-repo-api_test.html | 2 +- .../gr-settings-api/gr-settings-api_test.html | 2 +- .../gr-styles-api/gr-styles-api_test.html | 4 +- .../gr-theme-api/gr-theme-api_test.html | 2 +- .../shared/gr-avatar/gr-avatar_test.html | 6 +- .../gr-js-api-interface/gr-api-utils.js | 8 + .../gr-change-actions-js-api_test.html | 6 +- .../shared/gr-js-api-interface/gr-gerrit.js | 171 +----- .../gr-js-api-interface/gr-gerrit_test.html | 150 ++---- .../gr-js-api-interface.html | 8 + .../gr-js-api-interface_test.html | 13 +- .../gr-plugin-action-context_test.html | 1 - .../gr-js-api-interface/gr-plugin-loader.js | 393 ++++++++++++++ .../gr-plugin-loader_test.html | 502 ++++++++++++++++++ .../gr-plugin-rest-api_test.html | 1 - .../gr-js-api-interface/gr-public-js-api.js | 8 +- polygerrit-ui/app/test/index.html | 1 + 25 files changed, 1033 insertions(+), 519 deletions(-) create mode 100644 polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader.js create mode 100644 polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-loader_test.html diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html index 86f8aaf3cf..e60909c315 100644 --- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html +++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata-it_test.html @@ -89,6 +89,7 @@ limitations under the License. teardown(() => { sandbox.restore(); + Gerrit._testOnly_resetPlugins(); }); suite('by default', () => { @@ -141,7 +142,7 @@ limitations under the License. new URL('test/plugin.html?' + Math.random(), window.location.href).toString()); sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); element = createElement(); }); diff --git a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html index fe09869068..caa38d94ee 100644 --- a/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html +++ b/polygerrit-ui/app/elements/change/gr-change-metadata/gr-change-metadata_test.html @@ -725,7 +725,7 @@ limitations under the License. }, '0.1', 'http://some/plugins/url.html'); - Gerrit._setPluginsCount(0); + Gerrit._loadPlugins([]); flush(() => { assert.strictEqual(hookEl.plugin, plugin); assert.strictEqual(hookEl.change, element.change); diff --git a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html index f9c77987c7..f7d1a1813e 100644 --- a/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html +++ b/polygerrit-ui/app/elements/change/gr-change-view/gr-change-view_test.html @@ -81,7 +81,7 @@ limitations under the License. }); element = fixture('basic'); sandbox.stub(element.$.actions, 'reload').returns(Promise.resolve()); - Gerrit._setPluginsCount(0); + Gerrit._loadPlugins([]); }); teardown(done => { diff --git a/polygerrit-ui/app/elements/gr-app-element.js b/polygerrit-ui/app/elements/gr-app-element.js index e61bbfe096..d5d944a8c0 100644 --- a/polygerrit-ui/app/elements/gr-app-element.js +++ b/polygerrit-ui/app/elements/gr-app-element.js @@ -426,7 +426,8 @@ }, _computePluginScreenName({plugin, screen}) { - return Gerrit._getPluginScreenName(plugin, screen); + if (!plugin || !screen) return ''; + return `${plugin}-screen-${screen}`; }, _logWelcome() { diff --git a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html index 9cdcf76632..2537a37d9e 100644 --- a/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-admin-api/gr-admin-api_test.html @@ -39,7 +39,7 @@ limitations under the License. let plugin; Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); - sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); + Gerrit._loadPlugins([]); adminApi = plugin.admin(); }); diff --git a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html index 994d666035..b0ad58565d 100644 --- a/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html @@ -67,7 +67,7 @@ limitations under the License. replacementHook = plugin.registerCustomComponent( 'second', 'other-module', {replace: true}); // Mimic all plugins loaded. - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); flush(done); }); diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js index bdc765263e..21da106f82 100644 --- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js +++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host.js @@ -27,33 +27,29 @@ }, }, - behaviors: [ - Gerrit.BaseUrlBehavior, - ], - _configChanged(config) { const plugins = config.plugin; - const htmlPlugins = (plugins.html_resource_paths || []) - .map(p => this._urlFor(p)) - .filter(p => !Gerrit._isPluginPreloaded(p)); + const htmlPlugins = (plugins.html_resource_paths || []); const jsPlugins = - this._handleMigrations(plugins.js_resource_paths || [], htmlPlugins) - .map(p => this._urlFor(p)) - .filter(p => !Gerrit._isPluginPreloaded(p)); + this._handleMigrations(plugins.js_resource_paths || [], htmlPlugins); + const shouldLoadTheme = config.default_theme && !Gerrit._isPluginPreloaded('preloaded:gerrit-theme'); - const defaultTheme = - shouldLoadTheme ? this._urlFor(config.default_theme) : null; + const themeToLoad = + shouldLoadTheme ? [config.default_theme] : []; + + // Theme should be loaded first if has one to have better UX const pluginsPending = - [].concat(jsPlugins, htmlPlugins, defaultTheme || []); - Gerrit._setPluginsPending(pluginsPending); - if (defaultTheme) { - // Make theme first to be first to load. - // Load sync to work around rare theme loading race condition. - this._importHtmlPlugins([defaultTheme], true); + themeToLoad.concat(jsPlugins, htmlPlugins); + + const pluginOpts = {}; + + if (shouldLoadTheme) { + // Theme needs to be loaded synchronous. + pluginOpts[config.default_theme] = {sync: true}; } - this._loadJsPlugins(jsPlugins); - this._importHtmlPlugins(htmlPlugins); + + Gerrit._loadPlugins(pluginsPending, pluginOpts); }, /** @@ -66,53 +62,5 @@ return !htmlPlugins.includes(counterpart); }); }, - - /** - * @suppress {checkTypes} - * States that it expects no more than 3 parameters, but that's not true. - * @todo (beckysiegel) check Polymer annotations and submit change. - * @param {Array} plugins - * @param {boolean=} opt_sync - */ - _importHtmlPlugins(plugins, opt_sync) { - const async = !opt_sync; - for (const url of plugins) { - // onload (second param) needs to be a function. When null or undefined - // were passed, plugins were not loaded correctly. - (this.importHref || Polymer.importHref)( - this._urlFor(url), () => {}, - Gerrit._pluginInstallError.bind(null, `${url} import error`), - async); - } - }, - - _loadJsPlugins(plugins) { - for (const url of plugins) { - this._createScriptTag(this._urlFor(url)); - } - }, - - _createScriptTag(url) { - const el = document.createElement('script'); - el.defer = true; - el.src = url; - el.onerror = Gerrit._pluginInstallError.bind(null, `${url} load error`); - return document.body.appendChild(el); - }, - - _urlFor(pathOrUrl) { - if (!pathOrUrl) { - return pathOrUrl; - } - if (pathOrUrl.startsWith('preloaded:') || - pathOrUrl.startsWith('http')) { - // Plugins are loaded from another domain or preloaded. - return pathOrUrl; - } - if (!pathOrUrl.startsWith('/')) { - pathOrUrl = '/' + pathOrUrl; - } - return window.location.origin + this.getBaseUrl() + pathOrUrl; - }, }); })(); diff --git a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html index e57718290e..3a8e4d8cf5 100644 --- a/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-plugin-host/gr-plugin-host_test.html @@ -38,195 +38,57 @@ limitations under the License. suite('gr-plugin-host tests', () => { let element; let sandbox; - let url; setup(() => { element = fixture('basic'); sandbox = sinon.sandbox.create(); sandbox.stub(document.body, 'appendChild'); sandbox.stub(element, 'importHref'); - url = window.location.origin; }); teardown(() => { sandbox.restore(); }); - test('counts plugins', () => { - sandbox.stub(Gerrit, '_setPluginsCount'); + test('load plugins should be called', () => { + sandbox.stub(Gerrit, '_loadPlugins'); element.config = { plugin: { html_resource_paths: ['plugins/foo/bar', 'plugins/baz'], js_resource_paths: ['plugins/42'], }, }; - assert.isTrue(Gerrit._setPluginsCount.calledWith(3)); + assert.isTrue(Gerrit._loadPlugins.calledOnce); + assert.isTrue(Gerrit._loadPlugins.calledWith([ + 'plugins/42', 'plugins/foo/bar', 'plugins/baz', + ], {})); }); - test('imports relative html plugins from config', () => { - sandbox.stub(Gerrit, '_pluginInstallError'); - element.config = { - plugin: {html_resource_paths: ['foo/bar', 'baz']}, - }; - assert.equal(element.importHref.firstCall.args[0], url + '/foo/bar'); - assert.isTrue(element.importHref.firstCall.args[3]); - - assert.equal(element.importHref.secondCall.args[0], url + '/baz'); - assert.isTrue(element.importHref.secondCall.args[3]); - - assert.equal(Gerrit._pluginInstallError.callCount, 0); - element.importHref.firstCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 1); - element.importHref.secondCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 2); - }); - - test('imports relative html plugins from config with a base url', () => { - sandbox.stub(Gerrit, '_pluginInstallError'); - sandbox.stub(element, 'getBaseUrl').returns('/the-base'); - element.config = { - plugin: {html_resource_paths: ['foo/bar', 'baz']}}; - assert.equal(element.importHref.firstCall.args[0], - url + '/the-base/foo/bar'); - assert.isTrue(element.importHref.firstCall.args[3]); - - assert.equal(element.importHref.secondCall.args[0], - url + '/the-base/baz'); - assert.isTrue(element.importHref.secondCall.args[3]); - assert.equal(Gerrit._pluginInstallError.callCount, 0); - element.importHref.firstCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 1); - element.importHref.secondCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 2); - }); - - test('importHref is not called with null callback functions', () => { - const plugins = ['path/to/plugin']; - element._importHtmlPlugins(plugins); - assert.isTrue(element.importHref.calledOnce); - assert.isFunction(element.importHref.lastCall.args[1]); - assert.isFunction(element.importHref.lastCall.args[2]); - }); - - test('imports absolute html plugins from config', () => { - sandbox.stub(Gerrit, '_pluginInstallError'); + test('theme plugins should be loaded if enabled', () => { + sandbox.stub(Gerrit, '_loadPlugins'); element.config = { + default_theme: 'gerrit-theme.html', plugin: { - html_resource_paths: [ - 'http://example.com/foo/bar', - 'https://example.com/baz', - ], + html_resource_paths: ['plugins/foo/bar', 'plugins/baz'], + js_resource_paths: ['plugins/42'], }, }; - assert.equal(element.importHref.firstCall.args[0], - 'http://example.com/foo/bar'); - assert.isTrue(element.importHref.firstCall.args[3]); - - assert.equal(element.importHref.secondCall.args[0], - 'https://example.com/baz'); - assert.isTrue(element.importHref.secondCall.args[3]); - assert.equal(Gerrit._pluginInstallError.callCount, 0); - element.importHref.firstCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 1); - element.importHref.secondCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 2); + assert.isTrue(Gerrit._loadPlugins.calledOnce); + assert.isTrue(Gerrit._loadPlugins.calledWith([ + 'gerrit-theme.html', 'plugins/42', 'plugins/foo/bar', 'plugins/baz', + ], {'gerrit-theme.html': {sync: true}})); }); - test('adds js plugins from config to the body', () => { - element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}}; - assert.isTrue(document.body.appendChild.calledTwice); - }); - - test('imports relative js plugins from config', () => { - sandbox.stub(element, '_createScriptTag'); - element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}}; - assert.isTrue(element._createScriptTag.calledWith(url + '/foo/bar')); - assert.isTrue(element._createScriptTag.calledWith(url + '/baz')); - }); - - test('imports relative html plugins from config with a base url', () => { - sandbox.stub(element, '_createScriptTag'); - sandbox.stub(element, 'getBaseUrl').returns('/the-base'); - element.config = {plugin: {js_resource_paths: ['foo/bar', 'baz']}}; - assert.isTrue(element._createScriptTag.calledWith( - url + '/the-base/foo/bar')); - assert.isTrue(element._createScriptTag.calledWith( - url + '/the-base/baz')); - }); - - test('imports absolute html plugins from config', () => { - sandbox.stub(element, '_createScriptTag'); - element.config = { - plugin: { - js_resource_paths: [ - 'http://example.com/foo/bar', - 'https://example.com/baz', - ], - }, - }; - assert.isTrue(element._createScriptTag.calledWith( - 'http://example.com/foo/bar')); - assert.isTrue(element._createScriptTag.calledWith( - 'https://example.com/baz')); - }); - - test('default theme is loaded with html plugins', () => { - sandbox.stub(Gerrit, '_pluginInstallError'); - element.config = { - default_theme: '/oof', - plugin: { - html_resource_paths: ['some'], - }, - }; - assert.equal(element.importHref.firstCall.args[0], url + '/oof'); - assert.isFalse(element.importHref.firstCall.args[3]); - - assert.equal(element.importHref.secondCall.args[0], url + '/some'); - assert.isTrue(element.importHref.secondCall.args[3]); - assert.equal(Gerrit._pluginInstallError.callCount, 0); - element.importHref.firstCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 1); - element.importHref.secondCall.args[2](); - assert.equal(Gerrit._pluginInstallError.callCount, 2); - }); - - test('default theme is loaded with html plugins', () => { - sandbox.stub(Gerrit, '_setPluginsPending'); - element.config = { - default_theme: '/oof', - plugin: {}, - }; - assert.isTrue(Gerrit._setPluginsPending.calledWith([url + '/oof'])); - }); - - test('skips default theme loading if preloaded', () => { + test('skip theme if preloaded', () => { sandbox.stub(Gerrit, '_isPluginPreloaded') .withArgs('preloaded:gerrit-theme').returns(true); - sandbox.stub(Gerrit, '_setPluginsPending'); + sandbox.stub(Gerrit, '_loadPlugins'); element.config = { default_theme: '/oof', plugin: {}, }; - assert.isFalse(element.importHref.calledWith(url + '/oof')); - }); - - test('skips preloaded plugins', () => { - sandbox.stub(Gerrit, '_isPluginPreloaded') - .withArgs(url + '/plugins/foo/bar').returns(true) - .withArgs(url + '/plugins/42').returns(true); - sandbox.stub(Gerrit, '_setPluginsCount'); - sandbox.stub(Gerrit, '_setPluginsPending'); - sandbox.stub(element, '_createScriptTag'); - element.config = { - plugin: { - html_resource_paths: ['plugins/foo/bar', 'plugins/baz'], - js_resource_paths: ['plugins/42'], - }, - }; - assert.isTrue( - Gerrit._setPluginsPending.calledWith([url + '/plugins/baz'])); - assert.equal(element._createScriptTag.callCount, 0); - assert.isTrue(element.importHref.calledWith(url + '/plugins/baz')); + assert.isTrue(Gerrit._loadPlugins.calledOnce); + assert.isTrue(Gerrit._loadPlugins.calledWith([], {})); }); }); diff --git a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html index 7c7564bda4..0b32f8a701 100644 --- a/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-repo-api/gr-repo-api_test.html @@ -46,7 +46,7 @@ limitations under the License. let plugin; Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); - sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); + Gerrit._loadPlugins([]); repoApi = plugin.project(); }); diff --git a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html index d34ca94231..cbc2de622f 100644 --- a/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-settings-api/gr-settings-api_test.html @@ -48,7 +48,7 @@ limitations under the License. let plugin; Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); - sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); + Gerrit._loadPlugins([]); settingsApi = plugin.settings(); }); diff --git a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html index d67a3092bd..46bda6db9d 100644 --- a/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-styles-api/gr-styles-api_test.html @@ -46,7 +46,7 @@ limitations under the License. let plugin; Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); - sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); + Gerrit._loadPlugins([]); stylesApi = plugin.styles(); }); @@ -76,7 +76,7 @@ limitations under the License. let plugin; Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); - sandbox.stub(Gerrit, '_arePluginsLoaded').returns(true); + Gerrit._loadPlugins([]); stylesApi = plugin.styles(); displayInlineStyle = stylesApi.css('display: inline'); displayNoneStyle = stylesApi.css('display: none'); diff --git a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html index 82eb0f85d7..6332b9196e 100644 --- a/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html +++ b/polygerrit-ui/app/elements/plugins/gr-theme-api/gr-theme-api_test.html @@ -67,7 +67,7 @@ limitations under the License. stub('gr-custom-plugin-header', { ready() { customHeader = this; }, }); - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); }); test('sets logo and title', done => { diff --git a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html index 1c2afaab77..3456c1398b 100644 --- a/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html +++ b/polygerrit-ui/app/elements/shared/gr-avatar/gr-avatar_test.html @@ -117,7 +117,7 @@ limitations under the License. assert.strictEqual(element.style.backgroundImage, ''); // Emulate plugins loaded. - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); Promise.all([ element.$.restAPI.getConfig(), @@ -155,7 +155,7 @@ limitations under the License. assert.isFalse(element.hasAttribute('hidden')); // Emulate plugins loaded. - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); return Promise.all([ element.$.restAPI.getConfig(), @@ -197,7 +197,7 @@ limitations under the License. _account_id: 123, }; // Emulate plugins loaded. - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); return Promise.all([ element.$.restAPI.getConfig(), diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js index 2c5f39c59f..4123f70111 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-api-utils.js @@ -19,6 +19,7 @@ 'use strict'; const PRELOADED_PROTOCOL = 'preloaded:'; + const PLUGIN_LOADING_TIMEOUT_MS = 10000; let _restAPI; function getRestAPI() { @@ -28,6 +29,10 @@ return _restAPI; } + function getBaseUrl() { + return Gerrit.BaseUrlBehavior.getBaseUrl(); + } + /** * Retrieves the name of the plugin base on the url. * @param {string|URL} url @@ -96,6 +101,9 @@ getPluginNameFromUrl, send, getRestAPI, + getBaseUrl, + PRELOADED_PROTOCOL, + PLUGIN_LOADING_TIMEOUT_MS, // TEST only methods testOnly_resetInternalState, diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html index 0131912acb..7332877748 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-change-actions-js-api_test.html @@ -58,13 +58,14 @@ breaking changes to gr-change-actions won’t be noticed. Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); // Mimic all plugins loaded. - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); changeActions = plugin.changeActions(); element = fixture('basic'); }); teardown(() => { changeActions = null; + Gerrit._testOnly_resetPlugins(); }); test('does not throw', ()=> { @@ -85,11 +86,12 @@ breaking changes to gr-change-actions won’t be noticed. 'http://test.com/plugins/testplugin/static/test.js'); changeActions = plugin.changeActions(); // Mimic all plugins loaded. - Gerrit._setPluginsPending([]); + Gerrit._loadPlugins([]); }); teardown(() => { changeActions = null; + Gerrit._testOnly_resetPlugins(); }); test('property existence', () => { diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js index a5677000ad..03eb2e8d8c 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit.js @@ -23,42 +23,12 @@ (function(window) { 'use strict'; - /** - * Hash of loaded and installed plugins, name to Plugin object. - */ - const _plugins = {}; - - /** - * Array of plugin URLs to be loaded, name to url. - */ - let _pluginsPending = {}; - - let _pluginsInstalled = []; - - let _pluginsPendingCount = -1; - - const UNKNOWN_PLUGIN = 'unknown'; - const PRELOADED_PROTOCOL = 'preloaded:'; - - const PLUGIN_LOADING_TIMEOUT_MS = 10000; - - let _reporting; - const getReporting = () => { - if (!_reporting) { - _reporting = document.createElement('gr-reporting'); - } - return _reporting; - }; - // Import utils methods const { - getPluginNameFromUrl, send, getRestAPI, } = window._apiUtils; - const API_VERSION = '0.1'; - /** * Trigger the preinstalls for bundled plugins. * This needs to happen before Gerrit as plugin bundle overrides the Gerrit. @@ -72,9 +42,7 @@ window.Gerrit = window.Gerrit || {}; const Gerrit = window.Gerrit; - - let _resolveAllPluginsLoaded = null; - let _allPluginsPromise = null; + Gerrit._pluginLoader = new PluginLoader(); Gerrit._endpoints = new GrPluginEndpoints(); @@ -85,20 +53,13 @@ const { testOnly_resetInternalState, } = window._apiUtils; - Gerrit._testOnly_installPreloadedPlugins = installPreloadedPlugins; + Gerrit._testOnly_installPreloadedPlugins = (...args) => Gerrit._pluginLoader + .installPreloadedPlugins(...args); Gerrit._testOnly_flushPreinstalls = flushPreinstalls; Gerrit._testOnly_resetPlugins = () => { - _allPluginsPromise = null; - _pluginsInstalled = []; - _pluginsPending = {}; - _pluginsPendingCount = -1; - _reporting = null; - _resolveAllPluginsLoaded = null; testOnly_resetInternalState(); Gerrit._endpoints = new GrPluginEndpoints(); - for (const k of Object.keys(_plugins)) { - delete _plugins[k]; - } + Gerrit._pluginLoader = new PluginLoader(); }; } @@ -122,36 +83,7 @@ }; Gerrit.install = function(callback, opt_version, opt_src) { - // HTML import polyfill adds __importElement pointing to the import tag. - const script = document.currentScript && - (document.currentScript.__importElement || document.currentScript); - - let src = opt_src || (script && script.src); - if (!src || src.startsWith('data:')) { - src = script && script.baseURI; - } - const name = getPluginNameFromUrl(src); - - if (opt_version && opt_version !== API_VERSION) { - Gerrit._pluginInstallError(`Plugin ${name} install error: only version ` + - API_VERSION + ' is supported in PolyGerrit. ' + opt_version + - ' was given.'); - return; - } - - const existingPlugin = _plugins[name]; - const plugin = existingPlugin || new Plugin(src); - try { - callback(plugin); - if (name) { - _plugins[name] = plugin; - } - if (!existingPlugin) { - Gerrit._pluginInstalled(src); - } - } catch (e) { - Gerrit._pluginInstallError(`${e.name}: ${e.message}`); - } + Gerrit._pluginLoader.install(callback, opt_version, opt_src); }; Gerrit.getLoggedIn = function() { @@ -195,96 +127,33 @@ }; Gerrit.awaitPluginsLoaded = function() { - if (!_allPluginsPromise) { - if (Gerrit._arePluginsLoaded()) { - _allPluginsPromise = Promise.resolve(); - } else { - let timeoutId; - _allPluginsPromise = - Promise.race([ - new Promise(resolve => _resolveAllPluginsLoaded = resolve), - new Promise(resolve => timeoutId = setTimeout( - Gerrit._pluginLoadingTimeout, PLUGIN_LOADING_TIMEOUT_MS)), - ]).then(() => clearTimeout(timeoutId)); - } - } - return _allPluginsPromise; + return Gerrit._pluginLoader.awaitPluginsLoaded(); }; - Gerrit._pluginLoadingTimeout = function() { - console.error(`Failed to load plugins: ${Object.keys(_pluginsPending)}`); - Gerrit._setPluginsPending([]); - }; + // TODO(taoalpha): consider removing these proxy methods + // and using _pluginLoader directly - Gerrit._setPluginsPending = function(plugins) { - _pluginsPending = plugins.reduce((o, url) => { - // TODO(viktard): Remove guard (@see Issue 8962) - o[getPluginNameFromUrl(url) || UNKNOWN_PLUGIN] = url; - return o; - }, {}); - Gerrit._setPluginsCount(Object.keys(_pluginsPending).length); - }; - - Gerrit._setPluginsCount = function(count) { - _pluginsPendingCount = count; - if (Gerrit._arePluginsLoaded()) { - getReporting().pluginsLoaded(_pluginsInstalled); - if (_resolveAllPluginsLoaded) { - _resolveAllPluginsLoaded(); - } - } - }; - - Gerrit._pluginInstallError = function(message) { - document.dispatchEvent(new CustomEvent('show-alert', { - detail: { - message: `Plugin install error: ${message}`, - }, - })); - console.info(`Plugin install error: ${message}`); - Gerrit._setPluginsCount(_pluginsPendingCount - 1); - }; - - Gerrit._pluginInstalled = function(url) { - const name = getPluginNameFromUrl(url) || UNKNOWN_PLUGIN; - if (!_pluginsPending[name]) { - console.warn(`Unexpected plugin ${name} installed from ${url}.`); - } else { - delete _pluginsPending[name]; - _pluginsInstalled.push(name); - Gerrit._setPluginsCount(_pluginsPendingCount - 1); - getReporting().pluginLoaded(name); - console.log(`Plugin ${name} installed.`); - } + Gerrit._loadPlugins = function(plugins, opt_option) { + Gerrit._pluginLoader.loadPlugins(plugins, opt_option); }; Gerrit._arePluginsLoaded = function() { - return _pluginsPendingCount === 0; - }; - - Gerrit._getPluginScreenName = function(pluginName, screenName) { - return `${pluginName}-screen-${screenName}`; + return Gerrit._pluginLoader.arePluginsLoaded; }; Gerrit._isPluginPreloaded = function(url) { - const name = getPluginNameFromUrl(url); - if (name && Gerrit._preloadedPlugins) { - return name in Gerrit._preloadedPlugins; - } else { - return false; - } + return Gerrit._pluginLoader.isPluginPreloaded(url); }; - function installPreloadedPlugins() { - if (!Gerrit._preloadedPlugins) { return; } - for (const name in Gerrit._preloadedPlugins) { - if (!Gerrit._preloadedPlugins.hasOwnProperty(name)) { continue; } - const callback = Gerrit._preloadedPlugins[name]; - Gerrit.install(callback, API_VERSION, PRELOADED_PROTOCOL + name); - } - } + Gerrit._isPluginEnabled = function(pathOrUrl) { + return Gerrit._pluginLoader.isPluginEnabled(pathOrUrl); + }; + + Gerrit._isPluginLoaded = function(pathOrUrl) { + return Gerrit._pluginLoader.isPluginLoaded(pathOrUrl); + }; // Preloaded plugins should be installed after Gerrit.install() is set, // since plugin preloader substitutes Gerrit.install() temporarily. - installPreloadedPlugins(); + Gerrit._pluginLoader.installPreloadedPlugins(); })(window); \ No newline at end of file diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html index 9a05454d38..e81b8aad01 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-gerrit_test.html @@ -37,11 +37,11 @@ limitations under the License. diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html index 7fa2250025..dc81545273 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface.html @@ -31,6 +31,13 @@ limitations under the License. + @@ -41,5 +48,6 @@ limitations under the License. + diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html index 330310fd8e..ae12940f51 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-js-api-interface_test.html @@ -35,6 +35,7 @@ limitations under the License. + + + + + + + + + + + + + + diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html index 05f84c0ace..bcbd961c00 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-plugin-rest-api_test.html @@ -50,7 +50,6 @@ limitations under the License. a[k] = (...args) => restApiStub[k](...args); return a; }, {})); - Gerrit._setPluginsCount(1); Gerrit.install(p => { plugin = p; }, '0.1', 'http://test.com/plugins/testplugin/static/test.js'); instance = new GrPluginRestApi(); diff --git a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js index d44aceaee5..d76c983812 100644 --- a/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js +++ b/polygerrit-ui/app/elements/shared/gr-js-api-interface/gr-public-js-api.js @@ -267,10 +267,14 @@ return; } return this.registerCustomComponent( - Gerrit._getPluginScreenName(this.getPluginName(), screenName), + this._getScreenName(screenName), opt_moduleName); }; + Plugin.prototype._getScreenName = function(screenName) { + return `${this.getPluginName()}-screen-${screenName}`; + }; + const deprecatedAPI = { _loadedGwt: ()=> {}, @@ -321,7 +325,7 @@ 'Please use strings for patterns.'); return; } - this.hook(Gerrit._getPluginScreenName(this.getPluginName(), pattern)) + this.hook(this._getScreenName(pattern)) .onAttached(el => { el.style.display = 'none'; callback({ diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html index a1c3ae60f6..cb1fcef96c 100644 --- a/polygerrit-ui/app/test/index.html +++ b/polygerrit-ui/app/test/index.html @@ -188,6 +188,7 @@ limitations under the License. 'shared/gr-js-api-interface/gr-js-api-interface_test.html', 'shared/gr-js-api-interface/gr-gerrit_test.html', 'shared/gr-js-api-interface/gr-plugin-action-context_test.html', + 'shared/gr-js-api-interface/gr-plugin-loader_test.html', 'shared/gr-js-api-interface/gr-plugin-endpoints_test.html', 'shared/gr-js-api-interface/gr-plugin-rest-api_test.html', 'shared/gr-fixed-panel/gr-fixed-panel_test.html',