diff options
Diffstat (limited to 'ext/js/application.js')
-rw-r--r-- | ext/js/application.js | 92 |
1 files changed, 49 insertions, 43 deletions
diff --git a/ext/js/application.js b/ext/js/application.js index 87bd0e86..13938aa8 100644 --- a/ext/js/application.js +++ b/ext/js/application.js @@ -58,8 +58,10 @@ if (checkChromeNotAvailable()) { export class Application extends EventDispatcher { /** * Creates a new instance. The instance should not be used until it has been fully prepare()'d. + * @param {API} api + * @param {CrossFrameAPI} crossFrameApi */ - constructor() { + constructor(api, crossFrameApi) { super(); /** @type {WebExtension} */ @@ -84,24 +86,17 @@ export class Application extends EventDispatcher { /** @type {?boolean} */ this._isBackground = null; - /** @type {?API} */ - this._api = null; - /** @type {?CrossFrameAPI} */ - this._crossFrame = null; + /** @type {API} */ + this._api = api; + /** @type {CrossFrameAPI} */ + this._crossFrame = crossFrameApi; /** @type {boolean} */ this._isReady = false; - const {promise, resolve} = /** @type {import('core').DeferredPromiseDetails<void>} */ (deferPromise()); - /** @type {Promise<void>} */ - this._isBackendReadyPromise = promise; - /** @type {?(() => void)} */ - this._isBackendReadyPromiseResolve = resolve; - /* eslint-disable no-multi-spaces */ /** @type {import('application').ApiMap} */ this._apiMap = createApiMap([ ['applicationIsReady', this._onMessageIsReady.bind(this)], - ['applicationBackendReady', this._onMessageBackendReady.bind(this)], ['applicationGetUrl', this._onMessageGetUrl.bind(this)], ['applicationOptionsUpdated', this._onMessageOptionsUpdated.bind(this)], ['applicationDatabaseUpdated', this._onMessageDatabaseUpdated.bind(this)], @@ -116,15 +111,6 @@ export class Application extends EventDispatcher { } /** - * Whether the current frame is the background page/service worker or not. - * @type {boolean} - */ - get isBackground() { - if (this._isBackground === null) { throw new Error('Not prepared'); } - return /** @type {boolean} */ (this._isBackground); - } - - /** * Gets the API instance for communicating with the backend. * This value will be null on the background page/service worker. * @type {API} @@ -146,23 +132,10 @@ export class Application extends EventDispatcher { /** * Prepares the instance for use. - * @param {boolean} [isBackground=false] Assigns whether this instance is being used from the background page/service worker. */ - async prepare(isBackground = false) { - this._isBackground = isBackground; + prepare() { chrome.runtime.onMessage.addListener(this._onMessage.bind(this)); - - if (!isBackground) { - this._api = new API(this._webExtension); - - await this._webExtension.sendMessagePromise({action: 'requestBackendReadySignal'}); - await this._isBackendReadyPromise; - - this._crossFrame = new CrossFrameAPI(this._api); - await this._crossFrame.prepare(); - - log.on('log', this._onForwardLog.bind(this)); - } + log.on('log', this._onForwardLog.bind(this)); } /** @@ -170,6 +143,7 @@ export class Application extends EventDispatcher { * setup has completed. */ ready() { + if (this._isReady) { return; } this._isReady = true; this._webExtension.sendMessagePromise({action: 'applicationReady'}); } @@ -193,6 +167,45 @@ export class Application extends EventDispatcher { this.trigger('closePopups', {}); } + /** + * @param {(application: Application) => (Promise<void>)} mainFunction + */ + static async main(mainFunction) { + const webExtension = new WebExtension(); + const api = new API(webExtension); + await this.waitForBackendReady(webExtension); + const {tabId = null, frameId = null} = await api.frameInformationGet(); + const crossFrameApi = new CrossFrameAPI(api, tabId, frameId); + crossFrameApi.prepare(); + const application = new Application(api, crossFrameApi); + application.prepare(); + try { + await mainFunction(application); + } catch (error) { + log.error(error); + } finally { + application.ready(); + } + } + + /** + * @param {WebExtension} webExtension + */ + static async waitForBackendReady(webExtension) { + const {promise, resolve} = /** @type {import('core').DeferredPromiseDetails<void>} */ (deferPromise()); + /** @type {import('application').ApiMap} */ + const apiMap = createApiMap([['applicationBackendReady', () => { resolve(); }]]); + /** @type {import('extension').ChromeRuntimeOnMessageCallback<import('application').ApiMessageAny>} */ + const onMessage = ({action, params}, _sender, callback) => invokeApiMapHandler(apiMap, action, params, [], callback); + chrome.runtime.onMessage.addListener(onMessage); + try { + await webExtension.sendMessagePromise({action: 'requestBackendReadySignal'}); + await promise; + } finally { + chrome.runtime.onMessage.removeListener(onMessage); + } + } + // Private /** @@ -212,13 +225,6 @@ export class Application extends EventDispatcher { return this._isReady; } - /** @type {import('application').ApiHandler<'applicationBackendReady'>} */ - _onMessageBackendReady() { - if (this._isBackendReadyPromiseResolve === null) { return; } - this._isBackendReadyPromiseResolve(); - this._isBackendReadyPromiseResolve = null; - } - /** @type {import('application').ApiHandler<'applicationGetUrl'>} */ _onMessageGetUrl() { return {url: this._getUrl()}; |