Text scanning options propagation (#1020)

* Update Display.setOptionsContext to update options

* Update how options context is updated in Popup

* Omit optionsContext for some _showPopupContent calls

* Remove extension unload

* Disable modifier keys in frontend's options context

* Update how text scanner passes modifiers to options context

* Update how options context is passed to display

* Update how display uses options context
This commit is contained in:
toasted-nutbread 2020-11-12 20:32:46 -05:00 committed by GitHub
parent 219dfb4917
commit f2ad94e54f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 70 deletions

View File

@ -61,11 +61,6 @@ class DisplayFloat extends Display {
this._invokeOwner('closePopup'); this._invokeOwner('closePopup');
} }
async setOptionsContext(optionsContext) {
super.setOptionsContext(optionsContext);
await this.updateOptions();
}
async getDocumentTitle() { async getDocumentTitle() {
try { try {
const targetFrameId = 0; const targetFrameId = 0;
@ -99,9 +94,7 @@ class DisplayFloat extends Display {
async _onMessageConfigure({frameId, ownerFrameId, popupId, optionsContext, childrenSupported, scale}) { async _onMessageConfigure({frameId, ownerFrameId, popupId, optionsContext, childrenSupported, scale}) {
this.ownerFrameId = ownerFrameId; this.ownerFrameId = ownerFrameId;
this.setOptionsContext(optionsContext); await this.setOptionsContext(optionsContext);
await this.updateOptions();
if (childrenSupported && !this._nestedPopupsPrepared) { if (childrenSupported && !this._nestedPopupsPrepared) {
const {depth} = optionsContext; const {depth} = optionsContext;

View File

@ -33,21 +33,18 @@ class Frontend {
pageType, pageType,
allowRootFramePopupProxy allowRootFramePopupProxy
}) { }) {
this._id = generateId(16);
this._popup = null; this._popup = null;
this._disabledOverride = false; this._disabledOverride = false;
this._options = null; this._options = null;
this._pageZoomFactor = 1.0; this._pageZoomFactor = 1.0;
this._contentScale = 1.0; this._contentScale = 1.0;
this._lastShowPromise = Promise.resolve(); this._lastShowPromise = Promise.resolve();
this._activeModifiers = new Set();
this._optionsUpdatePending = false;
this._documentUtil = new DocumentUtil(); this._documentUtil = new DocumentUtil();
this._textScanner = new TextScanner({ this._textScanner = new TextScanner({
node: window, node: window,
ignoreElements: this._ignoreElements.bind(this), ignoreElements: this._ignoreElements.bind(this),
ignorePoint: this._ignorePoint.bind(this), ignorePoint: this._ignorePoint.bind(this),
getOptionsContext: this._getUpToDateOptionsContext.bind(this), getOptionsContext: this._getOptionsContext.bind(this),
documentUtil: this._documentUtil, documentUtil: this._documentUtil,
searchTerms: true, searchTerms: true,
searchKanji: true searchKanji: true
@ -112,7 +109,6 @@ class Frontend {
chrome.runtime.onMessage.addListener(this._onRuntimeMessage.bind(this)); chrome.runtime.onMessage.addListener(this._onRuntimeMessage.bind(this));
this._textScanner.on('clearSelection', this._onClearSelection.bind(this)); this._textScanner.on('clearSelection', this._onClearSelection.bind(this));
this._textScanner.on('activeModifiersChanged', this._onActiveModifiersChanged.bind(this));
this._textScanner.on('searched', this._onSearched.bind(this)); this._textScanner.on('searched', this._onSearched.bind(this));
api.crossFrame.registerHandlers([ api.crossFrame.registerHandlers([
@ -148,7 +144,6 @@ class Frontend {
if (!yomichan.isExtensionUnloaded) { if (!yomichan.isExtensionUnloaded) {
throw e; throw e;
} }
this._showExtensionUnloaded(null);
} }
} }
@ -236,18 +231,6 @@ class Frontend {
this._popup.clearAutoPlayTimer(); this._popup.clearAutoPlayTimer();
this._isPointerOverPopup = false; this._isPointerOverPopup = false;
} }
this._updatePendingOptions();
}
async _onActiveModifiersChanged({modifiers}) {
modifiers = new Set(modifiers);
if (areSetsEqual(modifiers, this._activeModifiers)) { return; }
this._activeModifiers = modifiers;
if (this._popup !== null && await this._popup.isVisible()) {
this._optionsUpdatePending = true;
return;
}
await this.updateOptions();
} }
_onSearched({type, definitions, sentence, inputInfo: {cause, empty}, textSource, optionsContext, error}) { _onSearched({type, definitions, sentence, inputInfo: {cause, empty}, textSource, optionsContext, error}) {
@ -386,7 +369,7 @@ class Frontend {
const optionsContext = await this._getOptionsContext(); const optionsContext = await this._getOptionsContext();
if (this._updatePopupToken !== token) { return; } if (this._updatePopupToken !== token) { return; }
if (popup !== null) { if (popup !== null) {
await popup.setOptionsContext(optionsContext, this._id); await popup.setOptionsContext(optionsContext);
} }
if (this._updatePopupToken !== token) { return; } if (this._updatePopupToken !== token) { return; }
@ -481,16 +464,15 @@ class Frontend {
} }
} }
async _showExtensionUnloaded(textSource) { _showExtensionUnloaded(textSource) {
if (textSource === null) { if (textSource === null) {
textSource = this._textScanner.getCurrentTextSource(); textSource = this._textScanner.getCurrentTextSource();
if (textSource === null) { return; } if (textSource === null) { return; }
} }
this._showPopupContent(textSource, await this._getOptionsContext()); this._showPopupContent(textSource, null);
} }
_showContent(textSource, focus, definitions, type, sentence, optionsContext) { _showContent(textSource, focus, definitions, type, sentence, optionsContext) {
const {url} = optionsContext;
const query = textSource.text(); const query = textSource.text();
const details = { const details = {
focus, focus,
@ -503,7 +485,7 @@ class Frontend {
state: { state: {
focusEntry: 0, focusEntry: 0,
sentence, sentence,
url optionsContext
}, },
content: { content: {
definitions definitions
@ -521,7 +503,6 @@ class Frontend {
this._popup !== null ? this._popup !== null ?
this._popup.showContent( this._popup.showContent(
{ {
source: this._id,
optionsContext, optionsContext,
elementRect: textSource.getRect(), elementRect: textSource.getRect(),
writingMode: textSource.getWritingMode() writingMode: textSource.getWritingMode()
@ -537,13 +518,6 @@ class Frontend {
return this._lastShowPromise; return this._lastShowPromise;
} }
async _updatePendingOptions() {
if (this._optionsUpdatePending) {
this._optionsUpdatePending = false;
await this.updateOptions();
}
}
_updateTextScannerEnabled() { _updateTextScannerEnabled() {
const enabled = ( const enabled = (
this._options.general.enable && this._options.general.enable &&
@ -580,7 +554,7 @@ class Frontend {
this._popup !== null && this._popup !== null &&
await this._popup.isVisible() await this._popup.isVisible()
) { ) {
this._showPopupContent(textSource, await this._getOptionsContext()); this._showPopupContent(textSource, null);
} }
} }
@ -610,11 +584,6 @@ class Frontend {
await promise; await promise;
} }
async _getUpToDateOptionsContext() {
await this._updatePendingOptions();
return await this._getOptionsContext();
}
_getPreventMiddleMouseValueForPageType(preventMiddleMouseOptions) { _getPreventMiddleMouseValueForPageType(preventMiddleMouseOptions) {
switch (this._pageType) { switch (this._pageType) {
case 'web': return preventMiddleMouseOptions.onWebPages; case 'web': return preventMiddleMouseOptions.onWebPages;
@ -639,7 +608,6 @@ class Frontend {
} }
const depth = this._depth; const depth = this._depth;
const modifierKeys = [...this._activeModifiers]; return {depth, url};
return {depth, url, modifierKeys};
} }
} }

View File

@ -39,7 +39,6 @@ class Popup extends EventDispatcher {
this._optionsContext = null; this._optionsContext = null;
this._contentScale = 1.0; this._contentScale = 1.0;
this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, ''); this._targetOrigin = chrome.runtime.getURL('/').replace(/\/$/, '');
this._previousOptionsContextSource = null;
this._frameSizeContentScale = null; this._frameSizeContentScale = null;
this._frameClient = null; this._frameClient = null;
@ -105,14 +104,10 @@ class Popup extends EventDispatcher {
this._onVisibleChange({value: this.isVisibleSync()}); this._onVisibleChange({value: this.isVisibleSync()});
} }
async setOptionsContext(optionsContext, source) { async setOptionsContext(optionsContext) {
this._optionsContext = optionsContext; this._optionsContext = optionsContext;
this._previousOptionsContextSource = source;
this._options = await api.optionsGet(optionsContext); this._options = await api.optionsGet(optionsContext);
this.updateTheme(); this.updateTheme();
this._invokeSafe('setOptionsContext', {optionsContext});
} }
hide(changeFocus) { hide(changeFocus) {
@ -154,9 +149,9 @@ class Popup extends EventDispatcher {
async showContent(details, displayDetails) { async showContent(details, displayDetails) {
if (this._options === null) { throw new Error('Options not assigned'); } if (this._options === null) { throw new Error('Options not assigned'); }
const {source, optionsContext, elementRect, writingMode} = details; const {optionsContext, elementRect, writingMode} = details;
if (typeof source !== 'undefined' && source !== this._previousOptionsContextSource) { if (optionsContext !== null) {
await this.setOptionsContext(optionsContext, source); await this._setOptionsContextIfDifferent(optionsContext);
} }
if (typeof elementRect !== 'undefined' && typeof writingMode !== 'undefined') { if (typeof elementRect !== 'undefined' && typeof writingMode !== 'undefined') {
@ -659,4 +654,9 @@ class Popup extends EventDispatcher {
bottom: window.innerHeight bottom: window.innerHeight
}; };
} }
async _setOptionsContextIfDifferent(optionsContext) {
if (deepEqual(this._optionsContext, optionsContext)) { return; }
await this.setOptionsContext(optionsContext);
}
} }

View File

@ -227,8 +227,9 @@ class Display extends EventDispatcher {
return this._optionsContext; return this._optionsContext;
} }
setOptionsContext(optionsContext) { async setOptionsContext(optionsContext) {
this._optionsContext = optionsContext; this._optionsContext = optionsContext;
await this.updateOptions();
} }
async updateOptions() { async updateOptions() {
@ -507,7 +508,7 @@ class Display extends EventDispatcher {
} }
} }
_onQueryParserSearch({type, definitions, sentence, inputInfo: {cause}, textSource}) { _onQueryParserSearch({type, definitions, sentence, inputInfo: {cause}, textSource, optionsContext}) {
const query = textSource.text(); const query = textSource.text();
const history = (cause === 'click'); const history = (cause === 'click');
const details = { const details = {
@ -516,7 +517,7 @@ class Display extends EventDispatcher {
params: this._createSearchParams(type, query, false), params: this._createSearchParams(type, query, false),
state: { state: {
sentence, sentence,
url: window.location.href optionsContext
}, },
content: { content: {
definitions definitions
@ -570,7 +571,7 @@ class Display extends EventDispatcher {
state: { state: {
focusEntry: 0, focusEntry: 0,
sentence: state.sentence, sentence: state.sentence,
url: state.url optionsContext: state.optionsContext
}, },
content: { content: {
definitions definitions
@ -629,7 +630,7 @@ class Display extends EventDispatcher {
state: { state: {
focusEntry: 0, focusEntry: 0,
sentence, sentence,
url: state.url optionsContext: state.optionsContext
}, },
content: { content: {
definitions definitions
@ -779,8 +780,7 @@ class Display extends EventDispatcher {
} }
} }
async _findDefinitions(isTerms, source, urlSearchParams) { async _findDefinitions(isTerms, source, urlSearchParams, optionsContext) {
const optionsContext = this.getOptionsContext();
if (isTerms) { if (isTerms) {
const findDetails = {}; const findDetails = {};
if (urlSearchParams.get('wildcards') !== 'off') { if (urlSearchParams.get('wildcards') !== 'off') {
@ -821,6 +821,16 @@ class Display extends EventDispatcher {
changeHistory = true; changeHistory = true;
} }
let {sentence=null, optionsContext=null, focusEntry=null, scrollX=null, scrollY=null} = state;
if (!(typeof optionsContext === 'object' && optionsContext !== null)) {
optionsContext = this.getOptionsContext();
state.optionsContext = optionsContext;
changeHistory = true;
}
let {url} = optionsContext;
if (typeof url !== 'string') { url = window.location.href; }
sentence = this._getValidSentenceData(sentence);
source = this.postProcessQuery(source); source = this.postProcessQuery(source);
let full = urlSearchParams.get('full'); let full = urlSearchParams.get('full');
full = (full === null ? source : this.postProcessQuery(full)); full = (full === null ? source : this.postProcessQuery(full));
@ -829,12 +839,15 @@ class Display extends EventDispatcher {
let {definitions} = content; let {definitions} = content;
if (!Array.isArray(definitions)) { if (!Array.isArray(definitions)) {
definitions = await this._findDefinitions(isTerms, source, urlSearchParams); definitions = await this._findDefinitions(isTerms, source, urlSearchParams, optionsContext);
if (this._setContentToken !== token) { return true; } if (this._setContentToken !== token) { return true; }
content.definitions = definitions; content.definitions = definitions;
changeHistory = true; changeHistory = true;
} }
await this._setOptionsContextIfDifferent(optionsContext);
if (this._setContentToken !== token) { return true; }
if (changeHistory) { if (changeHistory) {
this._historyStateUpdate(state, content); this._historyStateUpdate(state, content);
} }
@ -843,10 +856,6 @@ class Display extends EventDispatcher {
eventArgs.content = content; eventArgs.content = content;
this.trigger('contentUpdating', eventArgs); this.trigger('contentUpdating', eventArgs);
let {sentence=null, url=null, focusEntry=null, scrollX=null, scrollY=null} = state;
if (typeof url !== 'string') { url = window.location.href; }
sentence = this._getValidSentenceData(sentence);
this._definitions = definitions; this._definitions = definitions;
for (const definition of definitions) { for (const definition of definitions) {
@ -1486,4 +1495,9 @@ class Display extends EventDispatcher {
} }
return true; return true;
} }
async _setOptionsContextIfDifferent(optionsContext) {
if (deepEqual(this._optionsContext, optionsContext)) { return; }
await this.setOptionsContext(optionsContext);
}
} }

View File

@ -238,6 +238,14 @@ class TextScanner extends EventDispatcher {
// Private // Private
async _getOptionsContextForInput(inputInfo) {
const optionsContext = clone(await this._getOptionsContext());
const {modifiers, modifierKeys} = inputInfo;
optionsContext.modifiers = [...modifiers];
optionsContext.modifierKeys = [...modifierKeys];
return optionsContext;
}
async _search(textSource, searchTerms, searchKanji, inputInfo) { async _search(textSource, searchTerms, searchKanji, inputInfo) {
let definitions = null; let definitions = null;
let sentence = null; let sentence = null;
@ -251,7 +259,7 @@ class TextScanner extends EventDispatcher {
return; return;
} }
optionsContext = await this._getOptionsContext(); optionsContext = await this._getOptionsContextForInput(inputInfo);
searched = true; searched = true;
const result = await this._findDefinitions(textSource, searchTerms, searchKanji, optionsContext); const result = await this._findDefinitions(textSource, searchTerms, searchKanji, optionsContext);
@ -810,7 +818,6 @@ class TextScanner extends EventDispatcher {
_getMatchingInputGroupFromEvent(type, cause, event) { _getMatchingInputGroupFromEvent(type, cause, event) {
const modifiers = DocumentUtil.getActiveModifiersAndButtons(event); const modifiers = DocumentUtil.getActiveModifiersAndButtons(event);
const modifierKeys = DocumentUtil.getActiveModifiers(event); const modifierKeys = DocumentUtil.getActiveModifiers(event);
this.trigger('activeModifiersChanged', {modifiers, modifierKeys});
return this._getMatchingInputGroup(type, cause, modifiers, modifierKeys); return this._getMatchingInputGroup(type, cause, modifiers, modifierKeys);
} }