From 839e306cacbe122803b97589b89798d283069fb9 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 7 Jun 2020 21:50:14 -0400 Subject: [PATCH] Immediate backend event handlers (#555) * Add function to await until prepare is completed * Create BackendEventHandler to synchronously set up event handling --- ext/bg/js/backend.js | 117 ++++++++++++++++++++++++++++++----- ext/bg/js/background-main.js | 7 ++- 2 files changed, 106 insertions(+), 18 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 7971d16f..faba6c42 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -75,9 +75,15 @@ class Backend { this.popupWindow = null; - this._defaultBrowserActionTitle = null; this._isPrepared = false; this._prepareError = false; + this._preparePromise = null; + this._prepareCompletePromise = new Promise((resolve, reject) => { + this._prepareCompleteResolve = resolve; + this._prepareCompleteReject = reject; + }); + + this._defaultBrowserActionTitle = null; this._badgePrepareDelayTimer = null; this._logErrorLevel = null; @@ -134,7 +140,26 @@ class Backend { ]); } - async prepare() { + prepare() { + if (this._preparePromise === null) { + const promise = this._prepareInternal(); + promise.then( + (value) => { + this._isPrepared = true; + this._prepareCompleteResolve(value); + }, + (error) => { + this._prepareError = true; + this._prepareCompleteReject(error); + } + ); + promise.finally(() => this._updateBadge()); + this._preparePromise = promise; + } + return this._prepareCompletePromise; + } + + async _prepareInternal() { try { this._defaultBrowserActionTitle = await this._getBrowserIconTitle(); this._badgePrepareDelayTimer = setTimeout(() => { @@ -156,15 +181,6 @@ class Backend { this.onOptionsUpdated('background'); - if (isObject(chrome.commands) && isObject(chrome.commands.onCommand)) { - chrome.commands.onCommand.addListener(this._runCommand.bind(this)); - } - if (isObject(chrome.tabs) && isObject(chrome.tabs.onZoomChange)) { - chrome.tabs.onZoomChange.addListener(this._onZoomChange.bind(this)); - } - chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); - chrome.runtime.onConnect.addListener(this._onConnect.bind(this)); - const options = this.getOptions(this.optionsContext); if (options.general.showGuide) { chrome.tabs.create({url: chrome.runtime.getURL('/bg/guide.html')}); @@ -175,10 +191,7 @@ class Backend { this._sendMessageAllTabs('backendPrepared'); const callback = () => this.checkLastError(chrome.runtime.lastError); chrome.runtime.sendMessage({action: 'backendPrepared'}, callback); - - this._isPrepared = true; } catch (e) { - this._prepareError = true; yomichan.logError(e); throw e; } finally { @@ -186,15 +199,33 @@ class Backend { clearTimeout(this._badgePrepareDelayTimer); this._badgePrepareDelayTimer = null; } - - this._updateBadge(); } } + prepareComplete() { + return this._prepareCompletePromise; + } + isPrepared() { return this._isPrepared; } + handleCommand(...args) { + return this._runCommand(...args); + } + + handleZoomChange(...args) { + return this._onZoomChange(...args); + } + + handleConnect(...args) { + return this._onConnect(...args); + } + + handleMessage(...args) { + return this.onMessage(...args); + } + _sendMessageAllTabs(action, params={}) { const callback = () => this.checkLastError(chrome.runtime.lastError); chrome.tabs.query({}, (tabs) => { @@ -1293,3 +1324,57 @@ class Backend { } } } + +class BackendEventHandler { + constructor(backend) { + this._backend = backend; + } + + prepare() { + if (isObject(chrome.commands) && isObject(chrome.commands.onCommand)) { + const onCommand = this._createGenericEventHandler((...args) => this._backend.handleCommand(...args)); + chrome.commands.onCommand.addListener(onCommand); + } + + if (isObject(chrome.tabs) && isObject(chrome.tabs.onZoomChange)) { + const onZoomChange = this._createGenericEventHandler((...args) => this._backend.handleZoomChange(...args)); + chrome.tabs.onZoomChange.addListener(onZoomChange); + } + + const onConnect = this._createGenericEventHandler((...args) => this._backend.handleConnect(...args)); + chrome.runtime.onConnect.addListener(onConnect); + + const onMessage = this._onMessage.bind(this); + chrome.runtime.onMessage.addListener(onMessage); + } + + // Event handlers + + _createGenericEventHandler(handler) { + return this._onGenericEvent.bind(this, handler); + } + + _onGenericEvent(handler, ...args) { + if (this._backend.isPrepared()) { + handler(...args); + return; + } + + this._backend.prepareComplete().then( + () => { handler(...args); }, + () => {} // NOP + ); + } + + _onMessage(message, sender, sendResponse) { + if (this._backend.isPrepared()) { + return this._backend.handleMessage(message, sender, sendResponse); + } + + this._backend.prepareComplete().then( + () => { this._backend.handleMessage(message, sender, sendResponse); }, + () => { sendResponse(); } // NOP + ); + return true; + } +} diff --git a/ext/bg/js/background-main.js b/ext/bg/js/background-main.js index 345b4a77..9dd447c4 100644 --- a/ext/bg/js/background-main.js +++ b/ext/bg/js/background-main.js @@ -17,12 +17,15 @@ /* global * Backend + * BackendEventHandler */ -(async () => { +(() => { const backend = new Backend(); + const backendEventHandler = new BackendEventHandler(backend); + backendEventHandler.prepare(); if (typeof window === 'object' && window !== null) { window.yomichanBackend = backend; } - await backend.prepare(); + backend.prepare(); })();