summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/bg/js/backend.js99
-rw-r--r--ext/bg/js/context.js4
-rw-r--r--ext/bg/js/search-frontend.js2
-rw-r--r--ext/bg/js/search.js5
-rw-r--r--ext/bg/js/settings/backup.js6
-rw-r--r--ext/bg/js/settings/main.js5
-rw-r--r--ext/bg/js/util.js6
-rw-r--r--ext/fg/js/frontend-initialize.js2
-rw-r--r--ext/mixed/js/core.js13
-rw-r--r--ext/mixed/js/display.js1
-rw-r--r--test/test-database.js3
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
}
}
};