From ddc7c71e4f9da861d9d2bd56e60804ee9e70f621 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 25 Jan 2020 17:54:07 +0200 Subject: [PATCH 01/24] add support for native popup windows --- ext/bg/js/backend.js | 54 ++++++++++++++++++++++++++++++++------------ ext/bg/js/context.js | 4 ++-- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 43ee81c3..23811e9d 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -33,6 +33,7 @@ class Backend { this.isPreparedPromise = new Promise((resolve) => (this.isPreparedResolve = resolve)); this.clipboardPasteTarget = document.querySelector('#clipboard-paste-target'); + this.popupWindow = null; this.apiForwarder = new BackendApiForwarder(); } @@ -565,23 +566,46 @@ class Backend { // Command handlers async _onCommandSearch(params) { - const url = chrome.runtime.getURL('/bg/search.html'); - if (!(params && params.newTab)) { - try { - const tab = await Backend._findTab(1000, (url2) => ( - url2 !== null && - url2.startsWith(url) && - (url2.length === url.length || url2[url.length] === '?' || url2[url.length] === '#') - )); - if (tab !== null) { - await Backend._focusTab(tab); - return; + const {mode, query} = params || {}; + + const optionsContext = {depth: 0}; + const options = await this.getOptions(optionsContext); + const {popupWidth, popupHeight} = options.general; + + const baseUrl = chrome.runtime.getURL('/bg/search.html'); + const queryString = (query && query.length > 0) ? `?query=${encodeURIComponent(query)}` : ''; + const url = baseUrl + queryString; + + switch (mode) { + case 'sameTab': + try { + const tab = await Backend._findTab(1000, (url2) => ( + url2 !== null && + url2.startsWith(url) && + (url2.length === url.length || url2[url.length] === '?' || url2[url.length] === '#') + )); + if (tab !== null) { + await Backend._focusTab(tab); + return; + } + } catch (e) { + // NOP } - } catch (e) { - // NOP - } + chrome.tabs.create({url}); + return; + case 'newTab': + chrome.tabs.create({url}); + return; + case 'popup': + if (this.popupWindow !== null) { + chrome.windows.remove(this.popupWindow.id); + } + chrome.windows.create( + {url, width: popupWidth, height: popupHeight, type: 'popup'}, + (popupWindow) => { this.popupWindow = popupWindow; } + ); + return; } - chrome.tabs.create({url}); } _onCommandHelp() { diff --git a/ext/bg/js/context.js b/ext/bg/js/context.js index 834174bf..a0e5adc9 100644 --- a/ext/bg/js/context.js +++ b/ext/bg/js/context.js @@ -30,12 +30,12 @@ function setupButtonEvents(selector, command, url) { for (const node of nodes) { node.addEventListener('click', (e) => { if (e.button !== 0) { return; } - apiCommandExec(command, {newTab: e.ctrlKey}); + apiCommandExec(command, {mode: e.ctrlKey ? 'newTab' : 'sameTab'}); e.preventDefault(); }, false); node.addEventListener('auxclick', (e) => { if (e.button !== 1) { return; } - apiCommandExec(command, {newTab: true}); + apiCommandExec(command, {mode: 'newTab'}); e.preventDefault(); }, false); From 679e42c21ccc3ab778f4b26406b353769e171878 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 25 Jan 2020 18:11:19 +0200 Subject: [PATCH 02/24] move apiClipboardGet Firefox handling to Backend --- ext/bg/js/backend.js | 31 ++++++++++++++++++++++++------- ext/bg/js/search.js | 44 +++++++------------------------------------- 2 files changed, 31 insertions(+), 44 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 23811e9d..2fb7711f 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -522,13 +522,30 @@ class Backend { } async _onApiClipboardGet() { - const clipboardPasteTarget = this.clipboardPasteTarget; - clipboardPasteTarget.value = ''; - clipboardPasteTarget.focus(); - document.execCommand('paste'); - const result = clipboardPasteTarget.value; - clipboardPasteTarget.value = ''; - return result; + /* + Notes: + document.execCommand('paste') doesn't work on Firefox. + This may be a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1603985 + Therefore, navigator.clipboard.readText() is used on Firefox. + + 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. + */ + const browser = await Backend._getBrowser(); + if (browser === 'firefox' || browser === 'firefox-mobile') { + return await navigator.clipboard.readText(); + } else { + const clipboardPasteTarget = this.clipboardPasteTarget; + clipboardPasteTarget.value = ''; + clipboardPasteTarget.focus(); + document.execCommand('paste'); + const result = clipboardPasteTarget.value; + clipboardPasteTarget.value = ''; + return result; + } } async _onApiGetDisplayTemplatesHtml() { diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index f5c641a8..9508346e 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -36,8 +36,6 @@ class DisplaySearch extends Display { this.introVisible = true; this.introAnimationTimer = null; - this.isFirefox = false; - this.clipboardMonitorTimerId = null; this.clipboardMonitorTimerToken = null; this.clipboardInterval = 250; @@ -53,7 +51,6 @@ class DisplaySearch extends Display { async prepare() { try { await this.initialize(); - this.isFirefox = await DisplaySearch._isFirefox(); if (this.search !== null) { this.search.addEventListener('click', (e) => this.onSearch(e), false); @@ -250,13 +247,18 @@ class DisplaySearch extends Display { startClipboardMonitor() { // The token below is used as a unique identifier to ensure that a new clipboard monitor - // hasn't been started during the await call. The check below the await this.getClipboardText() + // hasn't been started during the await call. The check below the await apiClipboardGet() // call will exit early if the reference has changed. const token = {}; const intervalCallback = async () => { this.clipboardMonitorTimerId = null; - let text = await this.getClipboardText(); + let text = null; + try { + text = await apiClipboardGet(); + } catch (e) { + // NOP + } if (this.clipboardMonitorTimerToken !== token) { return; } if ( @@ -288,27 +290,6 @@ class DisplaySearch extends Display { } } - async getClipboardText() { - /* - Notes: - apiClipboardGet doesn't work on Firefox because document.execCommand('paste') - results in an empty string on the web extension background page. - This may be a bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1603985 - Therefore, navigator.clipboard.readText() is used on Firefox. - - 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; - } - } - isWanakanaEnabled() { return this.wanakanaEnable !== null && this.wanakanaEnable.checked; } @@ -399,17 +380,6 @@ class DisplaySearch extends Display { const match = /^[^?#]*\?(?:[^&#]*&)?query=([^&#]*)/.exec(url); 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 = { From f29abfc5115b06281c18467d7cf7b43bf133da82 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 25 Jan 2020 18:18:40 +0200 Subject: [PATCH 03/24] use correct optionsContext --- ext/bg/js/backend.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 2fb7711f..3cb9ce1d 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -585,8 +585,7 @@ class Backend { async _onCommandSearch(params) { const {mode, query} = params || {}; - const optionsContext = {depth: 0}; - const options = await this.getOptions(optionsContext); + const options = await this.getOptions(this.optionsContext); const {popupWidth, popupHeight} = options.general; const baseUrl = chrome.runtime.getURL('/bg/search.html'); From 939ad42dacfeff566497f3c2f8e9c64d59b8168d Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sat, 25 Jan 2020 19:00:36 +0200 Subject: [PATCH 04/24] add global clipboard monitor that spawns popups TODO: refactor the search page clipboard monitor and popup clipboard monitor to use a common ClipboardMonitor class --- ext/bg/data/options-schema.json | 5 +++++ ext/bg/js/backend.js | 17 +++++++++++++++++ ext/bg/js/options.js | 1 + ext/bg/js/settings/main.js | 1 + ext/bg/settings.html | 4 ++++ 5 files changed, 28 insertions(+) diff --git a/ext/bg/data/options-schema.json b/ext/bg/data/options-schema.json index 7e12481d..d6207952 100644 --- a/ext/bg/data/options-schema.json +++ b/ext/bg/data/options-schema.json @@ -79,6 +79,7 @@ "type": "object", "required": [ "enable", + "enableClipboardPopups", "resultOutputMode", "debugInfo", "maxResults", @@ -111,6 +112,10 @@ "type": "boolean", "default": true }, + "enableClipboardPopups": { + "type": "boolean", + "default": false + }, "resultOutputMode": { "type": "string", "enum": ["group", "merge", "split"], diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 3cb9ce1d..407dc965 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -34,6 +34,9 @@ class Backend { this.clipboardPasteTarget = document.querySelector('#clipboard-paste-target'); this.popupWindow = null; + this.clipboardPopupTimerId = null; + this.clipboardInterval = 250; + this.clipboardPreviousText = null; this.apiForwarder = new BackendApiForwarder(); } @@ -122,6 +125,20 @@ class Backend { } else { this.mecab.stopListener(); } + + window.clearInterval(this.clipboardPopupTimerId); + if (options.general.enableClipboardPopups) { + this.clipboardPopupTimerId = setInterval(() => { + this._onApiClipboardGet() + .then((result) => { + if (this.clipboardPreviousText === result) { + return; + } + this._onCommandSearch({mode: 'popup', query: result}); + this.clipboardPreviousText = result; + }); + }, this.clipboardInterval); + } } async getOptionsSchema() { diff --git a/ext/bg/js/options.js b/ext/bg/js/options.js index 78508059..97032660 100644 --- a/ext/bg/js/options.js +++ b/ext/bg/js/options.js @@ -266,6 +266,7 @@ function profileOptionsCreateDefaults() { return { general: { enable: true, + enableClipboardPopups: false, resultOutputMode: 'group', debugInfo: false, maxResults: 32, diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 4492cd42..ad3459f0 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -28,6 +28,7 @@ function getOptionsFullMutable() { async function formRead(options) { options.general.enable = $('#enable').prop('checked'); + options.general.enableClipboardPopups = $('#enable-clipboard-popups').prop('checked'); options.general.showGuide = $('#show-usage-guide').prop('checked'); options.general.compactTags = $('#compact-tags').prop('checked'); options.general.compactGlossaries = $('#compact-glossaries').prop('checked'); diff --git a/ext/bg/settings.html b/ext/bg/settings.html index 3480b124..b0fcec2b 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -134,6 +134,10 @@ +
+ +
+
From 222f869c842b13d4bc7464a8fa2cceff6f64fa00 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Jan 2020 02:11:48 +0200 Subject: [PATCH 05/24] fix search page hotkey --- ext/bg/js/backend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 407dc965..d80e7713 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -600,7 +600,7 @@ class Backend { // Command handlers async _onCommandSearch(params) { - const {mode, query} = params || {}; + const {mode, query} = params || {mode: 'sameTab'}; const options = await this.getOptions(this.optionsContext); const {popupWidth, popupHeight} = options.general; From 37a922adc0721520dac2f376b85ed2c8b5e42e69 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Jan 2020 02:13:01 +0200 Subject: [PATCH 06/24] fix settings page checkbox --- ext/bg/js/settings/main.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index ad3459f0..55472a7b 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -105,6 +105,7 @@ async function formRead(options) { async function formWrite(options) { $('#enable').prop('checked', options.general.enable); + $('#enable-clipboard-popups').prop('checked', options.general.enableClipboardPopups); $('#show-usage-guide').prop('checked', options.general.showGuide); $('#compact-tags').prop('checked', options.general.compactTags); $('#compact-glossaries').prop('checked', options.general.compactGlossaries); From c685fd0e5f90c32911a225f0aaa68f5ff9fdfb7b Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Jan 2020 03:02:33 +0200 Subject: [PATCH 07/24] extract ClipboardMonitor from DisplaySearch --- ext/bg/js/clipboard-monitor.js | 78 ++++++++++++++++++++++++++++++++++ ext/bg/js/search.js | 78 ++++++++-------------------------- ext/bg/search.html | 1 + 3 files changed, 97 insertions(+), 60 deletions(-) create mode 100644 ext/bg/js/clipboard-monitor.js diff --git a/ext/bg/js/clipboard-monitor.js b/ext/bg/js/clipboard-monitor.js new file mode 100644 index 00000000..579cdc56 --- /dev/null +++ b/ext/bg/js/clipboard-monitor.js @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 Alex Yatskov + * Author: Alex Yatskov + * + * 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 . + */ + + +class ClipboardMonitor { + constructor() { + this.timerId = null; + this.timerToken = null; + this.interval = 250; + this.previousText = null; + } + + onClipboardText(_text) { + throw new Error('Override me'); + } + + start() { + // The token below is used as a unique identifier to ensure that a new clipboard monitor + // hasn't been started during the await call. The check below the await apiClipboardGet() + // call will exit early if the reference has changed. + const token = {}; + const intervalCallback = async () => { + this.timerId = null; + + let text = null; + try { + text = await apiClipboardGet(); + } catch (e) { + // NOP + } + if (this.timerToken !== token) { return; } + + if ( + typeof text === 'string' && + (text = text.trim()).length > 0 && + text !== this.previousText + ) { + this.previousText = text; + if (jpIsStringPartiallyJapanese(text)) { + this.onClipboardText(text); + } + } + + this.timerId = setTimeout(intervalCallback, this.interval); + }; + + this.timerToken = token; + + intervalCallback(); + } + + stop() { + this.timerToken = null; + if (this.timerId !== null) { + clearTimeout(this.timerId); + this.timerId = null; + } + } + + setPreviousText(text) { + this.previousText = text; + } +} diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 9508346e..e32ba46e 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -36,10 +36,7 @@ class DisplaySearch extends Display { this.introVisible = true; this.introAnimationTimer = null; - this.clipboardMonitorTimerId = null; - this.clipboardMonitorTimerToken = null; - this.clipboardInterval = 250; - this.clipboardPreviousText = null; + this.clipboardMonitor = new ClipboardMonitor(); } static create() { @@ -93,7 +90,7 @@ class DisplaySearch extends Display { if (this.clipboardMonitorEnable !== null) { if (this.options.general.enableClipboardMonitor === true) { this.clipboardMonitorEnable.checked = true; - this.startClipboardMonitor(); + this.clipboardMonitor.start(); } else { this.clipboardMonitorEnable.checked = false; } @@ -103,7 +100,7 @@ class DisplaySearch extends Display { {permissions: ['clipboardRead']}, (granted) => { if (granted) { - this.startClipboardMonitor(); + this.clipboardMonitor.start(); apiOptionsSet({general: {enableClipboardMonitor: true}}, this.getOptionsContext()); } else { e.target.checked = false; @@ -111,16 +108,18 @@ class DisplaySearch extends Display { } ); } else { - this.stopClipboardMonitor(); + this.clipboardMonitor.stop(); apiOptionsSet({general: {enableClipboardMonitor: false}}, this.getOptionsContext()); } }); } window.addEventListener('popstate', (e) => this.onPopState(e)); + window.addEventListener('copy', (e) => this.onCopy(e)); + + this.clipboardMonitor.onClipboardText = (text) => this.onClipboardText(text); this.updateSearchButton(); - this.initClipboardMonitor(); } catch (e) { this.onError(e); } @@ -199,6 +198,17 @@ class DisplaySearch extends Display { } } + onCopy() { + // ignore copy from search page + this.clipboardMonitor.setPreviousText(document.getSelection().toString().trim()); + } + + onClipboardText(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); + } + async onSearchQueryUpdated(query, animate) { try { const details = {}; @@ -238,58 +248,6 @@ class DisplaySearch extends Display { this.queryParser.setOptions(this.options); } - initClipboardMonitor() { - // ignore copy from search page - window.addEventListener('copy', () => { - this.clipboardPreviousText = document.getSelection().toString().trim(); - }); - } - - startClipboardMonitor() { - // The token below is used as a unique identifier to ensure that a new clipboard monitor - // hasn't been started during the await call. The check below the await apiClipboardGet() - // call will exit early if the reference has changed. - const token = {}; - const intervalCallback = async () => { - this.clipboardMonitorTimerId = null; - - let text = null; - try { - text = await apiClipboardGet(); - } catch (e) { - // NOP - } - if (this.clipboardMonitorTimerToken !== token) { return; } - - if ( - typeof text === 'string' && - (text = text.trim()).length > 0 && - text !== this.clipboardPreviousText - ) { - this.clipboardPreviousText = text; - if (jpIsStringPartiallyJapanese(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.clipboardMonitorTimerId = setTimeout(intervalCallback, this.clipboardInterval); - }; - - this.clipboardMonitorTimerToken = token; - - intervalCallback(); - } - - stopClipboardMonitor() { - this.clipboardMonitorTimerToken = null; - if (this.clipboardMonitorTimerId !== null) { - clearTimeout(this.clipboardMonitorTimerId); - this.clipboardMonitorTimerId = null; - } - } - isWanakanaEnabled() { return this.wanakanaEnable !== null && this.wanakanaEnable.checked; } diff --git a/ext/bg/search.html b/ext/bg/search.html index bb7ac095..bb555e92 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -87,6 +87,7 @@ + From 90a5d795708898fba940f77358269354e96355a7 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Jan 2020 03:31:31 +0200 Subject: [PATCH 08/24] use ClipboardMonitor in Backend --- ext/bg/background.html | 1 + ext/bg/js/api.js | 4 ++++ ext/bg/js/backend.js | 25 +++++++++++-------------- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/ext/bg/background.html b/ext/bg/background.html index e6d32593..11023221 100644 --- a/ext/bg/background.html +++ b/ext/bg/background.html @@ -26,6 +26,7 @@ + diff --git a/ext/bg/js/api.js b/ext/bg/js/api.js index 285b8016..f4be7a0c 100644 --- a/ext/bg/js/api.js +++ b/ext/bg/js/api.js @@ -29,6 +29,10 @@ function apiGetDisplayTemplatesHtml() { return _apiInvoke('getDisplayTemplatesHtml'); } +function apiClipboardGet() { + return _apiInvoke('clipboardGet'); +} + function _apiInvoke(action, params={}) { const data = {action, params}; return new Promise((resolve, reject) => { diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index d80e7713..fa1660c2 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -22,6 +22,7 @@ class Backend { this.translator = new Translator(); this.anki = new AnkiNull(); this.mecab = new Mecab(); + this.clipboardMonitor = new ClipboardMonitor(); this.options = null; this.optionsSchema = null; this.optionsContext = { @@ -33,10 +34,8 @@ class Backend { this.isPreparedPromise = new Promise((resolve) => (this.isPreparedResolve = resolve)); this.clipboardPasteTarget = document.querySelector('#clipboard-paste-target'); + this.popupWindow = null; - this.clipboardPopupTimerId = null; - this.clipboardInterval = 250; - this.clipboardPreviousText = null; this.apiForwarder = new BackendApiForwarder(); } @@ -71,6 +70,8 @@ class Backend { this.isPreparedResolve(); this.isPreparedResolve = null; this.isPreparedPromise = null; + + this.clipboardMonitor.onClipboardText = (text) => this._onClipboardText(text); } onOptionsUpdated(source) { @@ -101,6 +102,10 @@ class Backend { } } + _onClipboardText(text) { + this._onCommandSearch({mode: 'popup', query: text}); + } + _onZoomChange({tabId, oldZoomFactor, newZoomFactor}) { const callback = () => this.checkLastError(chrome.runtime.lastError); chrome.tabs.sendMessage(tabId, {action: 'zoomChanged', params: {oldZoomFactor, newZoomFactor}}, callback); @@ -126,18 +131,10 @@ class Backend { this.mecab.stopListener(); } - window.clearInterval(this.clipboardPopupTimerId); if (options.general.enableClipboardPopups) { - this.clipboardPopupTimerId = setInterval(() => { - this._onApiClipboardGet() - .then((result) => { - if (this.clipboardPreviousText === result) { - return; - } - this._onCommandSearch({mode: 'popup', query: result}); - this.clipboardPreviousText = result; - }); - }, this.clipboardInterval); + this.clipboardMonitor.start(); + } else { + this.clipboardMonitor.stop(); } } From 91682dd63334da7acf458a320fbd48bbf7dff766 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Jan 2020 04:16:02 +0200 Subject: [PATCH 09/24] add permission check to formRead --- ext/bg/js/settings/main.js | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/bg/js/settings/main.js b/ext/bg/js/settings/main.js index 55472a7b..cf0f08db 100644 --- a/ext/bg/js/settings/main.js +++ b/ext/bg/js/settings/main.js @@ -28,7 +28,22 @@ function getOptionsFullMutable() { async function formRead(options) { options.general.enable = $('#enable').prop('checked'); - options.general.enableClipboardPopups = $('#enable-clipboard-popups').prop('checked'); + const enableClipboardPopups = $('#enable-clipboard-popups').prop('checked'); + if (enableClipboardPopups) { + options.general.enableClipboardPopups = await new Promise((resolve, _reject) => { + chrome.permissions.request( + {permissions: ['clipboardRead']}, + (granted) => { + if (!granted) { + $('#enable-clipboard-popups').prop('checked', false); + } + resolve(granted); + } + ); + }); + } else { + options.general.enableClipboardPopups = false; + } options.general.showGuide = $('#show-usage-guide').prop('checked'); options.general.compactTags = $('#compact-tags').prop('checked'); options.general.compactGlossaries = $('#compact-glossaries').prop('checked'); From d7f0369281f8e9f036d87be392828e81d8187e74 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 2 Feb 2020 14:41:41 +0200 Subject: [PATCH 10/24] use Promise --- ext/bg/js/backend.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index fa1660c2..06010a95 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -630,10 +630,10 @@ class Backend { if (this.popupWindow !== null) { chrome.windows.remove(this.popupWindow.id); } - chrome.windows.create( + this.popupWindow = await new Promise((resolve) => chrome.windows.create( {url, width: popupWidth, height: popupHeight, type: 'popup'}, - (popupWindow) => { this.popupWindow = popupWindow; } - ); + resolve + )); return; } } From 8d56d6ffcb0f02e1edaf4193e182e95bfe7d22c5 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 2 Feb 2020 14:58:31 +0200 Subject: [PATCH 11/24] handle closing already closed popup --- ext/bg/js/backend.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 06010a95..a9f2385b 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -628,7 +628,8 @@ class Backend { return; case 'popup': if (this.popupWindow !== null) { - chrome.windows.remove(this.popupWindow.id); + const callback = () => this.checkLastError(chrome.runtime.lastError); + chrome.windows.remove(this.popupWindow.id, callback); } this.popupWindow = await new Promise((resolve) => chrome.windows.create( {url, width: popupWidth, height: popupHeight, type: 'popup'}, From c16c38638b4ce39f64add0d6e430a21e44d8ec01 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 2 Feb 2020 16:08:19 +0200 Subject: [PATCH 12/24] hide search input in native popups --- ext/bg/js/backend.js | 6 ++++-- ext/bg/js/search.js | 37 ++++++++++++++++++------------------- ext/bg/search.html | 34 ++++++++++++++++++---------------- ext/mixed/css/display.css | 4 ++++ 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index a9f2385b..bdb8a76a 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -603,8 +603,10 @@ class Backend { const {popupWidth, popupHeight} = options.general; const baseUrl = chrome.runtime.getURL('/bg/search.html'); - const queryString = (query && query.length > 0) ? `?query=${encodeURIComponent(query)}` : ''; - const url = baseUrl + queryString; + const queryParams = {mode}; + if (query && query.length > 0) { queryParams.query = query; } + const queryString = new URLSearchParams(queryParams).toString(); + const url = `${baseUrl}?${queryString}`; switch (mode) { case 'sameTab': diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index e32ba46e..ea4ab235 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -53,6 +53,8 @@ class DisplaySearch extends Display { this.search.addEventListener('click', (e) => this.onSearch(e), false); } if (this.query !== null) { + const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); + document.documentElement.dataset.searchMode = mode; this.query.addEventListener('input', () => this.onSearchInput(), false); if (this.wanakanaEnable !== null) { @@ -63,7 +65,6 @@ class DisplaySearch extends Display { this.wanakanaEnable.checked = false; } this.wanakanaEnable.addEventListener('change', (e) => { - const query = DisplaySearch.getSearchQueryFromLocation(window.location.href) || ''; if (e.target.checked) { window.wanakana.bind(this.query); this.setQuery(window.wanakana.toKana(query)); @@ -77,15 +78,12 @@ class DisplaySearch extends Display { }); } - const query = DisplaySearch.getSearchQueryFromLocation(window.location.href); - if (query !== null) { - if (this.isWanakanaEnabled()) { - this.setQuery(window.wanakana.toKana(query)); - } else { - this.setQuery(query); - } - this.onSearchQueryUpdated(this.query.value, false); + if (this.isWanakanaEnabled()) { + this.setQuery(window.wanakana.toKana(query)); + } else { + this.setQuery(query); } + this.onSearchQueryUpdated(this.query.value, false); } if (this.clipboardMonitorEnable !== null) { if (this.options.general.enableClipboardMonitor === true) { @@ -162,13 +160,12 @@ class DisplaySearch extends Display { } onPopState() { - const query = DisplaySearch.getSearchQueryFromLocation(window.location.href) || ''; - if (this.query !== null) { - if (this.isWanakanaEnabled()) { - this.setQuery(window.wanakana.toKana(query)); - } else { - this.setQuery(query); - } + const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); + document.documentElement.dataset.searchMode = mode; + if (this.isWanakanaEnabled()) { + this.setQuery(window.wanakana.toKana(query)); + } else { + this.setQuery(query); } this.onSearchQueryUpdated(this.query.value, false); @@ -334,9 +331,11 @@ class DisplaySearch extends Display { } } - static getSearchQueryFromLocation(url) { - const match = /^[^?#]*\?(?:[^&#]*&)?query=([^&#]*)/.exec(url); - return match !== null ? decodeURIComponent(match[1]) : null; + static parseQueryStringFromLocation(url) { + const parsedUrl = new URL(url); + const parsedSearch = new URLSearchParams(parsedUrl.search); + return Array.from(parsedSearch.entries()) + .reduce((a, [k, v]) => Object.assign({}, a, {[k]: v}), {}); } } diff --git a/ext/bg/search.html b/ext/bg/search.html index bb555e92..10e5aa8e 100644 --- a/ext/bg/search.html +++ b/ext/bg/search.html @@ -25,23 +25,25 @@

Search your installed dictionaries by entering a Japanese expression into the field below.

-
- - - - - - - - -
+
+
+ + + + + + + + +
-
- - - - -
+
+ + + + +
+
diff --git a/ext/mixed/css/display.css b/ext/mixed/css/display.css index 3a66cec3..62e62243 100644 --- a/ext/mixed/css/display.css +++ b/ext/mixed/css/display.css @@ -136,6 +136,10 @@ html:root[data-yomichan-page=float] .navigation-header:not([hidden])~.navigation margin-right: 0.2em; } +html:root[data-yomichan-page=search][data-search-mode=popup] .search-input { + display: none; +} + /* * Entries From 8a295c4bb06b88bb135a34a3f692ac631a713f50 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Wed, 5 Feb 2020 01:41:57 +0200 Subject: [PATCH 13/24] fix constant usage from wrong scope --- ext/bg/js/search.js | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index ea4ab235..34dac15b 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -65,6 +65,7 @@ class DisplaySearch extends Display { this.wanakanaEnable.checked = false; } this.wanakanaEnable.addEventListener('change', (e) => { + const {query=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); if (e.target.checked) { window.wanakana.bind(this.query); this.setQuery(window.wanakana.toKana(query)); From 722a2a4bce08ae2e69d7abb1c5d09842bd29ebfa Mon Sep 17 00:00:00 2001 From: siikamiika Date: Wed, 5 Feb 2020 13:26:25 +0200 Subject: [PATCH 14/24] disable internal clipboard monitor in native popup --- ext/bg/js/search.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 34dac15b..feadefe4 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -49,11 +49,12 @@ class DisplaySearch extends Display { try { await this.initialize(); + const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); + if (this.search !== null) { this.search.addEventListener('click', (e) => this.onSearch(e), false); } if (this.query !== null) { - const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); document.documentElement.dataset.searchMode = mode; this.query.addEventListener('input', () => this.onSearchInput(), false); @@ -86,7 +87,7 @@ class DisplaySearch extends Display { } this.onSearchQueryUpdated(this.query.value, false); } - if (this.clipboardMonitorEnable !== null) { + if (this.clipboardMonitorEnable !== null && mode !== 'popup') { if (this.options.general.enableClipboardMonitor === true) { this.clipboardMonitorEnable.checked = true; this.clipboardMonitor.start(); From 9fbd47e4eaeee2f1121cdeae08dbc5439b247498 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 9 Feb 2020 21:06:59 +0200 Subject: [PATCH 15/24] rename sameTab --> existingOrNewTab --- ext/bg/js/backend.js | 4 ++-- ext/bg/js/context.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index bdb8a76a..f94463aa 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -597,7 +597,7 @@ class Backend { // Command handlers async _onCommandSearch(params) { - const {mode, query} = params || {mode: 'sameTab'}; + const {mode, query} = params || {mode: 'existingOrNewTab'}; const options = await this.getOptions(this.optionsContext); const {popupWidth, popupHeight} = options.general; @@ -609,7 +609,7 @@ class Backend { const url = `${baseUrl}?${queryString}`; switch (mode) { - case 'sameTab': + case 'existingOrNewTab': try { const tab = await Backend._findTab(1000, (url2) => ( url2 !== null && diff --git a/ext/bg/js/context.js b/ext/bg/js/context.js index a0e5adc9..37adb6b7 100644 --- a/ext/bg/js/context.js +++ b/ext/bg/js/context.js @@ -30,7 +30,7 @@ function setupButtonEvents(selector, command, url) { for (const node of nodes) { node.addEventListener('click', (e) => { if (e.button !== 0) { return; } - apiCommandExec(command, {mode: e.ctrlKey ? 'newTab' : 'sameTab'}); + apiCommandExec(command, {mode: e.ctrlKey ? 'newTab' : 'existingOrNewTab'}); e.preventDefault(); }, false); node.addEventListener('auxclick', (e) => { From 1797edc7d88f63872c3a593179d9dd5c7b3c0b47 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 9 Feb 2020 21:11:35 +0200 Subject: [PATCH 16/24] check chrome.windows support before using --- ext/bg/js/backend.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index f94463aa..6d23b695 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -629,6 +629,10 @@ class Backend { chrome.tabs.create({url}); return; case 'popup': + if (!isObject(chrome.windows)) { + // chrome.windows not supported (e.g. on Firefox mobile) + return; + } if (this.popupWindow !== null) { const callback = () => this.checkLastError(chrome.runtime.lastError); chrome.windows.remove(this.popupWindow.id, callback); From 4e59c2d55684b5a0b1d9edc580dd4c43bfc46211 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 9 Feb 2020 21:28:40 +0200 Subject: [PATCH 17/24] hide native popup option for firefox mobile --- ext/bg/css/settings.css | 14 ++++++++++++++ ext/bg/settings.html | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ext/bg/css/settings.css b/ext/bg/css/settings.css index 815a88fa..d686e8f8 100644 --- a/ext/bg/css/settings.css +++ b/ext/bg/css/settings.css @@ -222,6 +222,20 @@ html:root[data-operating-system=openbsd] [data-show-for-operating-system~=openbs display: initial; } +html:root[data-browser=edge] [data-hide-for-browser~=edge], +html:root[data-browser=chrome] [data-hide-for-browser~=chrome], +html:root[data-browser=firefox] [data-hide-for-browser~=firefox], +html:root[data-browser=firefox-mobile] [data-hide-for-browser~=firefox-mobile], +html:root[data-operating-system=mac] [data-hide-for-operating-system~=mac], +html:root[data-operating-system=win] [data-hide-for-operating-system~=win], +html:root[data-operating-system=android] [data-hide-for-operating-system~=android], +html:root[data-operating-system=cros] [data-hide-for-operating-system~=cros], +html:root[data-operating-system=linux] [data-hide-for-operating-system~=linux], +html:root[data-operating-system=openbsd] [data-hide-for-operating-system~=openbsd] { + display: none; +} + + @media screen and (max-width: 740px) { .col-xs-6 { float: none; diff --git a/ext/bg/settings.html b/ext/bg/settings.html index b0fcec2b..57616873 100644 --- a/ext/bg/settings.html +++ b/ext/bg/settings.html @@ -134,7 +134,7 @@ -
+
From 21bad6c6e380c9c0dbd03f82563a1570bf22963c Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 9 Feb 2020 21:47:11 +0200 Subject: [PATCH 18/24] simplify setQuery kana conversion --- ext/bg/js/search.js | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index feadefe4..1baee904 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -69,22 +69,17 @@ class DisplaySearch extends Display { const {query=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); if (e.target.checked) { window.wanakana.bind(this.query); - this.setQuery(window.wanakana.toKana(query)); apiOptionsSet({general: {enableWanakana: true}}, this.getOptionsContext()); } else { window.wanakana.unbind(this.query); - this.setQuery(query); apiOptionsSet({general: {enableWanakana: false}}, this.getOptionsContext()); } + this.setQuery(query); this.onSearchQueryUpdated(this.query.value, false); }); } - if (this.isWanakanaEnabled()) { - this.setQuery(window.wanakana.toKana(query)); - } else { - this.setQuery(query); - } + this.setQuery(query); this.onSearchQueryUpdated(this.query.value, false); } if (this.clipboardMonitorEnable !== null && mode !== 'popup') { @@ -164,12 +159,7 @@ class DisplaySearch extends Display { onPopState() { const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); document.documentElement.dataset.searchMode = mode; - if (this.isWanakanaEnabled()) { - this.setQuery(window.wanakana.toKana(query)); - } else { - this.setQuery(query); - } - + this.setQuery(query); this.onSearchQueryUpdated(this.query.value, false); } @@ -203,7 +193,7 @@ class DisplaySearch extends Display { } onClipboardText(text) { - this.setQuery(this.isWanakanaEnabled() ? window.wanakana.toKana(text) : text); + this.setQuery(text); window.history.pushState(null, '', `${window.location.pathname}?query=${encodeURIComponent(text)}`); this.onSearchQueryUpdated(this.query.value, true); } @@ -256,8 +246,9 @@ class DisplaySearch extends Display { } setQuery(query) { - this.query.value = query; - this.queryParser.setText(query); + const interpretedQuery = this.isWanakanaEnabled() ? window.wanakana.toKana(query) : query; + this.query.value = interpretedQuery; + this.queryParser.setText(interpretedQuery); } setIntroVisible(visible, animate) { From d4e74a05723b1b4347c51606bcee61dc72e34e51 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 9 Feb 2020 22:16:52 +0200 Subject: [PATCH 19/24] fix existing tab focus --- ext/bg/js/backend.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 6d23b695..9eb1d9ca 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -597,7 +597,7 @@ class Backend { // Command handlers async _onCommandSearch(params) { - const {mode, query} = params || {mode: 'existingOrNewTab'}; + const {mode='existingOrNewTab', query} = params || {}; const options = await this.getOptions(this.optionsContext); const {popupWidth, popupHeight} = options.general; @@ -613,11 +613,14 @@ class Backend { try { const tab = await Backend._findTab(1000, (url2) => ( url2 !== null && - url2.startsWith(url) && - (url2.length === url.length || url2[url.length] === '?' || url2[url.length] === '#') + url2.startsWith(baseUrl) && + (url2.length === baseUrl.length || url2[baseUrl.length] === '?' || url2[baseUrl.length] === '#') )); if (tab !== null) { await Backend._focusTab(tab); + if (queryParams.query) { + await new Promise((resolve) => chrome.tabs.update(tab.id, {url}, resolve)); + } return; } } catch (e) { From 4508efb9a695a97f19cf99ccb6155c55f9be5f0d Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 9 Feb 2020 22:41:20 +0200 Subject: [PATCH 20/24] stop clipboard monitor before starting it again --- ext/bg/js/clipboard-monitor.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/bg/js/clipboard-monitor.js b/ext/bg/js/clipboard-monitor.js index 579cdc56..b4a27fa2 100644 --- a/ext/bg/js/clipboard-monitor.js +++ b/ext/bg/js/clipboard-monitor.js @@ -30,6 +30,8 @@ class ClipboardMonitor { } start() { + this.stop(); + // The token below is used as a unique identifier to ensure that a new clipboard monitor // hasn't been started during the await call. The check below the await apiClipboardGet() // call will exit early if the reference has changed. From 56f1f8384dba7da6f1373768129bd37c24147520 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Mon, 10 Feb 2020 00:09:29 +0200 Subject: [PATCH 21/24] use parseUrl in Backend --- ext/bg/js/backend.js | 12 +++++++----- ext/bg/js/search.js | 13 +++---------- ext/mixed/js/core.js | 8 ++++++++ 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 9eb1d9ca..9565a8d9 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -608,14 +608,16 @@ class Backend { const queryString = new URLSearchParams(queryParams).toString(); const url = `${baseUrl}?${queryString}`; + const isTabMatch = (url2) => { + if (url2 === null || !url2.startsWith(baseUrl)) { return false; } + const {baseUrl: baseUrl2, queryParams: queryParams2} = parseUrl(url2); + return baseUrl2 === baseUrl && (queryParams2.mode === mode || (!queryParams2.mode && mode === 'existingOrNewTab')); + }; + switch (mode) { case 'existingOrNewTab': try { - const tab = await Backend._findTab(1000, (url2) => ( - url2 !== null && - url2.startsWith(baseUrl) && - (url2.length === baseUrl.length || url2[baseUrl.length] === '?' || url2[baseUrl.length] === '#') - )); + const tab = await Backend._findTab(1000, isTabMatch); if (tab !== null) { await Backend._focusTab(tab); if (queryParams.query) { diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 1baee904..b6a1e66a 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -49,7 +49,7 @@ class DisplaySearch extends Display { try { await this.initialize(); - const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); + const {queryParams: {query='', mode=''}} = parseUrl(window.location.href); if (this.search !== null) { this.search.addEventListener('click', (e) => this.onSearch(e), false); @@ -66,7 +66,7 @@ class DisplaySearch extends Display { this.wanakanaEnable.checked = false; } this.wanakanaEnable.addEventListener('change', (e) => { - const {query=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); + const {queryParams: {query=''}} = parseUrl(window.location.href); if (e.target.checked) { window.wanakana.bind(this.query); apiOptionsSet({general: {enableWanakana: true}}, this.getOptionsContext()); @@ -157,7 +157,7 @@ class DisplaySearch extends Display { } onPopState() { - const {query='', mode=''} = DisplaySearch.parseQueryStringFromLocation(window.location.href); + const {queryParams: {query='', mode=''}} = parseUrl(window.location.href); document.documentElement.dataset.searchMode = mode; this.setQuery(query); this.onSearchQueryUpdated(this.query.value, false); @@ -323,13 +323,6 @@ class DisplaySearch extends Display { document.title = `${text} - Yomichan Search`; } } - - static parseQueryStringFromLocation(url) { - const parsedUrl = new URL(url); - const parsedSearch = new URLSearchParams(parsedUrl.search); - return Array.from(parsedSearch.entries()) - .reduce((a, [k, v]) => Object.assign({}, a, {[k]: v}), {}); - } } DisplaySearch.onKeyDownIgnoreKeys = { diff --git a/ext/mixed/js/core.js b/ext/mixed/js/core.js index 0142d594..ca9e98e5 100644 --- a/ext/mixed/js/core.js +++ b/ext/mixed/js/core.js @@ -128,6 +128,14 @@ function stringReverse(string) { return string.split('').reverse().join('').replace(/([\uDC00-\uDFFF])([\uD800-\uDBFF])/g, '$2$1'); } +function parseUrl(url) { + const parsedUrl = new URL(url); + const baseUrl = `${parsedUrl.origin}${parsedUrl.pathname}`; + const queryParams = Array.from(parsedUrl.searchParams.entries()) + .reduce((a, [k, v]) => Object.assign({}, a, {[k]: v}), {}); + return {baseUrl, queryParams}; +} + /* * Async utilities From 89729d8c20e8d1113e640a46f448a9734da1fe56 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Mon, 10 Feb 2020 00:39:05 +0200 Subject: [PATCH 22/24] reuse existing popup window --- ext/bg/js/backend.js | 45 ++++++++++++++++++++++++++------------------ 1 file changed, 27 insertions(+), 18 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 9565a8d9..adfb4f10 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -614,17 +614,21 @@ class Backend { return baseUrl2 === baseUrl && (queryParams2.mode === mode || (!queryParams2.mode && mode === 'existingOrNewTab')); }; + const openInTab = async () => { + const tab = await Backend._findTab(1000, isTabMatch); + if (tab !== null) { + await Backend._focusTab(tab); + if (queryParams.query) { + await new Promise((resolve) => chrome.tabs.update(tab.id, {url}, resolve)); + } + return true; + } + }; + switch (mode) { case 'existingOrNewTab': try { - const tab = await Backend._findTab(1000, isTabMatch); - if (tab !== null) { - await Backend._focusTab(tab); - if (queryParams.query) { - await new Promise((resolve) => chrome.tabs.update(tab.id, {url}, resolve)); - } - return; - } + if (await openInTab()) { return; } } catch (e) { // NOP } @@ -634,18 +638,23 @@ class Backend { chrome.tabs.create({url}); return; case 'popup': - if (!isObject(chrome.windows)) { + try { // chrome.windows not supported (e.g. on Firefox mobile) - return; + if (!isObject(chrome.windows)) { return; } + if (await openInTab()) { return; } + // if the previous popup is open in an invalid state, close it + if (this.popupWindow !== null) { + const callback = () => this.checkLastError(chrome.runtime.lastError); + chrome.windows.remove(this.popupWindow.id, callback); + } + // open new popup + this.popupWindow = await new Promise((resolve) => chrome.windows.create( + {url, width: popupWidth, height: popupHeight, type: 'popup'}, + resolve + )); + } catch (e) { + // NOP } - if (this.popupWindow !== null) { - const callback = () => this.checkLastError(chrome.runtime.lastError); - chrome.windows.remove(this.popupWindow.id, callback); - } - this.popupWindow = await new Promise((resolve) => chrome.windows.create( - {url, width: popupWidth, height: popupHeight, type: 'popup'}, - resolve - )); return; } } From 460d306f60fa745368c8249e4bc4bdb0d0448f25 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Mon, 10 Feb 2020 01:16:06 +0200 Subject: [PATCH 23/24] update popup search with chrome.tabs.sendMessage --- ext/bg/js/backend.js | 4 +++- ext/bg/js/search.js | 23 ++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index adfb4f10..668d1fb7 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -619,7 +619,9 @@ class Backend { if (tab !== null) { await Backend._focusTab(tab); if (queryParams.query) { - await new Promise((resolve) => chrome.tabs.update(tab.id, {url}, resolve)); + await new Promise((resolve) => chrome.tabs.sendMessage( + tab.id, {action: 'searchQueryUpdate', params: {query: queryParams.query}}, resolve + )); } return true; } diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index b6a1e66a..37c96934 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -109,10 +109,12 @@ class DisplaySearch extends Display { }); } + chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); + window.addEventListener('popstate', (e) => this.onPopState(e)); window.addEventListener('copy', (e) => this.onCopy(e)); - this.clipboardMonitor.onClipboardText = (text) => this.onClipboardText(text); + this.clipboardMonitor.onClipboardText = (text) => this.onExternalSearchUpdate(text); this.updateSearchButton(); } catch (e) { @@ -163,6 +165,15 @@ class DisplaySearch extends Display { this.onSearchQueryUpdated(this.query.value, false); } + onRuntimeMessage({action, params}, sender, callback) { + const handler = DisplaySearch._runtimeMessageHandlers.get(action); + if (typeof handler !== 'function') { return false; } + + const result = handler(this, params, sender); + callback(result); + return false; + } + onKeyDown(e) { const key = Display.getKeyFromEvent(e); const ignoreKeys = DisplaySearch.onKeyDownIgnoreKeys; @@ -192,9 +203,11 @@ class DisplaySearch extends Display { this.clipboardMonitor.setPreviousText(document.getSelection().toString().trim()); } - onClipboardText(text) { + onExternalSearchUpdate(text) { this.setQuery(text); - window.history.pushState(null, '', `${window.location.pathname}?query=${encodeURIComponent(text)}`); + const url = new URL(window.location.href); + url.searchParams.set('query', text); + window.history.pushState(null, '', url.toString()); this.onSearchQueryUpdated(this.query.value, true); } @@ -340,4 +353,8 @@ DisplaySearch.onKeyDownIgnoreKeys = { 'Shift': [] }; +DisplaySearch._runtimeMessageHandlers = new Map([ + ['searchQueryUpdate', (self, {query}) => { self.onExternalSearchUpdate(query); }] +]); + DisplaySearch.instance = DisplaySearch.create(); From 14b9f4a82781b9a5044e22437f6b0b02af67a120 Mon Sep 17 00:00:00 2001 From: siikamiika Date: Mon, 10 Feb 2020 01:31:47 +0200 Subject: [PATCH 24/24] preserve search page mode on manual search --- ext/bg/js/search.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 37c96934..4da27513 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -152,9 +152,13 @@ class DisplaySearch extends Display { e.preventDefault(); const query = this.query.value; + this.queryParser.setText(query); - const queryString = query.length > 0 ? `?query=${encodeURIComponent(query)}` : ''; - window.history.pushState(null, '', `${window.location.pathname}${queryString}`); + + const url = new URL(window.location.href); + url.searchParams.set('query', query); + window.history.pushState(null, '', url.toString()); + this.onSearchQueryUpdated(query, true); }