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
This commit is contained in:
siikamiika 2020-04-26 22:33:50 +03:00 committed by GitHub
parent a49e4ccc4e
commit ca033a87a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 154 additions and 79 deletions

View File

@ -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,

View File

@ -19,7 +19,6 @@
* SettingsPopupPreview
*/
(async () => {
const instance = new SettingsPopupPreview();
await instance.prepare();
(() => {
new SettingsPopupPreview();
})();

View File

@ -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)]
]);
window.addEventListener('message', this.onMessage.bind(this), false);
}
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; }

View File

@ -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', '');

View File

@ -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() {

View File

@ -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 {

View File

@ -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`;
}

View File

@ -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());
}
}

View File

@ -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) {

View File

@ -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={}) {

View File

@ -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
});

View File

@ -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) {