Improve performance of DisplaySearch's clipboard monitor

This commit is contained in:
toasted-nutbread 2019-12-12 20:59:54 -05:00
parent 362e317a5d
commit bf93d9f5f9

View File

@ -16,13 +16,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
let IS_FIREFOX = null;
(async () => {
const {browser} = await apiGetEnvironmentInfo();
IS_FIREFOX = ['firefox', 'firefox-mobile'].includes(browser);
})();
class DisplaySearch extends Display { class DisplaySearch extends Display {
constructor() { constructor() {
super(document.querySelector('#spinner'), document.querySelector('#content')); super(document.querySelector('#spinner'), document.querySelector('#content'));
@ -43,8 +36,12 @@ class DisplaySearch extends Display {
this.introVisible = true; this.introVisible = true;
this.introAnimationTimer = null; this.introAnimationTimer = null;
this.clipboardMonitorIntervalId = null; this.isFirefox = false;
this.clipboardPrevText = null;
this.clipboardMonitorTimerId = null;
this.clipboardMonitorTimerToken = null;
this.clipboardInterval = 250;
this.clipboardPreviousText = null;
} }
static create() { static create() {
@ -56,6 +53,7 @@ class DisplaySearch extends Display {
async prepare() { async prepare() {
try { try {
await this.initialize(); await this.initialize();
this.isFirefox = await DisplaySearch._isFirefox();
if (this.search !== null) { if (this.search !== null) {
this.search.addEventListener('click', (e) => this.onSearch(e), false); this.search.addEventListener('click', (e) => this.onSearch(e), false);
@ -241,39 +239,63 @@ class DisplaySearch extends Display {
initClipboardMonitor() { initClipboardMonitor() {
// ignore copy from search page // ignore copy from search page
window.addEventListener('copy', () => { window.addEventListener('copy', () => {
this.clipboardPrevText = document.getSelection().toString().trim(); this.clipboardPreviousText = document.getSelection().toString().trim();
}); });
} }
startClipboardMonitor() { startClipboardMonitor() {
this.clipboardMonitorIntervalId = setInterval(async () => { const token = {};
let curText = null; const intervalCallback = async () => {
// TODO get rid of this and figure out why apiClipboardGet doesn't work on Firefox this.clipboardMonitorTimerId = null;
if (IS_FIREFOX) {
curText = (await navigator.clipboard.readText()).trim();
} else if (IS_FIREFOX === false) {
curText = (await apiClipboardGet()).trim();
}
if (curText && (curText !== this.clipboardPrevText) && jpIsJapaneseText(curText)) {
if (this.isWanakanaEnabled()) {
this.setQuery(window.wanakana.toKana(curText));
} else {
this.setQuery(curText);
}
const queryString = curText.length > 0 ? `?query=${encodeURIComponent(curText)}` : ''; let text = await this.getClipboardText();
window.history.pushState(null, '', `${window.location.pathname}${queryString}`); if (this.clipboardMonitorTimerToken !== token) { return; }
if (
typeof text === 'string' &&
(text = text.trim()).length > 0 &&
text !== this.clipboardPreviousText
) {
this.clipboardPreviousText = text;
if (jpIsJapaneseText(text)) {
this.setQuery(this.isWanakanaEnabled() ? window.wanakana.toKana(text) : text);
window.history.pushState(null, '', `${window.location.pathname}?query=${encodeURIComponent(text)}`);
this.onSearchQueryUpdated(this.query.value, true); this.onSearchQueryUpdated(this.query.value, true);
this.clipboardPrevText = curText;
} }
}, 100); }
this.clipboardMonitorTimerId = setTimeout(intervalCallback, this.clipboardInterval);
};
this.clipboardMonitorTimerToken = token;
intervalCallback();
} }
stopClipboardMonitor() { stopClipboardMonitor() {
if (this.clipboardMonitorIntervalId) { this.clipboardMonitorTimerToken = null;
clearInterval(this.clipboardMonitorIntervalId); if (this.clipboardMonitorTimerId !== null) {
this.clipboardMonitorIntervalId = null; clearTimeout(this.clipboardMonitorTimerId);
this.clipboardMonitorTimerId = null;
}
}
async getClipboardText() {
/*
Notes:
apiClipboardGet doesn't work on firefox because document.execCommand('paste') requires
user interaction. Therefore, navigator.clipboard.readText() is used.
navigator.clipboard.readText() can't be used in Chrome for two reasons:
* Requires page to be focused, else it rejects with an exception.
* When the page is focused, Chrome will request clipboard permission, despite already
being an extension with clipboard permissions. It effectively asks for the
non-extension permission for clipboard access.
*/
try {
return this.isFirefox ? await navigator.clipboard.readText() : await apiClipboardGet();
} catch (e) {
return null;
} }
} }
@ -367,6 +389,17 @@ class DisplaySearch extends Display {
const match = /^[^?#]*\?(?:[^&#]*&)?query=([^&#]*)/.exec(url); const match = /^[^?#]*\?(?:[^&#]*&)?query=([^&#]*)/.exec(url);
return match !== null ? decodeURIComponent(match[1]) : null; return match !== null ? decodeURIComponent(match[1]) : null;
} }
static async _isFirefox() {
const {browser} = await apiGetEnvironmentInfo();
switch (browser) {
case 'firefox':
case 'firefox-mobile':
return true;
default:
return false;
}
}
} }
DisplaySearch.onKeyDownIgnoreKeys = { DisplaySearch.onKeyDownIgnoreKeys = {