diff options
| -rw-r--r-- | ext/bg/js/backend.js | 99 | ||||
| -rw-r--r-- | ext/bg/js/context.js | 4 | ||||
| -rw-r--r-- | ext/bg/js/search-frontend.js | 2 | ||||
| -rw-r--r-- | ext/bg/js/search.js | 5 | ||||
| -rw-r--r-- | ext/bg/js/settings/backup.js | 6 | ||||
| -rw-r--r-- | ext/bg/js/settings/main.js | 5 | ||||
| -rw-r--r-- | ext/bg/js/util.js | 6 | ||||
| -rw-r--r-- | ext/fg/js/frontend-initialize.js | 2 | ||||
| -rw-r--r-- | ext/mixed/js/core.js | 13 | ||||
| -rw-r--r-- | ext/mixed/js/display.js | 1 | ||||
| -rw-r--r-- | test/test-database.js | 3 | 
11 files changed, 88 insertions, 58 deletions
| diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index b99d1ca4..5b7ab084 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -39,8 +39,7 @@ class Backend {              url: window.location.href          }; -        this.isPreparedResolve = null; -        this.isPreparedPromise = new Promise((resolve) => (this.isPreparedResolve = resolve)); +        this.isPrepared = false;          this.clipboardPasteTarget = document.querySelector('#clipboard-paste-target'); @@ -51,6 +50,7 @@ class Backend {          this.messageToken = yomichan.generateId(16);          this._messageHandlers = new Map([ +            ['yomichanCoreReady', this._onApiYomichanCoreReady.bind(this)],              ['optionsSchemaGet', this._onApiOptionsSchemaGet.bind(this)],              ['optionsGet', this._onApiOptionsGet.bind(this)],              ['optionsGetFull', this._onApiOptionsGetFull.bind(this)], @@ -110,29 +110,33 @@ class Backend {          }          chrome.runtime.onMessage.addListener(this.onMessage.bind(this)); -        const options = this.getOptionsSync(this.optionsContext); +        this.isPrepared = true; + +        const options = this.getOptions(this.optionsContext);          if (options.general.showGuide) {              chrome.tabs.create({url: chrome.runtime.getURL('/bg/guide.html')});          } -        this.isPreparedResolve(); -        this.isPreparedResolve = null; -        this.isPreparedPromise = null; -          this.clipboardMonitor.onClipboardText = this._onClipboardText.bind(this); -    } -    onOptionsUpdated(source) { -        this.applyOptions(); +        this._sendMessageAllTabs('backendPrepared'); +        chrome.runtime.sendMessage({action: 'backendPrepared'}); +    } +    _sendMessageAllTabs(action, params={}) {          const callback = () => this.checkLastError(chrome.runtime.lastError);          chrome.tabs.query({}, (tabs) => {              for (const tab of tabs) { -                chrome.tabs.sendMessage(tab.id, {action: 'optionsUpdated', params: {source}}, callback); +                chrome.tabs.sendMessage(tab.id, {action, params}, callback);              }          });      } +    onOptionsUpdated(source) { +        this.applyOptions(); +        this._sendMessageAllTabs('optionsUpdated', {source}); +    } +      onMessage({action, params}, sender, callback) {          const handler = this._messageHandlers.get(action);          if (typeof handler !== 'function') { return false; } @@ -160,7 +164,7 @@ class Backend {      }      applyOptions() { -        const options = this.getOptionsSync(this.optionsContext); +        const options = this.getOptions(this.optionsContext);          if (!options.general.enable) {              this.setExtensionBadgeBackgroundColor('#555555');              this.setExtensionBadgeText('off'); @@ -186,24 +190,15 @@ class Backend {          }      } -    async getOptionsSchema() { -        if (this.isPreparedPromise !== null) { -            await this.isPreparedPromise; -        } +    getOptionsSchema() {          return this.optionsSchema;      } -    async getFullOptions() { -        if (this.isPreparedPromise !== null) { -            await this.isPreparedPromise; -        } +    getFullOptions() {          return this.options;      } -    async setFullOptions(options) { -        if (this.isPreparedPromise !== null) { -            await this.isPreparedPromise; -        } +    setFullOptions(options) {          try {              this.options = JsonSchema.getValidValueOrDefault(this.optionsSchema, utilIsolate(options));          } catch (e) { @@ -212,18 +207,11 @@ class Backend {          }      } -    async getOptions(optionsContext) { -        if (this.isPreparedPromise !== null) { -            await this.isPreparedPromise; -        } -        return this.getOptionsSync(optionsContext); -    } - -    getOptionsSync(optionsContext) { -        return this.getProfileSync(optionsContext).options; +    getOptions(optionsContext) { +        return this.getProfile(optionsContext).options;      } -    getProfileSync(optionsContext) { +    getProfile(optionsContext) {          const profiles = this.options.profiles;          if (typeof optionsContext.index === 'number') {              return profiles[optionsContext.index]; @@ -290,20 +278,33 @@ class Backend {      // Message handlers -    _onApiOptionsSchemaGet() { +    _onApiYomichanCoreReady(_params, sender) { +        // tab ID isn't set in background (e.g. browser_action) +        if (typeof sender.tab === 'undefined') { +            chrome.runtime.sendMessage({action: 'backendPrepared'}); +            return Promise.resolve(); +        } + +        const tabId = sender.tab.id; +        return new Promise((resolve) => { +            chrome.tabs.sendMessage(tabId, {action: 'backendPrepared'}, resolve); +        }); +    } + +    async _onApiOptionsSchemaGet() {          return this.getOptionsSchema();      } -    _onApiOptionsGet({optionsContext}) { +    async _onApiOptionsGet({optionsContext}) {          return this.getOptions(optionsContext);      } -    _onApiOptionsGetFull() { +    async _onApiOptionsGetFull() {          return this.getFullOptions();      }      async _onApiOptionsSet({changedOptions, optionsContext, source}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          function getValuePaths(obj) {              const valuePaths = []; @@ -343,20 +344,20 @@ class Backend {      }      async _onApiOptionsSave({source}) { -        const options = await this.getFullOptions(); +        const options = this.getFullOptions();          await optionsSave(options);          this.onOptionsUpdated(source);      }      async _onApiKanjiFind({text, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          const definitions = await this.translator.findKanji(text, options);          definitions.splice(options.general.maxResults);          return definitions;      }      async _onApiTermsFind({text, details, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          const mode = options.general.resultOutputMode;          const [definitions, length] = await this.translator.findTerms(mode, text, details, options);          definitions.splice(options.general.maxResults); @@ -364,7 +365,7 @@ class Backend {      }      async _onApiTextParse({text, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          const results = [];          while (text.length > 0) {              const term = []; @@ -394,7 +395,7 @@ class Backend {      }      async _onApiTextParseMecab({text, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          const results = [];          const rawResults = await this.mecab.parseText(text);          for (const [mecabName, parsedLines] of Object.entries(rawResults)) { @@ -425,7 +426,7 @@ class Backend {      }      async _onApiDefinitionAdd({definition, mode, context, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          const templates = this.defaultAnkiFieldTemplates;          if (mode !== 'kanji') { @@ -450,7 +451,7 @@ class Backend {      }      async _onApiDefinitionsAddable({definitions, modes, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          const templates = this.defaultAnkiFieldTemplates;          const states = []; @@ -497,7 +498,7 @@ class Backend {      }      async _onApiNoteView({noteId}) { -        return this.anki.guiBrowse(`nid:${noteId}`); +        return await this.anki.guiBrowse(`nid:${noteId}`);      }      async _onApiTemplateRender({template, data}) { @@ -509,7 +510,7 @@ class Backend {      }      async _onApiAudioGetUrl({definition, source, optionsContext}) { -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          return await audioGetUrl(definition, source, options);      } @@ -668,7 +669,7 @@ class Backend {      async _onCommandSearch(params) {          const {mode='existingOrNewTab', query} = params || {}; -        const options = await this.getOptions(this.optionsContext); +        const options = this.getOptions(this.optionsContext);          const {popupWidth, popupHeight} = options.general;          const baseUrl = chrome.runtime.getURL('/bg/search.html'); @@ -752,7 +753,7 @@ class Backend {          };          const source = 'popup'; -        const options = await this.getOptions(optionsContext); +        const options = this.getOptions(optionsContext);          options.general.enable = !options.general.enable;          await this._onApiOptionsSave({source});      } diff --git a/ext/bg/js/context.js b/ext/bg/js/context.js index bec964fb..1095c7e0 100644 --- a/ext/bg/js/context.js +++ b/ext/bg/js/context.js @@ -48,7 +48,9 @@ function setupButtonEvents(selector, command, url) {      }  } -window.addEventListener('DOMContentLoaded', () => { +window.addEventListener('DOMContentLoaded', async () => { +    await yomichan.prepare(); +      showExtensionInfo();      apiGetEnvironmentInfo().then(({browser}) => { diff --git a/ext/bg/js/search-frontend.js b/ext/bg/js/search-frontend.js index 509c4009..453a0b79 100644 --- a/ext/bg/js/search-frontend.js +++ b/ext/bg/js/search-frontend.js @@ -19,6 +19,8 @@  /*global apiOptionsGet*/  async function searchFrontendSetup() { +    await yomichan.prepare(); +      const optionsContext = {          depth: 0,          url: window.location.href diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 0a7a5fe1..f3cba7ae 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -68,9 +68,8 @@ class DisplaySearch extends Display {      async prepare() {          try { -            const superPromise = super.prepare(); -            const queryParserPromise = this.queryParser.prepare(); -            await Promise.all([superPromise, queryParserPromise]); +            await super.prepare(); +            await this.queryParser.prepare();              const {queryParams: {query='', mode=''}} = parseUrl(window.location.href); diff --git a/ext/bg/js/settings/backup.js b/ext/bg/js/settings/backup.js index e945d186..daa08c61 100644 --- a/ext/bg/js/settings/backup.js +++ b/ext/bg/js/settings/backup.js @@ -120,7 +120,7 @@ async function _onSettingsExportClick() {  // Importing  async function _settingsImportSetOptionsFull(optionsFull) { -    return utilIsolate(await utilBackend().setFullOptions( +    return utilIsolate(utilBackend().setFullOptions(          utilBackgroundIsolate(optionsFull)      ));  } @@ -362,10 +362,10 @@ async function _onSettingsResetConfirmClick() {  // Setup -window.addEventListener('DOMContentLoaded', () => { +function backupInitialize() {      document.querySelector('#settings-export').addEventListener('click', _onSettingsExportClick, false);      document.querySelector('#settings-import').addEventListener('click', _onSettingsImportClick, false);      document.querySelector('#settings-import-file').addEventListener('change', _onSettingsImportFileChange, false);      document.querySelector('#settings-reset').addEventListener('click', _onSettingsResetClick, false);      document.querySelector('#settings-reset-modal-confirm').addEventListener('click', _onSettingsResetConfirmClick, false); -}, false); +} diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 127a6d2b..1bf1444c 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -21,7 +21,7 @@ utilBackend, utilIsolate, utilBackgroundIsolate  ankiErrorShown, ankiFieldsToDict  ankiTemplatesUpdateValue, onAnkiOptionsChanged, onDictionaryOptionsChanged  appearanceInitialize, audioSettingsInitialize, profileOptionsSetup, dictSettingsInitialize -ankiInitialize, ankiTemplatesInitialize, storageInfoInitialize +ankiInitialize, ankiTemplatesInitialize, storageInfoInitialize, backupInitialize  */  function getOptionsMutable(optionsContext) { @@ -262,6 +262,8 @@ function showExtensionInformation() {  async function onReady() { +    await yomichan.prepare(); +      showExtensionInformation();      formSetupEventListeners(); @@ -271,6 +273,7 @@ async function onReady() {      await dictSettingsInitialize();      ankiInitialize();      ankiTemplatesInitialize(); +    backupInitialize();      storageInfoInitialize(); diff --git a/ext/bg/js/util.js b/ext/bg/js/util.js index 5ce4b08c..79c6af06 100644 --- a/ext/bg/js/util.js +++ b/ext/bg/js/util.js @@ -73,7 +73,11 @@ function utilStringHashCode(string) {  }  function utilBackend() { -    return chrome.extension.getBackgroundPage().yomichanBackend; +    const backend = chrome.extension.getBackgroundPage().yomichanBackend; +    if (!backend.isPrepared) { +        throw new Error('Backend not ready yet'); +    } +    return backend;  }  async function utilAnkiGetModelNames() { diff --git a/ext/fg/js/frontend-initialize.js b/ext/fg/js/frontend-initialize.js index 54b874f2..bbb789cc 100644 --- a/ext/fg/js/frontend-initialize.js +++ b/ext/fg/js/frontend-initialize.js @@ -19,6 +19,8 @@  /*global PopupProxyHost, PopupProxy, Frontend*/  async function main() { +    await yomichan.prepare(); +      const data = window.frontendInitializationData || {};      const {id, depth=0, parentFrameId, ignoreNodes, url, proxy=false} = data; diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 83813796..0e22b9ac 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -269,7 +269,11 @@ const yomichan = (() => {          constructor() {              super(); +            this._isBackendPreparedResolve = null; +            this._isBackendPreparedPromise = new Promise((resolve) => (this._isBackendPreparedResolve = resolve)); +              this._messageHandlers = new Map([ +                ['backendPrepared', this._onBackendPrepared.bind(this)],                  ['getUrl', this._onMessageGetUrl.bind(this)],                  ['optionsUpdated', this._onMessageOptionsUpdated.bind(this)],                  ['zoomChanged', this._onMessageZoomChanged.bind(this)] @@ -280,6 +284,11 @@ const yomichan = (() => {          // Public +        prepare() { +            chrome.runtime.sendMessage({action: 'yomichanCoreReady'}); +            return this._isBackendPreparedPromise; +        } +          generateId(length) {              const array = new Uint8Array(length);              window.crypto.getRandomValues(array); @@ -305,6 +314,10 @@ const yomichan = (() => {              return false;          } +        _onBackendPrepared() { +            this._isBackendPreparedResolve(); +        } +          _onMessageGetUrl() {              return {url: window.location.href};          } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index e3e5e7df..6a762a65 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -153,6 +153,7 @@ class Display {      }      async prepare(options=null) { +        await yomichan.prepare();          const displayGeneratorPromise = this.displayGenerator.prepare();          const updateOptionsPromise = this.updateOptions(options);          await Promise.all([displayGeneratorPromise, updateOptionsPromise]); diff --git a/test/test-database.js b/test/test-database.js index 35f22523..9a24a393 100644 --- a/test/test-database.js +++ b/test/test-database.js @@ -30,6 +30,9 @@ const chrome = {          },          getURL(path2) {              return url.pathToFileURL(path.join(__dirname, '..', 'ext', path2.replace(/^\//, ''))); +        }, +        sendMessage() { +            // NOP          }      }  }; |