From db9c07d8920b85dc5c6c065a76f950d4eeafd95f Mon Sep 17 00:00:00 2001
From: Kuuuube <61125188+Kuuuube@users.noreply.github.com>
Date: Tue, 17 Dec 2024 09:50:20 -0500
Subject: [PATCH 2/4] Fix bad performance.mark names causing
performance.measure to fail (#1676)
---
ext/js/display/display.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index 49404cc28..5730b292f 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -787,7 +787,7 @@ export class Display extends EventDispatcher {
async _onStateChanged() {
if (this._historyChangeIgnore) { return; }
- performance.mark('display:onStateChanged:start');
+ performance.mark('display:_onStateChanged:start');
/** @type {?import('core').TokenObject} */
const token = {}; // Unique identifier token
@@ -809,7 +809,7 @@ export class Display extends EventDispatcher {
performance.measure('display:clear', 'display:clear:start', 'display:clear:end');
// Prepare
- performance.mark('display:prepare:start');
+ performance.mark('display:_onStateChanged:prepare:start');
const urlSearchParams = new URLSearchParams(location.search);
let type = urlSearchParams.get('type');
if (type === null && urlSearchParams.get('query') !== null) { type = 'terms'; }
From f23c870b79526b7b3c1143d68c70f0771b4c001b Mon Sep 17 00:00:00 2001
From: Kuuuube <61125188+Kuuuube@users.noreply.github.com>
Date: Tue, 17 Dec 2024 10:12:55 -0500
Subject: [PATCH 3/4] Fix constant error on firefox mobile from
chrome.contextMenus (#1675)
* Fix constant error on firefox mobile from chrome.contextMenus
* Put if check over whole block
* Split out into separate method so it can early return
---
ext/js/background/backend.js | 17 +++++++++++++----
1 file changed, 13 insertions(+), 4 deletions(-)
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js
index 93a4330f5..efb40ed47 100644
--- a/ext/js/background/backend.js
+++ b/ext/js/background/backend.js
@@ -1330,7 +1330,20 @@ export class Backend {
this._clipboardMonitor.stop();
}
+ this._setupContextMenu(options);
+
+ void this._accessibilityController.update(this._getOptionsFull(false));
+
+ this._sendMessageAllTabsIgnoreResponse({action: 'applicationOptionsUpdated', params: {source}});
+ }
+
+ /**
+ * @param {import('settings').ProfileOptions} options
+ */
+ _setupContextMenu(options) {
try {
+ if (!chrome.contextMenus) { return; }
+
if (options.general.enableContextMenuScanSelected) {
chrome.contextMenus.create({
id: 'yomitan_lookup',
@@ -1348,10 +1361,6 @@ export class Backend {
} catch (e) {
log.error(e);
}
-
- void this._accessibilityController.update(this._getOptionsFull(false));
-
- this._sendMessageAllTabsIgnoreResponse({action: 'applicationOptionsUpdated', params: {source}});
}
/**
From b39bd51ea78987f0ce60c934a2a80a1f000cfd7c Mon Sep 17 00:00:00 2001
From: Kuuuube <61125188+Kuuuube@users.noreply.github.com>
Date: Tue, 17 Dec 2024 14:04:43 -0500
Subject: [PATCH 4/4] Add safePerformance to guard against bad performance
calls (#1677)
* Disallow the use of performance
* Add safePerformance
* Replace all performance with safePerformance
* Disconnect safePerformance from Performance due to constructor issues
* Fix missing import
* Add safe-performance to eslintrc
* Ignore errors for using performance in dev directory
* Add missing imports
---
.eslintrc.json | 6 +++
dev/bin/schema-validate.js | 3 ++
dev/dictionary-validate.js | 3 ++
ext/js/app/frontend.js | 7 +--
ext/js/app/popup.js | 3 +-
ext/js/comm/cross-frame-api.js | 3 +-
ext/js/core/promise-animation-frame.js | 4 +-
ext/js/core/safe-performance.js | 68 ++++++++++++++++++++++++
ext/js/dictionary/dictionary-database.js | 7 +--
ext/js/display/display-generator.js | 13 ++---
ext/js/display/display.js | 57 ++++++++++----------
ext/js/language/text-scanner.js | 13 ++---
ext/js/language/translator.js | 19 +++----
13 files changed, 148 insertions(+), 58 deletions(-)
create mode 100644 ext/js/core/safe-performance.js
diff --git a/.eslintrc.json b/.eslintrc.json
index bc8533d4f..16517610a 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -79,6 +79,10 @@
{
"message": "Avoid using Response.json(), prefer readResponseJson.",
"selector": "MemberExpression[property.name=json]"
+ },
+ {
+ "message": "Avoid using performance, prefer safePerformance.",
+ "selector": "MemberExpression[object.name=performance]"
}
],
"no-self-compare": "error",
@@ -582,6 +586,7 @@
"ext/js/core/extension-error.js",
"ext/js/core/json.js",
"ext/js/core/log.js",
+ "ext/js/core/safe-performance.js",
"ext/js/core/to-error.js",
"ext/js/core/utilities.js",
"ext/js/data/database.js",
@@ -619,6 +624,7 @@
"ext/js/core/log-utilities.js",
"ext/js/core/log.js",
"ext/js/core/object-utilities.js",
+ "ext/js/core/safe-performance.js",
"ext/js/core/to-error.js",
"ext/js/core/utilities.js",
"ext/js/data/anki-util.js",
diff --git a/dev/bin/schema-validate.js b/dev/bin/schema-validate.js
index 4baeb8aba..dc40e55f4 100644
--- a/dev/bin/schema-validate.js
+++ b/dev/bin/schema-validate.js
@@ -43,15 +43,18 @@ function main() {
const schema = parseJson(schemaSource);
for (const dataFileName of args.slice(1)) {
+ // eslint-disable-next-line no-restricted-syntax
const start = performance.now();
try {
console.log(`Validating ${dataFileName}...`);
const dataSource = fs.readFileSync(dataFileName, {encoding: 'utf8'});
const data = parseJson(dataSource);
createJsonSchema(mode, schema).validate(data);
+ // eslint-disable-next-line no-restricted-syntax
const end = performance.now();
console.log(`No issues detected (${((end - start) / 1000).toFixed(2)}s)`);
} catch (e) {
+ // eslint-disable-next-line no-restricted-syntax
const end = performance.now();
console.log(`Encountered an error (${((end - start) / 1000).toFixed(2)}s)`);
console.warn(e);
diff --git a/dev/dictionary-validate.js b/dev/dictionary-validate.js
index 18532ad69..77fa0fca3 100644
--- a/dev/dictionary-validate.js
+++ b/dev/dictionary-validate.js
@@ -131,14 +131,17 @@ export async function testDictionaryFiles(mode, dictionaryFileNames) {
const schemas = getSchemas();
for (const dictionaryFileName of dictionaryFileNames) {
+ // eslint-disable-next-line no-restricted-syntax
const start = performance.now();
try {
console.log(`Validating ${dictionaryFileName}...`);
const source = fs.readFileSync(dictionaryFileName);
await validateDictionary(mode, source.buffer, schemas);
+ // eslint-disable-next-line no-restricted-syntax
const end = performance.now();
console.log(`No issues detected (${((end - start) / 1000).toFixed(2)}s)`);
} catch (e) {
+ // eslint-disable-next-line no-restricted-syntax
const end = performance.now();
console.log(`Encountered an error (${((end - start) / 1000).toFixed(2)}s)`);
console.warn(e);
diff --git a/ext/js/app/frontend.js b/ext/js/app/frontend.js
index d532cc9b6..08d62a081 100644
--- a/ext/js/app/frontend.js
+++ b/ext/js/app/frontend.js
@@ -20,6 +20,7 @@ import {createApiMap, invokeApiMapHandler} from '../core/api-map.js';
import {EventListenerCollection} from '../core/event-listener-collection.js';
import {log} from '../core/log.js';
import {promiseAnimationFrame} from '../core/promise-animation-frame.js';
+import {safePerformance} from '../core/safe-performance.js';
import {setProfile} from '../data/profiles-util.js';
import {addFullscreenChangeEventListener, getFullscreenElement} from '../dom/document-util.js';
import {TextSourceElement} from '../dom/text-source-element.js';
@@ -952,13 +953,13 @@ export class Frontend {
* @returns {Promise}
*/
async _scanSelectedText(allowEmptyRange, disallowExpandSelection, showEmpty = false) {
- performance.mark('frontend:scanSelectedText:start');
+ safePerformance.mark('frontend:scanSelectedText:start');
const range = this._getFirstSelectionRange(allowEmptyRange);
if (range === null) { return false; }
const source = disallowExpandSelection ? TextSourceRange.createLazy(range) : TextSourceRange.create(range);
await this._textScanner.search(source, {focus: true, restoreSelection: true}, showEmpty);
- performance.mark('frontend:scanSelectedText:end');
- performance.measure('frontend:scanSelectedText', 'frontend:scanSelectedText:start', 'frontend:scanSelectedText:end');
+ safePerformance.mark('frontend:scanSelectedText:end');
+ safePerformance.measure('frontend:scanSelectedText', 'frontend:scanSelectedText:start', 'frontend:scanSelectedText:end');
return true;
}
diff --git a/ext/js/app/popup.js b/ext/js/app/popup.js
index cc7abb9e5..c8ecdf687 100644
--- a/ext/js/app/popup.js
+++ b/ext/js/app/popup.js
@@ -21,6 +21,7 @@ import {DynamicProperty} from '../core/dynamic-property.js';
import {EventDispatcher} from '../core/event-dispatcher.js';
import {EventListenerCollection} from '../core/event-listener-collection.js';
import {ExtensionError} from '../core/extension-error.js';
+import {safePerformance} from '../core/safe-performance.js';
import {deepEqual} from '../core/utilities.js';
import {addFullscreenChangeEventListener, computeZoomScale, convertRectZoomCoordinates, getFullscreenElement} from '../dom/document-util.js';
import {loadStyle} from '../dom/style-util.js';
@@ -302,7 +303,7 @@ export class Popup extends EventDispatcher {
await this._show(sourceRects, writingMode);
if (displayDetails !== null) {
- performance.mark('invokeDisplaySetContent:start');
+ safePerformance.mark('invokeDisplaySetContent:start');
void this._invokeSafe('displaySetContent', {details: displayDetails});
}
}
diff --git a/ext/js/comm/cross-frame-api.js b/ext/js/comm/cross-frame-api.js
index a55bbfdd4..1b7d3f69e 100644
--- a/ext/js/comm/cross-frame-api.js
+++ b/ext/js/comm/cross-frame-api.js
@@ -22,6 +22,7 @@ import {EventListenerCollection} from '../core/event-listener-collection.js';
import {ExtensionError} from '../core/extension-error.js';
import {parseJson} from '../core/json.js';
import {log} from '../core/log.js';
+import {safePerformance} from '../core/safe-performance.js';
/**
* @augments EventDispatcher
@@ -106,7 +107,7 @@ export class CrossFrameAPIPort extends EventDispatcher {
return;
}
}
- performance.mark(`cross-frame-api:invoke:${action}`);
+ safePerformance.mark(`cross-frame-api:invoke:${action}`);
try {
this._port.postMessage(/** @type {import('cross-frame-api').InvokeMessage} */ ({type: 'invoke', id, data: {action, params}}));
} catch (e) {
diff --git a/ext/js/core/promise-animation-frame.js b/ext/js/core/promise-animation-frame.js
index 0bcd6970d..23f643ace 100644
--- a/ext/js/core/promise-animation-frame.js
+++ b/ext/js/core/promise-animation-frame.js
@@ -16,6 +16,8 @@
* along with this program. If not, see .
*/
+import {safePerformance} from './safe-performance.js';
+
/**
* Creates a promise that will resolve after the next animation frame, using `requestAnimationFrame`.
* @param {number} [timeout] A maximum duration (in milliseconds) to wait until the promise resolves. If null or omitted, no timeout is used.
@@ -51,7 +53,7 @@ export function promiseAnimationFrame(timeout) {
cancelAnimationFrame(frameRequest);
frameRequest = null;
}
- resolve({time: performance.now(), timeout: true});
+ resolve({time: safePerformance.now(), timeout: true});
};
frameRequest = requestAnimationFrame(onFrame);
diff --git a/ext/js/core/safe-performance.js b/ext/js/core/safe-performance.js
new file mode 100644
index 000000000..0bf5534ac
--- /dev/null
+++ b/ext/js/core/safe-performance.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2024 Yomitan Authors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+import {log} from './log.js';
+
+/**
+ * This class safely handles performance methods.
+ */
+class SafePerformance {
+ constructor() {}
+
+ /**
+ * @param {string} markName
+ * @param {PerformanceMarkOptions} [markOptions]
+ * @returns {PerformanceMark | undefined}
+ */
+ mark(markName, markOptions) {
+ try {
+ // eslint-disable-next-line no-restricted-syntax
+ return performance.mark(markName, markOptions);
+ } catch (e) {
+ log.error(e);
+ }
+ }
+
+ /**
+ *
+ * @param {string} measureName
+ * @param {string | PerformanceMeasureOptions} [startOrMeasureOptions]
+ * @param {string} [endMark]
+ * @returns {PerformanceMeasure | undefined}
+ */
+ measure(measureName, startOrMeasureOptions, endMark) {
+ try {
+ // eslint-disable-next-line no-restricted-syntax
+ return performance.measure(measureName, startOrMeasureOptions, endMark);
+ } catch (e) {
+ log.error(e);
+ }
+ }
+
+ /**
+ * @returns {DOMHighResTimeStamp}
+ */
+ now() {
+ // eslint-disable-next-line no-restricted-syntax
+ return performance.now();
+ }
+}
+
+/**
+ * This object is the default performance measurer used by the runtime.
+ */
+export const safePerformance = new SafePerformance();
diff --git a/ext/js/dictionary/dictionary-database.js b/ext/js/dictionary/dictionary-database.js
index 5c14bcb0b..4eb601e8a 100644
--- a/ext/js/dictionary/dictionary-database.js
+++ b/ext/js/dictionary/dictionary-database.js
@@ -17,6 +17,7 @@
*/
import {log} from '../core/log.js';
+import {safePerformance} from '../core/safe-performance.js';
import {stringReverse} from '../core/utilities.js';
import {Database} from '../data/database.js';
@@ -455,7 +456,7 @@ export class DictionaryDatabase {
* @returns {Promise}
*/
_findMultiBulk(objectStoreName, indexNames, items, createQuery, predicate, createResult) {
- performance.mark('findMultiBulk:start');
+ safePerformance.mark('findMultiBulk:start');
return new Promise((resolve, reject) => {
const itemCount = items.length;
const indexCount = indexNames.length;
@@ -463,8 +464,8 @@ export class DictionaryDatabase {
const results = [];
if (itemCount === 0 || indexCount === 0) {
resolve(results);
- performance.mark('findMultiBulk:end');
- performance.measure('findMultiBulk', 'findMultiBulk:start', 'findMultiBulk:end');
+ safePerformance.mark('findMultiBulk:end');
+ safePerformance.measure('findMultiBulk', 'findMultiBulk:start', 'findMultiBulk:end');
return;
}
diff --git a/ext/js/display/display-generator.js b/ext/js/display/display-generator.js
index e181eff5f..48d0c797c 100644
--- a/ext/js/display/display-generator.js
+++ b/ext/js/display/display-generator.js
@@ -17,6 +17,7 @@
*/
import {ExtensionError} from '../core/extension-error.js';
+import {safePerformance} from '../core/safe-performance.js';
import {getDisambiguations, getGroupedPronunciations, getTermFrequency, groupKanjiFrequencies, groupTermFrequencies, groupTermTags, isNonNounVerbOrAdjective} from '../dictionary/dictionary-data-util.js';
import {HtmlTemplateCollection} from '../dom/html-template-collection.js';
import {distributeFurigana, getKanaMorae, getPitchCategory, isCodePointKanji} from '../language/ja/japanese.js';
@@ -111,23 +112,23 @@ export class DisplayGenerator {
node.dataset.groupedFrequencyCount = `${groupedFrequencies.length}`;
node.dataset.primaryMatchTypes = [...primaryMatchTypes].join(' ');
- performance.mark('displayGenerator:createTermEntry:createTermHeadword:start');
+ safePerformance.mark('displayGenerator:createTermEntry:createTermHeadword:start');
for (let i = 0, ii = headwords.length; i < ii; ++i) {
const node2 = this._createTermHeadword(headwords[i], i, pronunciations);
node2.dataset.index = `${i}`;
headwordsContainer.appendChild(node2);
}
headwordsContainer.dataset.count = `${headwords.length}`;
- performance.mark('displayGenerator:createTermEntry:createTermHeadword:end');
- performance.measure('displayGenerator:createTermEntry:createTermHeadword', 'displayGenerator:createTermEntry:createTermHeadword:start', 'displayGenerator:createTermEntry:createTermHeadword:end');
+ safePerformance.mark('displayGenerator:createTermEntry:createTermHeadword:end');
+ safePerformance.measure('displayGenerator:createTermEntry:createTermHeadword', 'displayGenerator:createTermEntry:createTermHeadword:start', 'displayGenerator:createTermEntry:createTermHeadword:end');
- performance.mark('displayGenerator:createTermEntry:promises:start');
+ safePerformance.mark('displayGenerator:createTermEntry:promises:start');
this._appendMultiple(inflectionRuleChainsContainer, this._createInflectionRuleChain.bind(this), inflectionRuleChainCandidates);
this._appendMultiple(frequencyGroupListContainer, this._createFrequencyGroup.bind(this), groupedFrequencies, false);
this._appendMultiple(groupedPronunciationsContainer, this._createGroupedPronunciation.bind(this), groupedPronunciations);
this._appendMultiple(headwordTagsContainer, this._createTermTag.bind(this), termTags, headwords.length);
- performance.mark('displayGenerator:createTermEntry:promises:end');
- performance.measure('displayGenerator:createTermEntry:promises', 'displayGenerator:createTermEntry:promises:start', 'displayGenerator:createTermEntry:promises:end');
+ safePerformance.mark('displayGenerator:createTermEntry:promises:end');
+ safePerformance.measure('displayGenerator:createTermEntry:promises', 'displayGenerator:createTermEntry:promises:start', 'displayGenerator:createTermEntry:promises:end');
for (const term of uniqueTerms) {
headwordTagsContainer.appendChild(this._createSearchTag(term));
diff --git a/ext/js/display/display.js b/ext/js/display/display.js
index 5730b292f..4066e3319 100644
--- a/ext/js/display/display.js
+++ b/ext/js/display/display.js
@@ -24,6 +24,7 @@ import {EventDispatcher} from '../core/event-dispatcher.js';
import {EventListenerCollection} from '../core/event-listener-collection.js';
import {ExtensionError} from '../core/extension-error.js';
import {log} from '../core/log.js';
+import {safePerformance} from '../core/safe-performance.js';
import {toError} from '../core/to-error.js';
import {clone, deepEqual, promiseTimeout} from '../core/utilities.js';
import {setProfile} from '../data/profiles-util.js';
@@ -733,7 +734,7 @@ export class Display extends EventDispatcher {
/** @type {import('display').DirectApiHandler<'displaySetContent'>} */
_onMessageSetContent({details}) {
- performance.mark('invokeDisplaySetContent:end');
+ safePerformance.mark('invokeDisplaySetContent:end');
this.setContent(details);
}
@@ -787,14 +788,14 @@ export class Display extends EventDispatcher {
async _onStateChanged() {
if (this._historyChangeIgnore) { return; }
- performance.mark('display:_onStateChanged:start');
+ safePerformance.mark('display:_onStateChanged:start');
/** @type {?import('core').TokenObject} */
const token = {}; // Unique identifier token
this._setContentToken = token;
try {
// Clear
- performance.mark('display:clear:start');
+ safePerformance.mark('display:clear:start');
this._closePopups();
this._closeAllPopupMenus();
this._eventListeners.removeAllEventListeners();
@@ -805,11 +806,11 @@ export class Display extends EventDispatcher {
this._dictionaryEntries = [];
this._dictionaryEntryNodes = [];
this._elementOverflowController.clearElements();
- performance.mark('display:clear:end');
- performance.measure('display:clear', 'display:clear:start', 'display:clear:end');
+ safePerformance.mark('display:clear:end');
+ safePerformance.measure('display:clear', 'display:clear:start', 'display:clear:end');
// Prepare
- performance.mark('display:_onStateChanged:prepare:start');
+ safePerformance.mark('display:_onStateChanged:prepare:start');
const urlSearchParams = new URLSearchParams(location.search);
let type = urlSearchParams.get('type');
if (type === null && urlSearchParams.get('query') !== null) { type = 'terms'; }
@@ -818,10 +819,10 @@ export class Display extends EventDispatcher {
this._queryParserVisibleOverride = (fullVisible === null ? null : (fullVisible !== 'false'));
this._historyHasChanged = true;
- performance.mark('display:_onStateChanged:prepare:end');
- performance.measure('display:_onStateChanged:prepare', 'display:_onStateChanged:prepare:start', 'display:_onStateChanged:prepare:end');
+ safePerformance.mark('display:_onStateChanged:prepare:end');
+ safePerformance.measure('display:_onStateChanged:prepare', 'display:_onStateChanged:prepare:start', 'display:_onStateChanged:prepare:end');
- performance.mark('display:_onStateChanged:setContent:start');
+ safePerformance.mark('display:_onStateChanged:setContent:start');
// Set content
switch (type) {
case 'terms':
@@ -838,13 +839,13 @@ export class Display extends EventDispatcher {
this._clearContent();
break;
}
- performance.mark('display:_onStateChanged:setContent:end');
- performance.measure('display:_onStateChanged:setContent', 'display:_onStateChanged:setContent:start', 'display:_onStateChanged:setContent:end');
+ safePerformance.mark('display:_onStateChanged:setContent:end');
+ safePerformance.measure('display:_onStateChanged:setContent', 'display:_onStateChanged:setContent:start', 'display:_onStateChanged:setContent:end');
} catch (e) {
this.onError(toError(e));
}
- performance.mark('display:_onStateChanged:end');
- performance.measure('display:_onStateChanged', 'display:_onStateChanged:start', 'display:_onStateChanged:end');
+ safePerformance.mark('display:_onStateChanged:end');
+ safePerformance.measure('display:_onStateChanged', 'display:_onStateChanged:start', 'display:_onStateChanged:end');
}
/**
@@ -1327,7 +1328,7 @@ export class Display extends EventDispatcher {
const hasEnabledDictionaries = this._options ? this._options.dictionaries.some(({enabled}) => enabled) : false;
// Set query
- performance.mark('display:setQuery:start');
+ safePerformance.mark('display:setQuery:start');
let query = urlSearchParams.get('query');
if (query === null) { query = ''; }
let queryFull = urlSearchParams.get('full');
@@ -1340,8 +1341,8 @@ export class Display extends EventDispatcher {
queryOffset = Number.isFinite(queryOffset) ? Math.max(0, Math.min(queryFull.length - query.length, queryOffset)) : 0;
}
this._setQuery(query, queryFull, queryOffset);
- performance.mark('display:setQuery:end');
- performance.measure('display:setQuery', 'display:setQuery:start', 'display:setQuery:end');
+ safePerformance.mark('display:setQuery:end');
+ safePerformance.measure('display:setQuery', 'display:setQuery:start', 'display:setQuery:end');
let {state, content} = this._history;
let changeHistory = false;
@@ -1364,10 +1365,10 @@ export class Display extends EventDispatcher {
let {dictionaryEntries} = content;
if (!Array.isArray(dictionaryEntries)) {
- performance.mark('display:findDictionaryEntries:start');
+ safePerformance.mark('display:findDictionaryEntries:start');
dictionaryEntries = hasEnabledDictionaries && lookup && query.length > 0 ? await this._findDictionaryEntries(type === 'kanji', query, primaryReading, wildcardsEnabled, optionsContext) : [];
- performance.mark('display:findDictionaryEntries:end');
- performance.measure('display:findDictionaryEntries', 'display:findDictionaryEntries:start', 'display:findDictionaryEntries:end');
+ safePerformance.mark('display:findDictionaryEntries:end');
+ safePerformance.measure('display:findDictionaryEntries', 'display:findDictionaryEntries:start', 'display:findDictionaryEntries:end');
if (this._setContentToken !== token) { return; }
content.dictionaryEntries = dictionaryEntries;
changeHistory = true;
@@ -1402,10 +1403,10 @@ export class Display extends EventDispatcher {
this._dictionaryEntries = dictionaryEntries;
- performance.mark('display:updateNavigationAuto:start');
+ safePerformance.mark('display:updateNavigationAuto:start');
this._updateNavigationAuto();
- performance.mark('display:updateNavigationAuto:end');
- performance.measure('display:updateNavigationAuto', 'display:updateNavigationAuto:start', 'display:updateNavigationAuto:end');
+ safePerformance.mark('display:updateNavigationAuto:end');
+ safePerformance.measure('display:updateNavigationAuto', 'display:updateNavigationAuto:start', 'display:updateNavigationAuto:end');
this._setNoContentVisible(hasEnabledDictionaries && dictionaryEntries.length === 0 && lookup);
this._setNoDictionariesVisible(!hasEnabledDictionaries);
@@ -1413,11 +1414,11 @@ export class Display extends EventDispatcher {
const container = this._container;
container.textContent = '';
- performance.mark('display:contentUpdate:start');
+ safePerformance.mark('display:contentUpdate:start');
this._triggerContentUpdateStart();
for (let i = 0, ii = dictionaryEntries.length; i < ii; ++i) {
- performance.mark('display:createEntry:start');
+ safePerformance.mark('display:createEntry:start');
if (i > 0) {
await promiseTimeout(1);
@@ -1441,8 +1442,8 @@ export class Display extends EventDispatcher {
this._elementOverflowController.addElements(entry);
- performance.mark('display:createEntry:end');
- performance.measure('display:createEntry', 'display:createEntry:start', 'display:createEntry:end');
+ safePerformance.mark('display:createEntry:end');
+ safePerformance.measure('display:createEntry', 'display:createEntry:start', 'display:createEntry:end');
}
if (typeof scrollX === 'number' || typeof scrollY === 'number') {
@@ -1454,8 +1455,8 @@ export class Display extends EventDispatcher {
}
this._triggerContentUpdateComplete();
- performance.mark('display:contentUpdate:end');
- performance.measure('display:contentUpdate', 'display:contentUpdate:start', 'display:contentUpdate:end');
+ safePerformance.mark('display:contentUpdate:end');
+ safePerformance.measure('display:contentUpdate', 'display:contentUpdate:start', 'display:contentUpdate:end');
}
/** */
diff --git a/ext/js/language/text-scanner.js b/ext/js/language/text-scanner.js
index 886491f41..c99d7c063 100644
--- a/ext/js/language/text-scanner.js
+++ b/ext/js/language/text-scanner.js
@@ -20,6 +20,7 @@ import {ThemeController} from '../app/theme-controller.js';
import {EventDispatcher} from '../core/event-dispatcher.js';
import {EventListenerCollection} from '../core/event-listener-collection.js';
import {log} from '../core/log.js';
+import {safePerformance} from '../core/safe-performance.js';
import {clone} from '../core/utilities.js';
import {anyNodeMatchesSelector, everyNodeMatchesSelector, getActiveModifiers, getActiveModifiersAndButtons, isPointInSelection} from '../dom/document-util.js';
import {TextSourceElement} from '../dom/text-source-element.js';
@@ -456,7 +457,7 @@ export class TextScanner extends EventDispatcher {
*/
async _search(textSource, searchTerms, searchKanji, inputInfo, showEmpty = false, disallowExpandStartOffset = false) {
try {
- performance.mark('scanner:_search:start');
+ safePerformance.mark('scanner:_search:start');
const isAltText = textSource instanceof TextSourceElement;
if (inputInfo.pointerType === 'touch') {
if (isAltText) {
@@ -530,8 +531,8 @@ export class TextScanner extends EventDispatcher {
} else {
this._triggerSearchEmpty(inputInfo);
}
- performance.mark('scanner:_search:end');
- performance.measure('scanner:_search', 'scanner:_search:start', 'scanner:_search:end');
+ safePerformance.mark('scanner:_search:end');
+ safePerformance.measure('scanner:_search', 'scanner:_search:start', 'scanner:_search:end');
} catch (error) {
this.trigger('searchError', {
error: error instanceof Error ? error : new Error(`A search error occurred: ${error}`),
@@ -1260,7 +1261,7 @@ export class TextScanner extends EventDispatcher {
if (this._pendingLookup) { return; }
try {
- performance.mark('scanner:_searchAt:start');
+ safePerformance.mark('scanner:_searchAt:start');
const sourceInput = inputInfo.input;
let searchTerms = this._searchTerms;
let searchKanji = this._searchKanji;
@@ -1290,8 +1291,8 @@ export class TextScanner extends EventDispatcher {
} else {
this._triggerSearchEmpty(inputInfo);
}
- performance.mark('scanner:_searchAt:end');
- performance.measure('scanner:_searchAt', 'scanner:_searchAt:start', 'scanner:_searchAt:end');
+ safePerformance.mark('scanner:_searchAt:end');
+ safePerformance.measure('scanner:_searchAt', 'scanner:_searchAt:start', 'scanner:_searchAt:end');
} catch (e) {
log.error(e);
} finally {
diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js
index 0020db3c7..584a39489 100644
--- a/ext/js/language/translator.js
+++ b/ext/js/language/translator.js
@@ -16,6 +16,7 @@
* along with this program. If not, see .
*/
+import {safePerformance} from '../core/safe-performance.js';
import {applyTextReplacement} from '../general/regex-util.js';
import {isCodePointJapanese} from './ja/japanese.js';
import {LanguageTransformer} from './language-transformer.js';
@@ -76,7 +77,7 @@ export class Translator {
* @returns {Promise<{dictionaryEntries: import('dictionary').TermDictionaryEntry[], originalTextLength: number}>} An object containing dictionary entries and the length of the original source text.
*/
async findTerms(mode, text, options) {
- performance.mark('translator:findTerms:start');
+ safePerformance.mark('translator:findTerms:start');
const {enabledDictionaryMap, excludeDictionaryDefinitions, sortFrequencyDictionary, sortFrequencyDictionaryOrder, language, primaryReading} = options;
const tagAggregator = new TranslatorTagAggregator();
let {dictionaryEntries, originalTextLength} = await this._findTermsInternal(text, options, tagAggregator, primaryReading);
@@ -122,8 +123,8 @@ export class Translator {
if (pronunciations.length > 1) { this._sortTermDictionaryEntrySimpleData(pronunciations); }
}
const withUserFacingInflections = this._addUserFacingInflections(language, dictionaryEntries);
- performance.mark('translator:findTerms:end');
- performance.measure('translator:findTerms', 'translator:findTerms:start', 'translator:findTerms:end');
+ safePerformance.mark('translator:findTerms:end');
+ safePerformance.measure('translator:findTerms', 'translator:findTerms:start', 'translator:findTerms:end');
return {dictionaryEntries: withUserFacingInflections, originalTextLength};
}
@@ -373,7 +374,7 @@ export class Translator {
* @returns {Promise}
*/
async _getDeinflections(text, options) {
- performance.mark('translator:getDeinflections:start');
+ safePerformance.mark('translator:getDeinflections:start');
let deinflections = (
options.deinflect ?
this._getAlgorithmDeinflections(text, options) :
@@ -396,8 +397,8 @@ export class Translator {
}
deinflections = deinflections.filter((deinflection) => deinflection.databaseEntries.length);
- performance.mark('translator:getDeinflections:end');
- performance.measure('translator:getDeinflections', 'translator:getDeinflections:start', 'translator:getDeinflections:end');
+ safePerformance.mark('translator:getDeinflections:end');
+ safePerformance.measure('translator:getDeinflections', 'translator:getDeinflections:start', 'translator:getDeinflections:end');
return deinflections;
}
@@ -409,7 +410,7 @@ export class Translator {
* @returns {Promise}
*/
async _getDictionaryDeinflections(language, deinflections, enabledDictionaryMap, matchType) {
- performance.mark('translator:getDictionaryDeinflections:start');
+ safePerformance.mark('translator:getDictionaryDeinflections:start');
/** @type {import('translation-internal').DatabaseDeinflection[]} */
const dictionaryDeinflections = [];
for (const deinflection of deinflections) {
@@ -440,8 +441,8 @@ export class Translator {
await this._addEntriesToDeinflections(language, dictionaryDeinflections, enabledDictionaryMap, matchType);
- performance.mark('translator:getDictionaryDeinflections:end');
- performance.measure('translator:getDictionaryDeinflections', 'translator:getDictionaryDeinflections:start', 'translator:getDictionaryDeinflections:end');
+ safePerformance.mark('translator:getDictionaryDeinflections:end');
+ safePerformance.measure('translator:getDictionaryDeinflections', 'translator:getDictionaryDeinflections:start', 'translator:getDictionaryDeinflections:end');
return dictionaryDeinflections;
}