From 7c578f75827ae0a72b13f04b8342fd8129f702d4 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sun, 19 Apr 2020 14:28:29 -0400 Subject: Create popup-preview-frame-main.js --- ext/bg/js/settings/popup-preview-frame.js | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'ext/bg/js/settings/popup-preview-frame.js') diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index fba114e2..420a7c5a 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -41,12 +41,6 @@ class SettingsPopupPreview { ]); } - static create() { - const instance = new SettingsPopupPreview(); - instance.prepare(); - return instance; - } - async prepare() { // Setup events window.addEventListener('message', this.onMessage.bind(this), false); @@ -178,8 +172,3 @@ class SettingsPopupPreview { this.setInfoVisible(!this.popupShown); } } - -SettingsPopupPreview.instance = SettingsPopupPreview.create(); - - - -- cgit v1.2.3 From ca033a87a0d302151b430acfdf9d480514c14aed Mon Sep 17 00:00:00 2001 From: siikamiika Date: Sun, 26 Apr 2020 22:33:50 +0300 Subject: Update Popup and DisplayFloat optionsContext from Frontend (#464) * set optionsContext from Frontend * update Popup+Display options on Frontend change * remove popup setOptions * only update DisplayFloat options from Frontend * fix optionsContext usage * fix preview frame arguments * keep Frontend URL up to date * cache url * fix preview frame * trigger modifyingProfileChange in correct places * remove async from function not using await * refactor optionsContext in Frontend --- ext/bg/js/search.js | 4 +- ext/bg/js/settings/popup-preview-frame-main.js | 5 +-- ext/bg/js/settings/popup-preview-frame.js | 22 ++++++++--- ext/bg/js/settings/popup-preview.js | 18 +++++++++ ext/bg/js/settings/profiles.js | 10 +++++ ext/fg/js/content-script-main.js | 31 +++++++++++---- ext/fg/js/float.js | 23 ++++++----- ext/fg/js/frontend.js | 55 ++++++++++++++------------ ext/fg/js/popup-proxy-host.js | 17 +++++--- ext/fg/js/popup-proxy.js | 19 +++++---- ext/fg/js/popup.js | 27 ++++++++----- ext/mixed/js/display.js | 2 - 12 files changed, 154 insertions(+), 79 deletions(-) (limited to 'ext/bg/js/settings/popup-preview-frame.js') diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index 684ea6d3..a5484fc3 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -76,6 +76,8 @@ class DisplaySearch extends Display { async prepare() { try { await super.prepare(); + await this.updateOptions(); + yomichan.on('optionsUpdated', () => this.updateOptions()); await this.queryParser.prepare(); const {queryParams: {query='', mode=''}} = parseUrl(window.location.href); @@ -231,7 +233,7 @@ class DisplaySearch extends Display { this.setIntroVisible(!valid, animate); this.updateSearchButton(); if (valid) { - const {definitions} = await apiTermsFind(query, details, this.optionsContext); + const {definitions} = await apiTermsFind(query, details, this.getOptionsContext()); this.setContent('terms', {definitions, context: { focus: false, disableHistory: true, diff --git a/ext/bg/js/settings/popup-preview-frame-main.js b/ext/bg/js/settings/popup-preview-frame-main.js index 86c2814c..2ab6af6b 100644 --- a/ext/bg/js/settings/popup-preview-frame-main.js +++ b/ext/bg/js/settings/popup-preview-frame-main.js @@ -19,7 +19,6 @@ * SettingsPopupPreview */ -(async () => { - const instance = new SettingsPopupPreview(); - await instance.prepare(); +(() => { + new SettingsPopupPreview(); })(); diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index 420a7c5a..05a2a41b 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -32,19 +32,24 @@ class SettingsPopupPreview { this.popupShown = false; this.themeChangeTimeout = null; this.textSource = null; + this.optionsContext = null; this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); this._windowMessageHandlers = new Map([ + ['prepare', ({optionsContext}) => this.prepare(optionsContext)], ['setText', ({text}) => this.setText(text)], ['setCustomCss', ({css}) => this.setCustomCss(css)], - ['setCustomOuterCss', ({css}) => this.setCustomOuterCss(css)] + ['setCustomOuterCss', ({css}) => this.setCustomOuterCss(css)], + ['updateOptionsContext', ({optionsContext}) => this.updateOptionsContext(optionsContext)] ]); - } - async prepare() { - // Setup events window.addEventListener('message', this.onMessage.bind(this), false); + } + + async prepare(optionsContext) { + this.optionsContext = optionsContext; + // Setup events document.querySelector('#theme-dark-checkbox').addEventListener('change', this.onThemeDarkCheckboxChanged.bind(this), false); // Overwrite API functions @@ -62,8 +67,9 @@ class SettingsPopupPreview { this.frontend = new Frontend(this.popup); + this.frontend.getOptionsContext = async () => this.optionsContext; this.frontend.setEnabled = () => {}; - this.frontend.searchClear = () => {}; + this.frontend.onSearchClear = () => {}; await this.frontend.prepare(); @@ -145,6 +151,12 @@ class SettingsPopupPreview { this.frontend.popup.setCustomOuterCss(css, false); } + async updateOptionsContext(optionsContext) { + this.optionsContext = optionsContext; + await this.frontend.updateOptions(); + await this.updateSearch(); + } + async updateSearch() { const exampleText = document.querySelector('#example-text'); if (exampleText === null) { return; } diff --git a/ext/bg/js/settings/popup-preview.js b/ext/bg/js/settings/popup-preview.js index 45bf531e..fdc3dd94 100644 --- a/ext/bg/js/settings/popup-preview.js +++ b/ext/bg/js/settings/popup-preview.js @@ -16,6 +16,7 @@ */ /* global + * getOptionsContext * wanakana */ @@ -60,6 +61,23 @@ function showAppearancePreview() { frame.contentWindow.postMessage({action, params}, targetOrigin); }); + const updateOptionsContext = () => { + const action = 'updateOptionsContext'; + const params = { + optionsContext: getOptionsContext() + }; + frame.contentWindow.postMessage({action, params}, targetOrigin); + }; + yomichan.on('modifyingProfileChange', updateOptionsContext); + + frame.addEventListener('load', () => { + const action = 'prepare'; + const params = { + optionsContext: getOptionsContext() + }; + frame.contentWindow.postMessage({action, params}, targetOrigin); + }); + container.append(frame); buttonContainer.remove(); settings.css('display', ''); diff --git a/ext/bg/js/settings/profiles.js b/ext/bg/js/settings/profiles.js index 867b17aa..3f4b1da7 100644 --- a/ext/bg/js/settings/profiles.js +++ b/ext/bg/js/settings/profiles.js @@ -190,6 +190,8 @@ async function onTargetProfileChanged() { currentProfileIndex = index; await profileOptionsUpdateTarget(optionsFull); + + yomichan.trigger('modifyingProfileChange'); } async function onProfileAdd() { @@ -197,9 +199,13 @@ async function onProfileAdd() { const profile = utilBackgroundIsolate(optionsFull.profiles[currentProfileIndex]); profile.name = profileOptionsCreateCopyName(profile.name, optionsFull.profiles, 100); optionsFull.profiles.push(profile); + currentProfileIndex = optionsFull.profiles.length - 1; + await profileOptionsUpdateTarget(optionsFull); await settingsSaveOptions(); + + yomichan.trigger('modifyingProfileChange'); } async function onProfileRemove(e) { @@ -238,6 +244,8 @@ async function onProfileRemoveConfirm() { await profileOptionsUpdateTarget(optionsFull); await settingsSaveOptions(); + + yomichan.trigger('modifyingProfileChange'); } function onProfileNameChanged() { @@ -263,6 +271,8 @@ async function onProfileMove(offset) { await profileOptionsUpdateTarget(optionsFull); await settingsSaveOptions(); + + yomichan.trigger('modifyingProfileChange'); } async function onProfileCopy() { diff --git a/ext/fg/js/content-script-main.js b/ext/fg/js/content-script-main.js index 14285536..0b852644 100644 --- a/ext/fg/js/content-script-main.js +++ b/ext/fg/js/content-script-main.js @@ -25,7 +25,7 @@ * apiOptionsGet */ -async function createIframePopupProxy(url, frameOffsetForwarder, setDisabled) { +async function createIframePopupProxy(frameOffsetForwarder, setDisabled) { const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( chrome.runtime.onMessage, ({action, params}, {resolve}) => { @@ -39,7 +39,7 @@ async function createIframePopupProxy(url, frameOffsetForwarder, setDisabled) { const getFrameOffset = frameOffsetForwarder.getOffset.bind(frameOffsetForwarder); - const popup = new PopupProxy(popupId, 0, null, frameId, url, getFrameOffset, setDisabled); + const popup = new PopupProxy(popupId, 0, null, frameId, getFrameOffset, setDisabled); await popup.prepare(); return popup; @@ -54,8 +54,8 @@ async function getOrCreatePopup(depth) { return popup; } -async function createPopupProxy(depth, id, parentFrameId, url) { - const popup = new PopupProxy(null, depth + 1, id, parentFrameId, url); +async function createPopupProxy(depth, id, parentFrameId) { + const popup = new PopupProxy(null, depth + 1, id, parentFrameId); await popup.prepare(); return popup; @@ -86,8 +86,22 @@ async function createPopupProxy(depth, id, parentFrameId, url) { applyOptions(); }; + let urlUpdatedAt = 0; + let proxyHostUrlCached = url; + const getProxyHostUrl = async () => { + const now = Date.now(); + if (popups.proxy !== null && now - urlUpdatedAt > 500) { + proxyHostUrlCached = await popups.proxy.getHostUrl(); + urlUpdatedAt = now; + } + return proxyHostUrlCached; + }; + const applyOptions = async () => { - const optionsContext = {depth: isSearchPage ? 0 : depth, url}; + const optionsContext = { + depth: isSearchPage ? 0 : depth, + url: proxy ? await getProxyHostUrl() : window.location.href + }; const options = await apiOptionsGet(optionsContext); if (!proxy && frameOffsetForwarder === null) { @@ -97,10 +111,10 @@ async function createPopupProxy(depth, id, parentFrameId, url) { let popup; if (isIframe && options.general.showIframePopupsInRootFrame && DOM.getFullscreenElement() === null && iframePopupsInRootFrameAvailable) { - popup = popups.iframe || await createIframePopupProxy(url, frameOffsetForwarder, disableIframePopupsInRootFrame); + popup = popups.iframe || await createIframePopupProxy(frameOffsetForwarder, disableIframePopupsInRootFrame); popups.iframe = popup; } else if (proxy) { - popup = popups.proxy || await createPopupProxy(depth, id, parentFrameId, url); + popup = popups.proxy || await createPopupProxy(depth, id, parentFrameId); popups.proxy = popup; } else { popup = popups.normal || await getOrCreatePopup(depth); @@ -108,7 +122,8 @@ async function createPopupProxy(depth, id, parentFrameId, url) { } if (frontend === null) { - frontend = new Frontend(popup); + const getUrl = proxy ? getProxyHostUrl : null; + frontend = new Frontend(popup, getUrl); frontendPreparePromise = frontend.prepare(); await frontendPreparePromise; } else { diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index 18d15a72..2a5eba83 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -29,11 +29,6 @@ class DisplayFloat extends Display { this._popupId = null; - this.optionsContext = { - depth: 0, - url: window.location.href - }; - this._orphaned = false; this._prepareInvoked = false; this._messageToken = null; @@ -51,10 +46,11 @@ class DisplayFloat extends Display { ]); this._windowMessageHandlers = new Map([ + ['setOptionsContext', ({optionsContext}) => this.setOptionsContext(optionsContext)], ['setContent', ({type, details}) => this.setContent(type, details)], ['clearAutoPlayTimer', () => this.clearAutoPlayTimer()], ['setCustomCss', ({css}) => this.setCustomCss(css)], - ['prepare', ({popupInfo, url, childrenSupported, scale}) => this.prepare(popupInfo, url, childrenSupported, scale)], + ['prepare', ({popupInfo, optionsContext, childrenSupported, scale}) => this.prepare(popupInfo, optionsContext, childrenSupported, scale)], ['setContentScale', ({scale}) => this.setContentScale(scale)] ]); @@ -62,18 +58,20 @@ class DisplayFloat extends Display { window.addEventListener('message', this.onMessage.bind(this), false); } - async prepare(popupInfo, url, childrenSupported, scale) { + async prepare(popupInfo, optionsContext, childrenSupported, scale) { if (this._prepareInvoked) { return; } this._prepareInvoked = true; - const {id, depth, parentFrameId} = popupInfo; + const {id, parentFrameId} = popupInfo; this._popupId = id; - this.optionsContext.depth = depth; - this.optionsContext.url = url; + + this.optionsContext = optionsContext; await super.prepare(); + await this.updateOptions(); if (childrenSupported) { + const {depth, url} = optionsContext; popupNestedInitialize(id, depth, parentFrameId, url); } @@ -158,6 +156,11 @@ class DisplayFloat extends Display { } } + async setOptionsContext(optionsContext) { + this.optionsContext = optionsContext; + await this.updateOptions(); + } + setContentScale(scale) { document.body.style.fontSize = `${scale}em`; } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index eecfe2e1..46921d36 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -26,24 +26,23 @@ */ class Frontend extends TextScanner { - constructor(popup) { + constructor(popup, getUrl=null) { super( window, () => this.popup.isProxy() ? [] : [this.popup.getContainer()], [(x, y) => this.popup.containsPoint(x, y)] ); + this._id = yomichan.generateId(16); + this.popup = popup; + this._getUrl = getUrl; + this._disabledOverride = false; this.options = null; - this.optionsContext = { - depth: popup.depth, - url: popup.url - }; - this._pageZoomFactor = 1.0; this._contentScale = 1.0; this._orphaned = false; @@ -143,11 +142,12 @@ class Frontend extends TextScanner { async setPopup(popup) { this.onSearchClear(false); this.popup = popup; - await popup.setOptions(this.options); + await popup.setOptionsContext(await this.getOptionsContext(), this._id); } async updateOptions() { - this.options = await apiOptionsGet(this.getOptionsContext()); + const optionsContext = await this.getOptionsContext(); + this.options = await apiOptionsGet(optionsContext); this.setOptions(this.options, this._canEnable()); const ignoreNodes = ['.scan-disable', '.scan-disable *']; @@ -156,7 +156,7 @@ class Frontend extends TextScanner { } this.ignoreNodes = ignoreNodes.join(','); - await this.popup.setOptions(this.options); + await this.popup.setOptionsContext(optionsContext, this._id); this._updateContentScale(); @@ -170,19 +170,20 @@ class Frontend extends TextScanner { try { if (textSource !== null) { + const optionsContext = await this.getOptionsContext(); results = ( - await this.findTerms(textSource) || - await this.findKanji(textSource) + await this.findTerms(textSource, optionsContext) || + await this.findKanji(textSource, optionsContext) ); if (results !== null) { const focus = (cause === 'mouse'); - this.showContent(textSource, focus, results.definitions, results.type); + this.showContent(textSource, focus, results.definitions, results.type, optionsContext); } } } catch (e) { if (this._orphaned) { if (textSource !== null && this.options.scanning.modifier !== 'none') { - this._showPopupContent(textSource, 'orphaned'); + this._showPopupContent(textSource, await this.getOptionsContext(), 'orphaned'); } } else { this.onError(e); @@ -196,11 +197,12 @@ class Frontend extends TextScanner { return results; } - showContent(textSource, focus, definitions, type) { + showContent(textSource, focus, definitions, type, optionsContext) { + const {url} = optionsContext; const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); - const url = window.location.href; this._showPopupContent( textSource, + optionsContext, type, {definitions, context: {sentence, url, focus, disableHistory: true}} ); @@ -210,13 +212,13 @@ class Frontend extends TextScanner { return this._lastShowPromise; } - async findTerms(textSource) { + async findTerms(textSource, optionsContext) { this.setTextSourceScanLength(textSource, this.options.scanning.length); const searchText = textSource.text(); if (searchText.length === 0) { return null; } - const {definitions, length} = await apiTermsFind(searchText, {}, this.getOptionsContext()); + const {definitions, length} = await apiTermsFind(searchText, {}, optionsContext); if (definitions.length === 0) { return null; } textSource.setEndOffset(length); @@ -224,13 +226,13 @@ class Frontend extends TextScanner { return {definitions, type: 'terms'}; } - async findKanji(textSource) { + async findKanji(textSource, optionsContext) { this.setTextSourceScanLength(textSource, 1); const searchText = textSource.text(); if (searchText.length === 0) { return null; } - const definitions = await apiKanjiFind(searchText, this.getOptionsContext()); + const definitions = await apiKanjiFind(searchText, optionsContext); if (definitions.length === 0) { return null; } return {definitions, type: 'kanji'}; @@ -242,17 +244,20 @@ class Frontend extends TextScanner { super.onSearchClear(changeFocus); } - getOptionsContext() { - this.optionsContext.url = this.popup.url; - return this.optionsContext; + async getOptionsContext() { + const url = this._getUrl !== null ? await this._getUrl() : window.location.href; + const depth = this.popup.depth; + return {depth, url}; } - _showPopupContent(textSource, type=null, details=null) { + _showPopupContent(textSource, optionsContext, type=null, details=null) { + const context = {optionsContext, source: this._id}; this._lastShowPromise = this.popup.showContent( textSource.getRect(), textSource.getWritingMode(), type, - details + details, + context ); return this._lastShowPromise; } @@ -294,7 +299,7 @@ class Frontend extends TextScanner { async _updatePopupPosition() { const textSource = this.getCurrentTextSource(); if (textSource !== null && await this.popup.isVisible()) { - this._showPopupContent(textSource); + this._showPopupContent(textSource, await this.getOptionsContext()); } } diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js index 958462ff..d87553e6 100644 --- a/ext/fg/js/popup-proxy-host.js +++ b/ext/fg/js/popup-proxy-host.js @@ -37,7 +37,7 @@ class PopupProxyHost { this._apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${this._frameId}`, new Map([ ['getOrCreatePopup', this._onApiGetOrCreatePopup.bind(this)], - ['setOptions', this._onApiSetOptions.bind(this)], + ['setOptionsContext', this._onApiSetOptionsContext.bind(this)], ['hide', this._onApiHide.bind(this)], ['isVisible', this._onApiIsVisibleAsync.bind(this)], ['setVisibleOverride', this._onApiSetVisibleOverride.bind(this)], @@ -45,7 +45,8 @@ class PopupProxyHost { ['showContent', this._onApiShowContent.bind(this)], ['setCustomCss', this._onApiSetCustomCss.bind(this)], ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], - ['setContentScale', this._onApiSetContentScale.bind(this)] + ['setContentScale', this._onApiSetContentScale.bind(this)], + ['getHostUrl', this._onApiGetHostUrl.bind(this)] ])); } @@ -103,9 +104,9 @@ class PopupProxyHost { }; } - async _onApiSetOptions({id, options}) { + async _onApiSetOptionsContext({id, optionsContext, source}) { const popup = this._getPopup(id); - return await popup.setOptions(options); + return await popup.setOptionsContext(optionsContext, source); } async _onApiHide({id, changeFocus}) { @@ -129,11 +130,11 @@ class PopupProxyHost { return await popup.containsPoint(x, y); } - async _onApiShowContent({id, elementRect, writingMode, type, details}) { + async _onApiShowContent({id, elementRect, writingMode, type, details, context}) { const popup = this._getPopup(id); elementRect = PopupProxyHost._convertJsonRectToDOMRect(popup, elementRect); if (!PopupProxyHost._popupCanShow(popup)) { return; } - return await popup.showContent(elementRect, writingMode, type, details); + return await popup.showContent(elementRect, writingMode, type, details, context); } async _onApiSetCustomCss({id, css}) { @@ -151,6 +152,10 @@ class PopupProxyHost { return popup.setContentScale(scale); } + async _onApiGetHostUrl() { + return window.location.href; + } + // Private functions _getPopup(id) { diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 3af83db2..cd3c1bc9 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -20,12 +20,11 @@ */ class PopupProxy { - constructor(id, depth, parentId, parentFrameId, url, getFrameOffset=null, setDisabled=null) { + constructor(id, depth, parentId, parentFrameId, getFrameOffset=null, setDisabled=null) { this._parentId = parentId; this._parentFrameId = parentFrameId; this._id = id; this._depth = depth; - this._url = url; this._apiSender = new FrontendApiSender(); this._getFrameOffset = getFrameOffset; this._setDisabled = setDisabled; @@ -49,10 +48,6 @@ class PopupProxy { return this._depth; } - get url() { - return this._url; - } - // Public functions async prepare() { @@ -64,8 +59,8 @@ class PopupProxy { return true; } - async setOptions(options) { - return await this._invokeHostApi('setOptions', {id: this._id, options}); + async setOptionsContext(optionsContext, source) { + return await this._invokeHostApi('setOptionsContext', {id: this._id, optionsContext, source}); } hide(changeFocus) { @@ -88,14 +83,14 @@ class PopupProxy { return await this._invokeHostApi('containsPoint', {id: this._id, x, y}); } - async showContent(elementRect, writingMode, type=null, details=null) { + async showContent(elementRect, writingMode, type, details, context) { let {x, y, width, height} = elementRect; if (this._getFrameOffset !== null) { await this._updateFrameOffset(); [x, y] = this._applyFrameOffset(x, y); } elementRect = {x, y, width, height}; - return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details}); + return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details, context}); } async setCustomCss(css) { @@ -110,6 +105,10 @@ class PopupProxy { this._invokeHostApi('setContentScale', {id: this._id, scale}); } + async getHostUrl() { + return await this._invokeHostApi('getHostUrl', {}); + } + // Private _invokeHostApi(action, params={}) { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index ae158263..e735431b 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -19,6 +19,7 @@ * DOM * apiGetMessageToken * apiInjectStylesheet + * apiOptionsGet */ class Popup { @@ -33,10 +34,12 @@ class Popup { this._visible = false; this._visibleOverride = null; this._options = null; + this._optionsContext = null; this._contentScale = 1.0; this._containerSizeContentScale = null; this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); this._messageToken = null; + this._previousOptionsContextSource = null; this._container = document.createElement('iframe'); this._container.className = 'yomichan-float'; @@ -72,19 +75,20 @@ class Popup { return this._frameId; } - get url() { - return window.location.href; - } - // Public functions isProxy() { return false; } - async setOptions(options) { - this._options = options; + async setOptionsContext(optionsContext, source) { + this._optionsContext = optionsContext; + this._previousOptionsContextSource = source; + + this._options = await apiOptionsGet(optionsContext); this.updateTheme(); + + this._invokeApi('setOptionsContext', {optionsContext}); } hide(changeFocus) { @@ -120,8 +124,14 @@ class Popup { return false; } - async showContent(elementRect, writingMode, type=null, details=null) { + async showContent(elementRect, writingMode, type, details, context) { if (this._options === null) { throw new Error('Options not assigned'); } + + const {optionsContext, source} = context; + if (source !== this._previousOptionsContextSource) { + await this.setOptionsContext(optionsContext, source); + } + await this._show(elementRect, writingMode); if (type === null) { return; } this._invokeApi('setContent', {type, details}); @@ -219,10 +229,9 @@ class Popup { this._invokeApi('prepare', { popupInfo: { id: this._id, - depth: this._depth, parentFrameId }, - url: this.url, + optionsContext: this._optionsContext, childrenSupported: this._childrenSupported, scale: this._contentScale }); diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 9587ec3b..70b7fcd3 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -177,8 +177,6 @@ class Display { async prepare() { await yomichan.prepare(); await this.displayGenerator.prepare(); - await this.updateOptions(); - yomichan.on('optionsUpdated', () => this.updateOptions()); } onError(_error) { -- cgit v1.2.3 From 48c7010f4ea8daafd30e5650625c377affa0cecd Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Mon, 27 Apr 2020 18:10:37 -0400 Subject: Frontend refactor (part 1) (#484) * Remove _getVisualViewportScale * Use super's mouse event listener definitions * Remove redundant override * Remove getTouchEventListeners override * Rename Display.onSearchClear to onEscape * Change onSearchClear to clearSelection and use an event * Update how text is marked for selection and deselection * Replace onError with yomichan.logError * Update setEnabled to refresh all event listeners --- ext/bg/js/search-query-parser.js | 25 ++--------------- ext/bg/js/search.js | 2 +- ext/bg/js/settings/popup-preview-frame.js | 2 +- ext/fg/js/float.js | 2 +- ext/fg/js/frontend.js | 26 ++++++++--------- ext/mixed/js/display.js | 4 +-- ext/mixed/js/text-scanner.js | 46 +++++++++++++++---------------- 7 files changed, 42 insertions(+), 65 deletions(-) (limited to 'ext/bg/js/settings/popup-preview-frame.js') diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js index 0001c9ff..3215f8e4 100644 --- a/ext/bg/js/search-query-parser.js +++ b/ext/bg/js/search-query-parser.js @@ -44,12 +44,7 @@ class QueryParser extends TextScanner { await this.queryParserGenerator.prepare(); } - onError(error) { - yomichan.logError(error); - } - - onClick(e) { - super.onClick(e); + onClick2(e) { this.searchAt(e.clientX, e.clientY, 'click'); } @@ -84,22 +79,8 @@ class QueryParser extends TextScanner { getMouseEventListeners() { return [ - [this.node, 'click', this.onClick.bind(this)], - [this.node, 'mousedown', this.onMouseDown.bind(this)], - [this.node, 'mousemove', this.onMouseMove.bind(this)], - [this.node, 'mouseover', this.onMouseOver.bind(this)], - [this.node, 'mouseout', this.onMouseOut.bind(this)] - ]; - } - - getTouchEventListeners() { - return [ - [this.node, 'auxclick', this.onAuxClick.bind(this)], - [this.node, 'touchstart', this.onTouchStart.bind(this)], - [this.node, 'touchend', this.onTouchEnd.bind(this)], - [this.node, 'touchcancel', this.onTouchCancel.bind(this)], - [this.node, 'touchmove', this.onTouchMove.bind(this), {passive: false}], - [this.node, 'contextmenu', this.onContextMenu.bind(this)] + ...super.getMouseEventListeners(), + [this.node, 'click', this.onClick2.bind(this)] ]; } diff --git a/ext/bg/js/search.js b/ext/bg/js/search.js index cbd7b562..b7d2eed8 100644 --- a/ext/bg/js/search.js +++ b/ext/bg/js/search.js @@ -125,7 +125,7 @@ class DisplaySearch extends Display { yomichan.logError(error); } - onSearchClear() { + onEscape() { if (this.query === null) { return; } diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index 05a2a41b..e73c04a0 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -69,7 +69,7 @@ class SettingsPopupPreview { this.frontend.getOptionsContext = async () => this.optionsContext; this.frontend.setEnabled = () => {}; - this.frontend.onSearchClear = () => {}; + this.frontend.clearSelection = () => {}; await this.frontend.prepare(); diff --git a/ext/fg/js/float.js b/ext/fg/js/float.js index fd3b92cc..294093cd 100644 --- a/ext/fg/js/float.js +++ b/ext/fg/js/float.js @@ -92,7 +92,7 @@ class DisplayFloat extends Display { this._orphaned = true; } - onSearchClear() { + onEscape() { window.parent.postMessage('popupClose', '*'); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 46921d36..50f52724 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -49,7 +49,7 @@ class Frontend extends TextScanner { this._lastShowPromise = Promise.resolve(); this._windowMessageHandlers = new Map([ - ['popupClose', () => this.onSearchClear(true)], + ['popupClose', () => this.clearSelection(false)], ['selectionCopy', () => document.execCommand('copy')] ]); @@ -79,10 +79,12 @@ class Frontend extends TextScanner { yomichan.on('zoomChanged', this.onZoomChanged.bind(this)); chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); + this.on('clearSelection', this.onClearSelection.bind(this)); + this._updateContentScale(); this._broadcastRootPopupInformation(); } catch (e) { - this.onError(e); + yomichan.logError(e); } } @@ -140,7 +142,7 @@ class Frontend extends TextScanner { } async setPopup(popup) { - this.onSearchClear(false); + this.clearSelection(true); this.popup = popup; await popup.setOptionsContext(await this.getOptionsContext(), this._id); } @@ -186,11 +188,11 @@ class Frontend extends TextScanner { this._showPopupContent(textSource, await this.getOptionsContext(), 'orphaned'); } } else { - this.onError(e); + yomichan.logError(e); } } finally { if (results === null && this.options.scanning.autoHideResults) { - this.onSearchClear(true); + this.clearSelection(false); } } @@ -238,10 +240,9 @@ class Frontend extends TextScanner { return {definitions, type: 'kanji'}; } - onSearchClear(changeFocus) { - this.popup.hide(changeFocus); + onClearSelection({passive}) { + this.popup.hide(!passive); this.popup.clearAutoPlayTimer(); - super.onSearchClear(changeFocus); } async getOptionsContext() { @@ -269,7 +270,9 @@ class Frontend extends TextScanner { contentScale /= this._pageZoomFactor; } if (popupScaleRelativeToVisualViewport) { - contentScale /= Frontend._getVisualViewportScale(); + const visualViewport = window.visualViewport; + const visualViewportScale = (visualViewport !== null && typeof visualViewport === 'object' ? visualViewport.scale : 1.0); + contentScale /= visualViewportScale; } if (contentScale === this._contentScale) { return; } @@ -302,9 +305,4 @@ class Frontend extends TextScanner { this._showPopupContent(textSource, await this.getOptionsContext()); } } - - static _getVisualViewportScale() { - const visualViewport = window.visualViewport; - return visualViewport !== null && typeof visualViewport === 'object' ? visualViewport.scale : 1.0; - } } diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js index 70b7fcd3..32081c70 100644 --- a/ext/mixed/js/display.js +++ b/ext/mixed/js/display.js @@ -69,7 +69,7 @@ class Display { this._onKeyDownHandlers = new Map([ ['Escape', () => { - this.onSearchClear(); + this.onEscape(); return true; }], ['PageUp', (e) => { @@ -183,7 +183,7 @@ class Display { throw new Error('Override me'); } - onSearchClear() { + onEscape() { throw new Error('Override me'); } diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index 1c32714b..c582ccd8 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -21,8 +21,9 @@ * docRangeFromPoint */ -class TextScanner { +class TextScanner extends EventDispatcher { constructor(node, ignoreElements, ignorePoints) { + super(); this.node = node; this.ignoreElements = ignoreElements; this.ignorePoints = ignorePoints; @@ -32,6 +33,7 @@ class TextScanner { this.scanTimerPromise = null; this.causeCurrent = null; this.textSourceCurrent = null; + this.textSourceCurrentSelected = false; this.pendingLookup = false; this.options = null; @@ -92,7 +94,7 @@ class TextScanner { if (DOM.isMouseButtonDown(e, 'primary')) { this.scanTimerClear(); - this.onSearchClear(true); + this.clearSelection(false); } } @@ -200,10 +202,6 @@ class TextScanner { throw new Error('Override me'); } - onError(error) { - yomichan.logError(error); - } - async scanTimerWait() { const delay = this.options.scanning.delay; const promise = promiseTimeout(delay, true); @@ -225,17 +223,12 @@ class TextScanner { } setEnabled(enabled, canEnable) { - if (enabled && canEnable) { - if (!this.enabled) { - this.hookEvents(); - this.enabled = true; - } + this.eventListeners.removeAllEventListeners(); + this.enabled = enabled && canEnable; + if (this.enabled) { + this.hookEvents(); } else { - if (this.enabled) { - this.eventListeners.removeAllEventListeners(); - this.enabled = false; - } - this.onSearchClear(false); + this.clearSelection(true); } } @@ -300,10 +293,7 @@ class TextScanner { const result = await this.onSearchSource(textSource, cause); if (result !== null) { this.causeCurrent = cause; - this.textSourceCurrent = textSource; - if (this.options.scanning.selectText) { - textSource.select(); - } + this.setCurrentTextSource(textSource); } this.pendingLookup = false; } finally { @@ -312,7 +302,7 @@ class TextScanner { } } } catch (e) { - this.onError(e); + yomichan.logError(e); } } @@ -333,13 +323,15 @@ class TextScanner { } } - onSearchClear(_) { + clearSelection(passive) { if (this.textSourceCurrent !== null) { - if (this.options.scanning.selectText) { + if (this.textSourceCurrentSelected) { this.textSourceCurrent.deselect(); } this.textSourceCurrent = null; + this.textSourceCurrentSelected = false; } + this.trigger('clearSelection', {passive}); } getCurrentTextSource() { @@ -347,7 +339,13 @@ class TextScanner { } setCurrentTextSource(textSource) { - return this.textSourceCurrent = textSource; + this.textSourceCurrent = textSource; + if (this.options.scanning.selectText) { + this.textSourceCurrent.select(); + this.textSourceCurrentSelected = true; + } else { + this.textSourceCurrentSelected = false; + } } static isScanningModifierPressed(scanningModifier, mouseEvent) { -- cgit v1.2.3 From 08ada6844af424e8ff28e592fc6b9dbc1a9a97eb Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 2 May 2020 12:47:15 -0400 Subject: Remove Frontend inheritance (#486) * Make Frontend use composition instead of inheritance for TextScanner * Use push instead of concat * Update setOptions and setEnabled APIs * Update how onWindowMessage event listener is added/removed * Rename options to _options * Use bind instead of arrow function * Fix selection being cleared due to settings changes --- ext/bg/js/search-query-parser.js | 1 + ext/bg/js/settings/popup-preview-frame.js | 9 +-- ext/fg/js/frontend.js | 94 +++++++++++++++++++------------ ext/mixed/js/text-scanner.js | 22 ++++++-- 4 files changed, 77 insertions(+), 49 deletions(-) (limited to 'ext/bg/js/settings/popup-preview-frame.js') diff --git a/ext/bg/js/search-query-parser.js b/ext/bg/js/search-query-parser.js index 3215f8e4..137234e8 100644 --- a/ext/bg/js/search-query-parser.js +++ b/ext/bg/js/search-query-parser.js @@ -86,6 +86,7 @@ class QueryParser extends TextScanner { setOptions(options) { super.setOptions(options); + super.setEnabled(true); this.queryParser.dataset.termSpacing = `${options.parsing.termSpacing}`; } diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index e73c04a0..cb548ed7 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -66,12 +66,10 @@ class SettingsPopupPreview { this.popup.setCustomOuterCss = this.popupSetCustomOuterCss.bind(this); this.frontend = new Frontend(this.popup); - this.frontend.getOptionsContext = async () => this.optionsContext; - this.frontend.setEnabled = () => {}; - this.frontend.clearSelection = () => {}; - await this.frontend.prepare(); + this.frontend.setDisabledOverride(true); + this.frontend.canClearSelection = false; // Update search this.updateSearch(); @@ -169,8 +167,7 @@ class SettingsPopupPreview { const source = new TextSourceRange(range, range.toString(), null, null); try { - await this.frontend.onSearchSource(source, 'script'); - this.frontend.setCurrentTextSource(source); + await this.frontend.setTextSource(source); } finally { source.cleanup(); } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 50f52724..76ad27e0 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -25,14 +25,8 @@ * docSentenceExtract */ -class Frontend extends TextScanner { +class Frontend { constructor(popup, getUrl=null) { - super( - window, - () => this.popup.isProxy() ? [] : [this.popup.getContainer()], - [(x, y) => this.popup.containsPoint(x, y)] - ); - this._id = yomichan.generateId(16); this.popup = popup; @@ -41,15 +35,23 @@ class Frontend extends TextScanner { this._disabledOverride = false; - this.options = null; + this._options = null; this._pageZoomFactor = 1.0; this._contentScale = 1.0; this._orphaned = false; this._lastShowPromise = Promise.resolve(); + this._enabledEventListeners = new EventListenerCollection(); + this._textScanner = new TextScanner( + window, + () => this.popup.isProxy() ? [] : [this.popup.getContainer()], + [(x, y) => this.popup.containsPoint(x, y)] + ); + this._textScanner.onSearchSource = this.onSearchSource.bind(this); + this._windowMessageHandlers = new Map([ - ['popupClose', () => this.clearSelection(false)], + ['popupClose', () => this._textScanner.clearSelection(false)], ['selectionCopy', () => document.execCommand('copy')] ]); @@ -60,6 +62,14 @@ class Frontend extends TextScanner { ]); } + get canClearSelection() { + return this._textScanner.canClearSelection; + } + + set canClearSelection(value) { + this._textScanner.canClearSelection = value; + } + async prepare() { try { await this.updateOptions(); @@ -79,7 +89,7 @@ class Frontend extends TextScanner { yomichan.on('zoomChanged', this.onZoomChanged.bind(this)); chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); - this.on('clearSelection', this.onClearSelection.bind(this)); + this._textScanner.on('clearSelection', this.onClearSelection.bind(this)); this._updateContentScale(); this._broadcastRootPopupInformation(); @@ -129,44 +139,45 @@ class Frontend extends TextScanner { this._updateContentScale(); } - getMouseEventListeners() { - return [ - ...super.getMouseEventListeners(), - [window, 'message', this.onWindowMessage.bind(this)] - ]; - } - setDisabledOverride(disabled) { this._disabledOverride = disabled; - this.setEnabled(this.options.general.enable, this._canEnable()); + this._updateTextScannerEnabled(); } async setPopup(popup) { - this.clearSelection(true); + this._textScanner.clearSelection(true); this.popup = popup; await popup.setOptionsContext(await this.getOptionsContext(), this._id); } async updateOptions() { const optionsContext = await this.getOptionsContext(); - this.options = await apiOptionsGet(optionsContext); - this.setOptions(this.options, this._canEnable()); + this._options = await apiOptionsGet(optionsContext); + this._textScanner.setOptions(this._options); + this._updateTextScannerEnabled(); const ignoreNodes = ['.scan-disable', '.scan-disable *']; - if (!this.options.scanning.enableOnPopupExpressions) { + if (!this._options.scanning.enableOnPopupExpressions) { ignoreNodes.push('.source-text', '.source-text *'); } - this.ignoreNodes = ignoreNodes.join(','); + this._textScanner.ignoreNodes = ignoreNodes.join(','); await this.popup.setOptionsContext(optionsContext, this._id); this._updateContentScale(); - if (this.textSourceCurrent !== null && this.causeCurrent !== null) { - await this.onSearchSource(this.textSourceCurrent, this.causeCurrent); + const textSourceCurrent = this._textScanner.getCurrentTextSource(); + const causeCurrent = this._textScanner.causeCurrent; + if (textSourceCurrent !== null && causeCurrent !== null) { + await this.onSearchSource(textSourceCurrent, causeCurrent); } } + async setTextSource(textSource) { + await this.onSearchSource(textSource, 'script'); + this._textScanner.setCurrentTextSource(textSource); + } + async onSearchSource(textSource, cause) { let results = null; @@ -184,15 +195,15 @@ class Frontend extends TextScanner { } } catch (e) { if (this._orphaned) { - if (textSource !== null && this.options.scanning.modifier !== 'none') { + if (textSource !== null && this._options.scanning.modifier !== 'none') { this._showPopupContent(textSource, await this.getOptionsContext(), 'orphaned'); } } else { yomichan.logError(e); } } finally { - if (results === null && this.options.scanning.autoHideResults) { - this.clearSelection(false); + if (results === null && this._options.scanning.autoHideResults) { + this._textScanner.clearSelection(false); } } @@ -201,7 +212,7 @@ class Frontend extends TextScanner { showContent(textSource, focus, definitions, type, optionsContext) { const {url} = optionsContext; - const sentence = docSentenceExtract(textSource, this.options.anki.sentenceExt); + const sentence = docSentenceExtract(textSource, this._options.anki.sentenceExt); this._showPopupContent( textSource, optionsContext, @@ -215,7 +226,7 @@ class Frontend extends TextScanner { } async findTerms(textSource, optionsContext) { - this.setTextSourceScanLength(textSource, this.options.scanning.length); + this._textScanner.setTextSourceScanLength(textSource, this._options.scanning.length); const searchText = textSource.text(); if (searchText.length === 0) { return null; } @@ -229,7 +240,7 @@ class Frontend extends TextScanner { } async findKanji(textSource, optionsContext) { - this.setTextSourceScanLength(textSource, 1); + this._textScanner.setTextSourceScanLength(textSource, 1); const searchText = textSource.text(); if (searchText.length === 0) { return null; } @@ -263,8 +274,21 @@ class Frontend extends TextScanner { return this._lastShowPromise; } + _updateTextScannerEnabled() { + const enabled = ( + this._options.general.enable && + this.popup.depth <= this._options.scanning.popupNestingMaxDepth && + !this._disabledOverride + ); + this._enabledEventListeners.removeAllEventListeners(); + this._textScanner.setEnabled(enabled); + if (enabled) { + this._enabledEventListeners.addEventListener(window, 'message', this.onWindowMessage.bind(this)); + } + } + _updateContentScale() { - const {popupScalingFactor, popupScaleRelativeToPageZoom, popupScaleRelativeToVisualViewport} = this.options.general; + const {popupScalingFactor, popupScaleRelativeToPageZoom, popupScaleRelativeToVisualViewport} = this._options.general; let contentScale = popupScalingFactor; if (popupScaleRelativeToPageZoom) { contentScale /= this._pageZoomFactor; @@ -295,12 +319,8 @@ class Frontend extends TextScanner { }); } - _canEnable() { - return this.popup.depth <= this.options.scanning.popupNestingMaxDepth && !this._disabledOverride; - } - async _updatePopupPosition() { - const textSource = this.getCurrentTextSource(); + const textSource = this._textScanner.getCurrentTextSource(); if (textSource !== null && await this.popup.isVisible()) { this._showPopupContent(textSource, await this.getOptionsContext()); } diff --git a/ext/mixed/js/text-scanner.js b/ext/mixed/js/text-scanner.js index c582ccd8..774eef44 100644 --- a/ext/mixed/js/text-scanner.js +++ b/ext/mixed/js/text-scanner.js @@ -45,6 +45,16 @@ class TextScanner extends EventDispatcher { this.preventNextMouseDown = false; this.preventNextClick = false; this.preventScroll = false; + + this._canClearSelection = true; + } + + get canClearSelection() { + return this._canClearSelection; + } + + set canClearSelection(value) { + this._canClearSelection = value; } onMouseOver(e) { @@ -222,9 +232,9 @@ class TextScanner extends EventDispatcher { } } - setEnabled(enabled, canEnable) { + setEnabled(enabled) { this.eventListeners.removeAllEventListeners(); - this.enabled = enabled && canEnable; + this.enabled = enabled; if (this.enabled) { this.hookEvents(); } else { @@ -233,9 +243,9 @@ class TextScanner extends EventDispatcher { } hookEvents() { - let eventListenerInfos = this.getMouseEventListeners(); + const eventListenerInfos = this.getMouseEventListeners(); if (this.options.scanning.touchInputEnabled) { - eventListenerInfos = eventListenerInfos.concat(this.getTouchEventListeners()); + eventListenerInfos.push(...this.getTouchEventListeners()); } for (const [node, type, listener, options] of eventListenerInfos) { @@ -264,9 +274,8 @@ class TextScanner extends EventDispatcher { ]; } - setOptions(options, canEnable=true) { + setOptions(options) { this.options = options; - this.setEnabled(this.options.general.enable, canEnable); } async searchAt(x, y, cause) { @@ -324,6 +333,7 @@ class TextScanner extends EventDispatcher { } clearSelection(passive) { + if (!this._canClearSelection) { return; } if (this.textSourceCurrent !== null) { if (this.textSourceCurrentSelected) { this.textSourceCurrent.deselect(); -- cgit v1.2.3 From 38d6f58fac9ab590454aeb3f41b3cd79f3c4464a Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Wed, 6 May 2020 19:35:36 -0400 Subject: Frontend refactor (#508) * Use this.popup instead of this.frontend.popup * Rename Frontend.popup to _popup * Make Frontend functions private * Clean up Frontend constructor --- ext/bg/js/settings/popup-preview-frame.js | 8 +-- ext/fg/js/frontend.js | 109 ++++++++++++++---------------- 2 files changed, 55 insertions(+), 62 deletions(-) (limited to 'ext/bg/js/settings/popup-preview-frame.js') diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index cb548ed7..c7b0c218 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -120,7 +120,7 @@ class SettingsPopupPreview { } this.themeChangeTimeout = setTimeout(() => { this.themeChangeTimeout = null; - this.frontend.popup.updateTheme(); + this.popup.updateTheme(); }, 300); } @@ -141,12 +141,12 @@ class SettingsPopupPreview { setCustomCss(css) { if (this.frontend === null) { return; } - this.frontend.popup.setCustomCss(css); + this.popup.setCustomCss(css); } setCustomOuterCss(css) { if (this.frontend === null) { return; } - this.frontend.popup.setCustomOuterCss(css, false); + this.popup.setCustomOuterCss(css, false); } async updateOptionsContext(optionsContext) { @@ -174,7 +174,7 @@ class SettingsPopupPreview { this.textSource = source; await this.frontend.showContentCompleted(); - if (this.frontend.popup.isVisibleSync()) { + if (this.popup.isVisibleSync()) { this.popupShown = true; } diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js index 7ba0de2e..f6cce123 100644 --- a/ext/fg/js/frontend.js +++ b/ext/fg/js/frontend.js @@ -28,30 +28,23 @@ class Frontend { constructor(popup, getUrl=null) { this._id = yomichan.generateId(16); - - this.popup = popup; - + this._popup = popup; this._getUrl = getUrl; - this._disabledOverride = false; - this._options = null; - this._pageZoomFactor = 1.0; this._contentScale = 1.0; this._orphaned = false; this._lastShowPromise = Promise.resolve(); - this._enabledEventListeners = new EventListenerCollection(); + this._activeModifiers = new Set(); + this._optionsUpdatePending = false; this._textScanner = new TextScanner( window, - () => this.popup.isProxy() ? [] : [this.popup.getContainer()], - [(x, y) => this.popup.containsPoint(x, y)] + () => this._popup.isProxy() ? [] : [this._popup.getContainer()], + [(x, y) => this._popup.containsPoint(x, y)] ); - this._textScanner.onSearchSource = this.onSearchSource.bind(this); - - this._activeModifiers = new Set(); - this._optionsUpdatePending = false; + this._textScanner.onSearchSource = this._onSearchSource.bind(this); this._windowMessageHandlers = new Map([ ['popupClose', () => this._textScanner.clearSelection(false)], @@ -59,7 +52,7 @@ class Frontend { ]); this._runtimeMessageHandlers = new Map([ - ['popupSetVisibleOverride', ({visible}) => { this.popup.setVisibleOverride(visible); }], + ['popupSetVisibleOverride', ({visible}) => { this._popup.setVisibleOverride(visible); }], ['rootPopupRequestInformationBroadcast', () => { this._broadcastRootPopupInformation(); }], ['requestDocumentInformationBroadcast', ({uniqueId}) => { this._broadcastDocumentInformation(uniqueId); }] ]); @@ -79,21 +72,21 @@ class Frontend { const {zoomFactor} = await apiGetZoom(); this._pageZoomFactor = zoomFactor; - window.addEventListener('resize', this.onResize.bind(this), false); + window.addEventListener('resize', this._onResize.bind(this), false); const visualViewport = window.visualViewport; if (visualViewport !== null && typeof visualViewport === 'object') { - window.visualViewport.addEventListener('scroll', this.onVisualViewportScroll.bind(this)); - window.visualViewport.addEventListener('resize', this.onVisualViewportResize.bind(this)); + window.visualViewport.addEventListener('scroll', this._onVisualViewportScroll.bind(this)); + window.visualViewport.addEventListener('resize', this._onVisualViewportResize.bind(this)); } - yomichan.on('orphaned', this.onOrphaned.bind(this)); + yomichan.on('orphaned', this._onOrphaned.bind(this)); yomichan.on('optionsUpdated', this.updateOptions.bind(this)); - yomichan.on('zoomChanged', this.onZoomChanged.bind(this)); - chrome.runtime.onMessage.addListener(this.onRuntimeMessage.bind(this)); + yomichan.on('zoomChanged', this._onZoomChanged.bind(this)); + chrome.runtime.onMessage.addListener(this._onRuntimeMessage.bind(this)); - this._textScanner.on('clearSelection', this.onClearSelection.bind(this)); - this._textScanner.on('activeModifiersChanged', this.onActiveModifiersChanged.bind(this)); + this._textScanner.on('clearSelection', this._onClearSelection.bind(this)); + this._textScanner.on('activeModifiersChanged', this._onActiveModifiersChanged.bind(this)); this._updateContentScale(); this._broadcastRootPopupInformation(); @@ -102,11 +95,11 @@ class Frontend { } } - onResize() { + _onResize() { this._updatePopupPosition(); } - onWindowMessage(e) { + _onWindowMessage(e) { const action = e.data; const handler = this._windowMessageHandlers.get(action); if (typeof handler !== 'function') { return false; } @@ -114,9 +107,9 @@ class Frontend { handler(); } - onRuntimeMessage({action, params}, sender, callback) { + _onRuntimeMessage({action, params}, sender, callback) { const {targetPopupId} = params || {}; - if (typeof targetPopupId !== 'undefined' && targetPopupId !== this.popup.id) { return; } + if (typeof targetPopupId !== 'undefined' && targetPopupId !== this._popup.id) { return; } const handler = this._runtimeMessageHandlers.get(action); if (typeof handler !== 'function') { return false; } @@ -126,20 +119,20 @@ class Frontend { return false; } - onOrphaned() { + _onOrphaned() { this._orphaned = true; } - onZoomChanged({newZoomFactor}) { + _onZoomChanged({newZoomFactor}) { this._pageZoomFactor = newZoomFactor; this._updateContentScale(); } - onVisualViewportScroll() { + _onVisualViewportScroll() { this._updatePopupPosition(); } - onVisualViewportResize() { + _onVisualViewportResize() { this._updateContentScale(); } @@ -150,7 +143,7 @@ class Frontend { async setPopup(popup) { this._textScanner.clearSelection(true); - this.popup = popup; + this._popup = popup; await popup.setOptionsContext(await this.getOptionsContext(), this._id); } @@ -166,18 +159,18 @@ class Frontend { } this._textScanner.ignoreNodes = ignoreNodes.join(','); - await this.popup.setOptionsContext(optionsContext, this._id); + await this._popup.setOptionsContext(optionsContext, this._id); this._updateContentScale(); const textSourceCurrent = this._textScanner.getCurrentTextSource(); const causeCurrent = this._textScanner.causeCurrent; if (textSourceCurrent !== null && causeCurrent !== null) { - await this.onSearchSource(textSourceCurrent, causeCurrent); + await this._onSearchSource(textSourceCurrent, causeCurrent); } } - async updatePendingOptions() { + async _updatePendingOptions() { if (this._optionsUpdatePending) { this._optionsUpdatePending = false; await this.updateOptions(); @@ -185,12 +178,12 @@ class Frontend { } async setTextSource(textSource) { - await this.onSearchSource(textSource, 'script'); + await this._onSearchSource(textSource, 'script'); this._textScanner.setCurrentTextSource(textSource); } - async onSearchSource(textSource, cause) { - await this.updatePendingOptions(); + async _onSearchSource(textSource, cause) { + await this._updatePendingOptions(); let results = null; @@ -198,12 +191,12 @@ class Frontend { if (textSource !== null) { const optionsContext = await this.getOptionsContext(); results = ( - await this.findTerms(textSource, optionsContext) || - await this.findKanji(textSource, optionsContext) + await this._findTerms(textSource, optionsContext) || + await this._findKanji(textSource, optionsContext) ); if (results !== null) { const focus = (cause === 'mouse'); - this.showContent(textSource, focus, results.definitions, results.type, optionsContext); + this._showContent(textSource, focus, results.definitions, results.type, optionsContext); } } } catch (e) { @@ -223,7 +216,7 @@ class Frontend { return results; } - showContent(textSource, focus, definitions, type, optionsContext) { + _showContent(textSource, focus, definitions, type, optionsContext) { const {url} = optionsContext; const sentence = docSentenceExtract(textSource, this._options.anki.sentenceExt); this._showPopupContent( @@ -238,7 +231,7 @@ class Frontend { return this._lastShowPromise; } - async findTerms(textSource, optionsContext) { + async _findTerms(textSource, optionsContext) { const searchText = this._textScanner.getTextSourceContent(textSource, this._options.scanning.length); if (searchText.length === 0) { return null; } @@ -250,7 +243,7 @@ class Frontend { return {definitions, type: 'terms'}; } - async findKanji(textSource, optionsContext) { + async _findKanji(textSource, optionsContext) { const searchText = this._textScanner.getTextSourceContent(textSource, 1); if (searchText.length === 0) { return null; } @@ -262,16 +255,16 @@ class Frontend { return {definitions, type: 'kanji'}; } - onClearSelection({passive}) { - this.popup.hide(!passive); - this.popup.clearAutoPlayTimer(); - this.updatePendingOptions(); + _onClearSelection({passive}) { + this._popup.hide(!passive); + this._popup.clearAutoPlayTimer(); + this._updatePendingOptions(); } - async onActiveModifiersChanged({modifiers}) { + async _onActiveModifiersChanged({modifiers}) { if (areSetsEqual(modifiers, this._activeModifiers)) { return; } this._activeModifiers = modifiers; - if (await this.popup.isVisible()) { + if (await this._popup.isVisible()) { this._optionsUpdatePending = true; return; } @@ -280,14 +273,14 @@ class Frontend { async getOptionsContext() { const url = this._getUrl !== null ? await this._getUrl() : window.location.href; - const depth = this.popup.depth; + const depth = this._popup.depth; const modifierKeys = [...this._activeModifiers]; return {depth, url, modifierKeys}; } _showPopupContent(textSource, optionsContext, type=null, details=null) { const context = {optionsContext, source: this._id}; - this._lastShowPromise = this.popup.showContent( + this._lastShowPromise = this._popup.showContent( textSource.getRect(), textSource.getWritingMode(), type, @@ -300,13 +293,13 @@ class Frontend { _updateTextScannerEnabled() { const enabled = ( this._options.general.enable && - this.popup.depth <= this._options.scanning.popupNestingMaxDepth && + this._popup.depth <= this._options.scanning.popupNestingMaxDepth && !this._disabledOverride ); this._enabledEventListeners.removeAllEventListeners(); this._textScanner.setEnabled(enabled); if (enabled) { - this._enabledEventListeners.addEventListener(window, 'message', this.onWindowMessage.bind(this)); + this._enabledEventListeners.addEventListener(window, 'message', this._onWindowMessage.bind(this)); } } @@ -324,27 +317,27 @@ class Frontend { if (contentScale === this._contentScale) { return; } this._contentScale = contentScale; - this.popup.setContentScale(this._contentScale); + this._popup.setContentScale(this._contentScale); this._updatePopupPosition(); } _broadcastRootPopupInformation() { - if (!this.popup.isProxy() && this.popup.depth === 0 && this.popup.frameId === 0) { - apiBroadcastTab('rootPopupInformation', {popupId: this.popup.id, frameId: this.popup.frameId}); + if (!this._popup.isProxy() && this._popup.depth === 0 && this._popup.frameId === 0) { + apiBroadcastTab('rootPopupInformation', {popupId: this._popup.id, frameId: this._popup.frameId}); } } _broadcastDocumentInformation(uniqueId) { apiBroadcastTab('documentInformationBroadcast', { uniqueId, - frameId: this.popup.frameId, + frameId: this._popup.frameId, title: document.title }); } async _updatePopupPosition() { const textSource = this._textScanner.getCurrentTextSource(); - if (textSource !== null && await this.popup.isVisible()) { + if (textSource !== null && await this._popup.isVisible()) { this._showPopupContent(textSource, await this.getOptionsContext()); } } -- cgit v1.2.3 From b936c3e4b1bc993e535b02dee91bf6afc15a3564 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 8 May 2020 19:04:53 -0400 Subject: Popup proxy host refactor (#516) * Rename PopupProxyHost to PopupFactory * Update FrontendApiReceiver to support non-async handlers * Make some functions non-async * Make setCustomCss non-async * Make setContentScale non-async * Remove static * Rename variables * Pass frameId into PopupFactory's constructor * Change FrontendApiReceiver source from popup-proxy-host to popup-factor * Rename _invokeHostApi to _invoke * Rename PopupProxy.getHostUrl to getUrl --- ext/bg/js/search-main.js | 2 +- ext/bg/js/settings/popup-preview-frame.js | 11 +- ext/bg/settings-popup-preview.html | 2 +- ext/fg/js/content-script-main.js | 28 +++-- ext/fg/js/frontend-api-receiver.js | 27 ++++- ext/fg/js/popup-factory.js | 181 +++++++++++++++++++++++++++++ ext/fg/js/popup-proxy-host.js | 186 ------------------------------ ext/fg/js/popup-proxy.js | 32 ++--- ext/fg/js/popup.js | 2 +- ext/manifest.json | 2 +- 10 files changed, 247 insertions(+), 226 deletions(-) create mode 100644 ext/fg/js/popup-factory.js delete mode 100644 ext/fg/js/popup-proxy-host.js (limited to 'ext/bg/js/settings/popup-preview-frame.js') diff --git a/ext/bg/js/search-main.js b/ext/bg/js/search-main.js index 1075d46e..6e092fbc 100644 --- a/ext/bg/js/search-main.js +++ b/ext/bg/js/search-main.js @@ -31,7 +31,7 @@ async function injectSearchFrontend() { '/fg/js/frontend-api-receiver.js', '/fg/js/frame-offset-forwarder.js', '/fg/js/popup.js', - '/fg/js/popup-proxy-host.js', + '/fg/js/popup-factory.js', '/fg/js/frontend.js', '/fg/js/content-script-main.js' ]); diff --git a/ext/bg/js/settings/popup-preview-frame.js b/ext/bg/js/settings/popup-preview-frame.js index c7b0c218..8901a0c4 100644 --- a/ext/bg/js/settings/popup-preview-frame.js +++ b/ext/bg/js/settings/popup-preview-frame.js @@ -18,8 +18,9 @@ /* global * Frontend * Popup - * PopupProxyHost + * PopupFactory * TextSourceRange + * apiFrameInformationGet * apiOptionsGet */ @@ -56,10 +57,12 @@ class SettingsPopupPreview { window.apiOptionsGet = this.apiOptionsGet.bind(this); // Overwrite frontend - const popupHost = new PopupProxyHost(); - await popupHost.prepare(); + const {frameId} = await apiFrameInformationGet(); - this.popup = popupHost.getOrCreatePopup(); + const popupFactory = new PopupFactory(frameId); + await popupFactory.prepare(); + + this.popup = popupFactory.getOrCreatePopup(); this.popup.setChildrenSupported(false); this.popupSetCustomOuterCssOld = this.popup.setCustomOuterCss; diff --git a/ext/bg/settings-popup-preview.html b/ext/bg/settings-popup-preview.html index a332fb22..3d7f6455 100644 --- a/ext/bg/settings-popup-preview.html +++ b/ext/bg/settings-popup-preview.html @@ -127,7 +127,7 @@ - + diff --git a/ext/fg/js/content-script-main.js b/ext/fg/js/content-script-main.js index 277e6567..3afc9648 100644 --- a/ext/fg/js/content-script-main.js +++ b/ext/fg/js/content-script-main.js @@ -19,10 +19,11 @@ * DOM * FrameOffsetForwarder * Frontend + * PopupFactory * PopupProxy - * PopupProxyHost * apiBroadcastTab * apiForwardLogsToBackend + * apiFrameInformationGet * apiOptionsGet */ @@ -47,10 +48,17 @@ async function createIframePopupProxy(frameOffsetForwarder, setDisabled) { } async function getOrCreatePopup(depth) { - const popupHost = new PopupProxyHost(); - await popupHost.prepare(); + const {frameId} = await apiFrameInformationGet(); + if (typeof frameId !== 'number') { + const error = new Error('Failed to get frameId'); + yomichan.logError(error); + throw error; + } - const popup = popupHost.getOrCreatePopup(null, null, depth); + const popupFactory = new PopupFactory(frameId); + await popupFactory.prepare(); + + const popup = popupFactory.getOrCreatePopup(null, null, depth); return popup; } @@ -89,20 +97,20 @@ async function createPopupProxy(depth, id, parentFrameId) { }; let urlUpdatedAt = 0; - let proxyHostUrlCached = url; - const getProxyHostUrl = async () => { + let popupProxyUrlCached = url; + const getPopupProxyUrl = async () => { const now = Date.now(); if (popups.proxy !== null && now - urlUpdatedAt > 500) { - proxyHostUrlCached = await popups.proxy.getHostUrl(); + popupProxyUrlCached = await popups.proxy.getUrl(); urlUpdatedAt = now; } - return proxyHostUrlCached; + return popupProxyUrlCached; }; const applyOptions = async () => { const optionsContext = { depth: isSearchPage ? 0 : depth, - url: proxy ? await getProxyHostUrl() : window.location.href + url: proxy ? await getPopupProxyUrl() : window.location.href }; const options = await apiOptionsGet(optionsContext); @@ -124,7 +132,7 @@ async function createPopupProxy(depth, id, parentFrameId) { } if (frontend === null) { - const getUrl = proxy ? getProxyHostUrl : null; + const getUrl = proxy ? getPopupProxyUrl : null; frontend = new Frontend(popup, getUrl); frontendPreparePromise = frontend.prepare(); await frontendPreparePromise; diff --git a/ext/fg/js/frontend-api-receiver.js b/ext/fg/js/frontend-api-receiver.js index c5bb58af..3fa9e8b6 100644 --- a/ext/fg/js/frontend-api-receiver.js +++ b/ext/fg/js/frontend-api-receiver.js @@ -17,9 +17,9 @@ class FrontendApiReceiver { - constructor(source='', handlers=new Map()) { + constructor(source, messageHandlers) { this._source = source; - this._handlers = handlers; + this._messageHandlers = messageHandlers; } prepare() { @@ -35,14 +35,29 @@ class FrontendApiReceiver { _onMessage(port, {id, action, params, target, senderId}) { if (target !== this._source) { return; } - const handler = this._handlers.get(action); - if (typeof handler !== 'function') { return; } + const messageHandler = this._messageHandlers.get(action); + if (typeof messageHandler === 'undefined') { return; } + + const {handler, async} = messageHandler; this._sendAck(port, id, senderId); - this._invokeHandler(handler, params, port, id, senderId); + if (async) { + this._invokeHandlerAsync(handler, params, port, id, senderId); + } else { + this._invokeHandler(handler, params, port, id, senderId); + } + } + + _invokeHandler(handler, params, port, id, senderId) { + try { + const result = handler(params); + this._sendResult(port, id, senderId, {result}); + } catch (error) { + this._sendResult(port, id, senderId, {error: errorToJson(error)}); + } } - async _invokeHandler(handler, params, port, id, senderId) { + async _invokeHandlerAsync(handler, params, port, id, senderId) { try { const result = await handler(params); this._sendResult(port, id, senderId, {result}); diff --git a/ext/fg/js/popup-factory.js b/ext/fg/js/popup-factory.js new file mode 100644 index 00000000..21e64dd0 --- /dev/null +++ b/ext/fg/js/popup-factory.js @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2019-2020 Yomichan 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 . + */ + +/* global + * FrontendApiReceiver + * Popup + */ + +class PopupFactory { + constructor(frameId) { + this._popups = new Map(); + this._frameId = frameId; + } + + // Public functions + + async prepare() { + const apiReceiver = new FrontendApiReceiver(`popup-factory#${this._frameId}`, new Map([ + ['getOrCreatePopup', {async: false, handler: this._onApiGetOrCreatePopup.bind(this)}], + ['setOptionsContext', {async: true, handler: this._onApiSetOptionsContext.bind(this)}], + ['hide', {async: false, handler: this._onApiHide.bind(this)}], + ['isVisible', {async: true, handler: this._onApiIsVisibleAsync.bind(this)}], + ['setVisibleOverride', {async: true, handler: this._onApiSetVisibleOverride.bind(this)}], + ['containsPoint', {async: true, handler: this._onApiContainsPoint.bind(this)}], + ['showContent', {async: true, handler: this._onApiShowContent.bind(this)}], + ['setCustomCss', {async: false, handler: this._onApiSetCustomCss.bind(this)}], + ['clearAutoPlayTimer', {async: false, handler: this._onApiClearAutoPlayTimer.bind(this)}], + ['setContentScale', {async: false, handler: this._onApiSetContentScale.bind(this)}], + ['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}] + ])); + apiReceiver.prepare(); + } + + getOrCreatePopup(id=null, parentId=null, depth=null) { + // Find by existing id + if (id !== null) { + const popup = this._popups.get(id); + if (typeof popup !== 'undefined') { + return popup; + } + } + + // Find by existing parent id + let parent = null; + if (parentId !== null) { + parent = this._popups.get(parentId); + if (typeof parent !== 'undefined') { + const popup = parent.child; + if (popup !== null) { + return popup; + } + } else { + parent = null; + } + } + + // New unique id + if (id === null) { + id = yomichan.generateId(16); + } + + // Create new popup + if (parent !== null) { + if (depth !== null) { + throw new Error('Depth cannot be set when parent exists'); + } + depth = parent.depth + 1; + } else if (depth === null) { + depth = 0; + } + const popup = new Popup(id, depth, this._frameId); + if (parent !== null) { + popup.setParent(parent); + } + this._popups.set(id, popup); + return popup; + } + + // API message handlers + + _onApiGetOrCreatePopup({id, parentId}) { + const popup = this.getOrCreatePopup(id, parentId); + return { + id: popup.id + }; + } + + async _onApiSetOptionsContext({id, optionsContext, source}) { + const popup = this._getPopup(id); + return await popup.setOptionsContext(optionsContext, source); + } + + _onApiHide({id, changeFocus}) { + const popup = this._getPopup(id); + return popup.hide(changeFocus); + } + + async _onApiIsVisibleAsync({id}) { + const popup = this._getPopup(id); + return await popup.isVisible(); + } + + async _onApiSetVisibleOverride({id, visible}) { + const popup = this._getPopup(id); + return await popup.setVisibleOverride(visible); + } + + async _onApiContainsPoint({id, x, y}) { + const popup = this._getPopup(id); + [x, y] = this._convertPopupPointToRootPagePoint(popup, x, y); + return await popup.containsPoint(x, y); + } + + async _onApiShowContent({id, elementRect, writingMode, type, details, context}) { + const popup = this._getPopup(id); + elementRect = this._convertJsonRectToDOMRect(popup, elementRect); + if (!this._popupCanShow(popup)) { return; } + return await popup.showContent(elementRect, writingMode, type, details, context); + } + + _onApiSetCustomCss({id, css}) { + const popup = this._getPopup(id); + return popup.setCustomCss(css); + } + + _onApiClearAutoPlayTimer({id}) { + const popup = this._getPopup(id); + return popup.clearAutoPlayTimer(); + } + + _onApiSetContentScale({id, scale}) { + const popup = this._getPopup(id); + return popup.setContentScale(scale); + } + + _onApiGetUrl() { + return window.location.href; + } + + // Private functions + + _getPopup(id) { + const popup = this._popups.get(id); + if (typeof popup === 'undefined') { + throw new Error(`Invalid popup ID ${id}`); + } + return popup; + } + + _convertJsonRectToDOMRect(popup, jsonRect) { + const [x, y] = this._convertPopupPointToRootPagePoint(popup, jsonRect.x, jsonRect.y); + return new DOMRect(x, y, jsonRect.width, jsonRect.height); + } + + _convertPopupPointToRootPagePoint(popup, x, y) { + if (popup.parent !== null) { + const popupRect = popup.parent.getContainerRect(); + x += popupRect.x; + y += popupRect.y; + } + return [x, y]; + } + + _popupCanShow(popup) { + return popup.parent === null || popup.parent.isVisibleSync(); + } +} diff --git a/ext/fg/js/popup-proxy-host.js b/ext/fg/js/popup-proxy-host.js deleted file mode 100644 index 39150bc7..00000000 --- a/ext/fg/js/popup-proxy-host.js +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2019-2020 Yomichan 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 . - */ - -/* global - * FrontendApiReceiver - * Popup - * apiFrameInformationGet - */ - -class PopupProxyHost { - constructor() { - this._popups = new Map(); - this._frameId = null; - } - - // Public functions - - async prepare() { - const {frameId} = await apiFrameInformationGet(); - if (typeof frameId !== 'number') { return; } - this._frameId = frameId; - - const apiReceiver = new FrontendApiReceiver(`popup-proxy-host#${this._frameId}`, new Map([ - ['getOrCreatePopup', this._onApiGetOrCreatePopup.bind(this)], - ['setOptionsContext', this._onApiSetOptionsContext.bind(this)], - ['hide', this._onApiHide.bind(this)], - ['isVisible', this._onApiIsVisibleAsync.bind(this)], - ['setVisibleOverride', this._onApiSetVisibleOverride.bind(this)], - ['containsPoint', this._onApiContainsPoint.bind(this)], - ['showContent', this._onApiShowContent.bind(this)], - ['setCustomCss', this._onApiSetCustomCss.bind(this)], - ['clearAutoPlayTimer', this._onApiClearAutoPlayTimer.bind(this)], - ['setContentScale', this._onApiSetContentScale.bind(this)], - ['getHostUrl', this._onApiGetHostUrl.bind(this)] - ])); - apiReceiver.prepare(); - } - - getOrCreatePopup(id=null, parentId=null, depth=null) { - // Find by existing id - if (id !== null) { - const popup = this._popups.get(id); - if (typeof popup !== 'undefined') { - return popup; - } - } - - // Find by existing parent id - let parent = null; - if (parentId !== null) { - parent = this._popups.get(parentId); - if (typeof parent !== 'undefined') { - const popup = parent.child; - if (popup !== null) { - return popup; - } - } else { - parent = null; - } - } - - // New unique id - if (id === null) { - id = yomichan.generateId(16); - } - - // Create new popup - if (parent !== null) { - if (depth !== null) { - throw new Error('Depth cannot be set when parent exists'); - } - depth = parent.depth + 1; - } else if (depth === null) { - depth = 0; - } - const popup = new Popup(id, depth, this._frameId); - if (parent !== null) { - popup.setParent(parent); - } - this._popups.set(id, popup); - return popup; - } - - // API message handlers - - async _onApiGetOrCreatePopup({id, parentId}) { - const popup = this.getOrCreatePopup(id, parentId); - return { - id: popup.id - }; - } - - async _onApiSetOptionsContext({id, optionsContext, source}) { - const popup = this._getPopup(id); - return await popup.setOptionsContext(optionsContext, source); - } - - async _onApiHide({id, changeFocus}) { - const popup = this._getPopup(id); - return popup.hide(changeFocus); - } - - async _onApiIsVisibleAsync({id}) { - const popup = this._getPopup(id); - return await popup.isVisible(); - } - - async _onApiSetVisibleOverride({id, visible}) { - const popup = this._getPopup(id); - return await popup.setVisibleOverride(visible); - } - - async _onApiContainsPoint({id, x, y}) { - const popup = this._getPopup(id); - [x, y] = PopupProxyHost._convertPopupPointToRootPagePoint(popup, x, y); - return await popup.containsPoint(x, y); - } - - async _onApiShowContent({id, elementRect, writingMode, type, details, context}) { - const popup = this._getPopup(id); - elementRect = PopupProxyHost._convertJsonRectToDOMRect(popup, elementRect); - if (!PopupProxyHost._popupCanShow(popup)) { return; } - return await popup.showContent(elementRect, writingMode, type, details, context); - } - - async _onApiSetCustomCss({id, css}) { - const popup = this._getPopup(id); - return popup.setCustomCss(css); - } - - async _onApiClearAutoPlayTimer({id}) { - const popup = this._getPopup(id); - return popup.clearAutoPlayTimer(); - } - - async _onApiSetContentScale({id, scale}) { - const popup = this._getPopup(id); - return popup.setContentScale(scale); - } - - async _onApiGetHostUrl() { - return window.location.href; - } - - // Private functions - - _getPopup(id) { - const popup = this._popups.get(id); - if (typeof popup === 'undefined') { - throw new Error(`Invalid popup ID ${id}`); - } - return popup; - } - - static _convertJsonRectToDOMRect(popup, jsonRect) { - const [x, y] = PopupProxyHost._convertPopupPointToRootPagePoint(popup, jsonRect.x, jsonRect.y); - return new DOMRect(x, y, jsonRect.width, jsonRect.height); - } - - static _convertPopupPointToRootPagePoint(popup, x, y) { - if (popup.parent !== null) { - const popupRect = popup.parent.getContainerRect(); - x += popupRect.x; - y += popupRect.y; - } - return [x, y]; - } - - static _popupCanShow(popup) { - return popup.parent === null || popup.parent.isVisibleSync(); - } -} diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js index 93418202..6a84e000 100644 --- a/ext/fg/js/popup-proxy.js +++ b/ext/fg/js/popup-proxy.js @@ -51,7 +51,7 @@ class PopupProxy { // Public functions async prepare() { - const {id} = await this._invokeHostApi('getOrCreatePopup', {id: this._id, parentId: this._parentId}); + const {id} = await this._invoke('getOrCreatePopup', {id: this._id, parentId: this._parentId}); this._id = id; } @@ -60,19 +60,19 @@ class PopupProxy { } async setOptionsContext(optionsContext, source) { - return await this._invokeHostApi('setOptionsContext', {id: this._id, optionsContext, source}); + return await this._invoke('setOptionsContext', {id: this._id, optionsContext, source}); } hide(changeFocus) { - this._invokeHostApi('hide', {id: this._id, changeFocus}); + this._invoke('hide', {id: this._id, changeFocus}); } async isVisible() { - return await this._invokeHostApi('isVisible', {id: this._id}); + return await this._invoke('isVisible', {id: this._id}); } setVisibleOverride(visible) { - this._invokeHostApi('setVisibleOverride', {id: this._id, visible}); + this._invoke('setVisibleOverride', {id: this._id, visible}); } async containsPoint(x, y) { @@ -80,7 +80,7 @@ class PopupProxy { await this._updateFrameOffset(); [x, y] = this._applyFrameOffset(x, y); } - return await this._invokeHostApi('containsPoint', {id: this._id, x, y}); + return await this._invoke('containsPoint', {id: this._id, x, y}); } async showContent(elementRect, writingMode, type, details, context) { @@ -90,32 +90,32 @@ class PopupProxy { [x, y] = this._applyFrameOffset(x, y); } elementRect = {x, y, width, height}; - return await this._invokeHostApi('showContent', {id: this._id, elementRect, writingMode, type, details, context}); + return await this._invoke('showContent', {id: this._id, elementRect, writingMode, type, details, context}); } - async setCustomCss(css) { - return await this._invokeHostApi('setCustomCss', {id: this._id, css}); + setCustomCss(css) { + this._invoke('setCustomCss', {id: this._id, css}); } clearAutoPlayTimer() { - this._invokeHostApi('clearAutoPlayTimer', {id: this._id}); + this._invoke('clearAutoPlayTimer', {id: this._id}); } - async setContentScale(scale) { - this._invokeHostApi('setContentScale', {id: this._id, scale}); + setContentScale(scale) { + this._invoke('setContentScale', {id: this._id, scale}); } - async getHostUrl() { - return await this._invokeHostApi('getHostUrl', {}); + async getUrl() { + return await this._invoke('getUrl', {}); } // Private - _invokeHostApi(action, params={}) { + _invoke(action, params={}) { if (typeof this._parentFrameId !== 'number') { return Promise.reject(new Error('Invalid frame')); } - return this._apiSender.invoke(action, params, `popup-proxy-host#${this._parentFrameId}`); + return this._apiSender.invoke(action, params, `popup-factory#${this._parentFrameId}`); } async _updateFrameOffset() { diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js index 7db53f0d..79b37251 100644 --- a/ext/fg/js/popup.js +++ b/ext/fg/js/popup.js @@ -139,7 +139,7 @@ class Popup { this._invokeApi('setContent', {type, details}); } - async setCustomCss(css) { + setCustomCss(css) { this._invokeApi('setCustomCss', {css}); } diff --git a/ext/manifest.json b/ext/manifest.json index 3cb634f0..80823fc4 100644 --- a/ext/manifest.json +++ b/ext/manifest.json @@ -44,9 +44,9 @@ "fg/js/frontend-api-receiver.js", "fg/js/popup.js", "fg/js/source.js", + "fg/js/popup-factory.js", "fg/js/frame-offset-forwarder.js", "fg/js/popup-proxy.js", - "fg/js/popup-proxy-host.js", "fg/js/frontend.js", "fg/js/content-script-main.js" ], -- cgit v1.2.3