Extension unload indication fix (#662)

* Remove unused function

* Rename field

* Change extensionUnloaded trigger function

* Update how extension unloaded content is shown

* Ignore certain errors caused by extension unload

* Add _showExtensionUnloaded function

* Wrap internals of updateOptions

* Suppress errors caued by extension unload

* Make the frontend trigger the popup's extensionUnloaded event
This commit is contained in:
toasted-nutbread 2020-07-18 14:15:36 -04:00 committed by GitHub
parent f9c76efea0
commit dac33e6961
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 55 deletions

View File

@ -31,7 +31,7 @@ class DisplayFloat extends Display {
this._nestedPopupsPrepared = false; this._nestedPopupsPrepared = false;
this._ownerFrameId = null; this._ownerFrameId = null;
this._frameEndpoint = new FrameEndpoint(); this._frameEndpoint = new FrameEndpoint();
this._windowMessageHandlers = new Map([ this._messageHandlers = new Map([
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}], ['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}], ['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.bind(this)}],
['setContent', {async: false, handler: this._onMessageSetContent.bind(this)}], ['setContent', {async: false, handler: this._onMessageSetContent.bind(this)}],
@ -39,6 +39,9 @@ class DisplayFloat extends Display {
['setCustomCss', {async: false, handler: this._onMessageSetCustomCss.bind(this)}], ['setCustomCss', {async: false, handler: this._onMessageSetCustomCss.bind(this)}],
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}] ['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
]); ]);
this._windowMessageHandlers = new Map([
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
]);
this.registerActions([ this.registerActions([
['copy-host-selection', () => this._copySelection()] ['copy-host-selection', () => this._copySelection()]
@ -54,6 +57,7 @@ class DisplayFloat extends Display {
api.crossFrame.registerHandlers([ api.crossFrame.registerHandlers([
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}] ['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
]); ]);
window.addEventListener('message', this._onWindowMessage.bind(this), false);
this._frameEndpoint.signal(); this._frameEndpoint.signal();
} }
@ -107,7 +111,7 @@ class DisplayFloat extends Display {
} }
const {action, params} = data.data; const {action, params} = data.data;
const handlerInfo = this._windowMessageHandlers.get(action); const handlerInfo = this._messageHandlers.get(action);
if (typeof handlerInfo === 'undefined') { if (typeof handlerInfo === 'undefined') {
throw new Error(`Invalid action: ${action}`); throw new Error(`Invalid action: ${action}`);
} }
@ -117,6 +121,18 @@ class DisplayFloat extends Display {
return {async, result}; return {async, result};
} }
_onWindowMessage(e) {
const data = e.data;
if (!this._frameEndpoint.authenticate(data)) { return; }
const {action, params} = data.data;
const messageHandler = this._windowMessageHandlers.get(action);
if (typeof messageHandler === 'undefined') { return; }
const callback = () => {}; // NOP
yomichan.invokeMessageHandler(messageHandler, params, callback);
}
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); this.setOptionsContext(optionsContext);
@ -152,6 +168,11 @@ class DisplayFloat extends Display {
this._setContentScale(scale); this._setContentScale(scale);
} }
_onMessageExtensionUnloaded() {
if (yomichan.isExtensionUnloaded) { return; }
yomichan.triggerExtensionUnloaded();
}
// Private // Private
_copySelection() { _copySelection() {

View File

@ -147,26 +147,13 @@ class Frontend {
} }
async updateOptions() { async updateOptions() {
const optionsContext = await this.getOptionsContext(); try {
this._options = await api.optionsGet(optionsContext); await this._updateOptionsInternal();
} catch (e) {
await this._updatePopup(); if (!yomichan.isExtensionUnloaded) {
throw e;
this._textScanner.setOptions(this._options); }
this._updateTextScannerEnabled(); this._showExtensionUnloaded(null);
const ignoreNodes = ['.scan-disable', '.scan-disable *'];
if (!this._options.scanning.enableOnPopupExpressions) {
ignoreNodes.push('.source-text', '.source-text *');
}
this._textScanner.ignoreNodes = ignoreNodes.join(',');
this._updateContentScale();
const textSourceCurrent = this._textScanner.getCurrentTextSource();
const causeCurrent = this._textScanner.causeCurrent;
if (textSourceCurrent !== null && causeCurrent !== null) {
await this._search(textSourceCurrent, causeCurrent);
} }
} }
@ -243,6 +230,30 @@ class Frontend {
await this.updateOptions(); await this.updateOptions();
} }
async _updateOptionsInternal() {
const optionsContext = await this.getOptionsContext();
this._options = await api.optionsGet(optionsContext);
await this._updatePopup();
this._textScanner.setOptions(this._options);
this._updateTextScannerEnabled();
const ignoreNodes = ['.scan-disable', '.scan-disable *'];
if (!this._options.scanning.enableOnPopupExpressions) {
ignoreNodes.push('.source-text', '.source-text *');
}
this._textScanner.ignoreNodes = ignoreNodes.join(',');
this._updateContentScale();
const textSourceCurrent = this._textScanner.getCurrentTextSource();
const causeCurrent = this._textScanner.causeCurrent;
if (textSourceCurrent !== null && causeCurrent !== null) {
await this._search(textSourceCurrent, causeCurrent);
}
}
async _updatePopup() { async _updatePopup() {
const showIframePopupsInRootFrame = this._options.general.showIframePopupsInRootFrame; const showIframePopupsInRootFrame = this._options.general.showIframePopupsInRootFrame;
const isIframe = !this._useProxyPopup && (window !== window.parent); const isIframe = !this._useProxyPopup && (window !== window.parent);
@ -328,8 +339,15 @@ class Frontend {
return this._popup === null || this._popup.isProxy() ? [] : [this._popup.getContainer()]; return this._popup === null || this._popup.isProxy() ? [] : [this._popup.getContainer()];
} }
_ignorePoint(x, y) { async _ignorePoint(x, y) {
return this._popup !== null && this._popup.containsPoint(x, y); try {
return this._popup !== null && await this._popup.containsPoint(x, y);
} catch (e) {
if (!yomichan.isExtensionUnloaded) {
throw e;
}
return false;
}
} }
async _search(textSource, cause) { async _search(textSource, cause) {
@ -352,7 +370,7 @@ class Frontend {
} catch (e) { } catch (e) {
if (yomichan.isExtensionUnloaded) { if (yomichan.isExtensionUnloaded) {
if (textSource !== null && this._options.scanning.modifier !== 'none') { if (textSource !== null && this._options.scanning.modifier !== 'none') {
this._showPopupContent(textSource, await this.getOptionsContext(), 'extensionUnloaded'); this._showExtensionUnloaded(textSource);
} }
} else { } else {
yomichan.logError(e); yomichan.logError(e);
@ -392,6 +410,14 @@ class Frontend {
return {definitions, type: 'kanji'}; return {definitions, type: 'kanji'};
} }
async _showExtensionUnloaded(textSource) {
if (textSource === null) {
textSource = this._textScanner.getCurrentTextSource();
if (textSource === null) { return; }
}
this._showPopupContent(textSource, await this.getOptionsContext());
}
_showContent(textSource, focus, definitions, type, optionsContext) { _showContent(textSource, focus, definitions, type, optionsContext) {
const {url} = optionsContext; const {url} = optionsContext;
const sentenceExtent = this._options.anki.sentenceExt; const sentenceExtent = this._options.anki.sentenceExt;
@ -414,6 +440,10 @@ class Frontend {
details, details,
context context
); );
this._lastShowPromise.catch((error) => {
if (yomichan.isExtensionUnloaded) { return; }
yomichan.logError(error);
});
return this._lastShowPromise; return this._lastShowPromise;
} }

View File

@ -69,7 +69,11 @@ class PopupProxy extends EventDispatcher {
} }
async isVisible() { async isVisible() {
return await this._invoke('isVisible', {id: this._id}); try {
return await this._invoke('isVisible', {id: this._id});
} catch (e) {
return false;
}
} }
setVisibleOverride(visible) { setVisibleOverride(visible) {
@ -98,8 +102,12 @@ class PopupProxy extends EventDispatcher {
this._invoke('setCustomCss', {id: this._id, css}); this._invoke('setCustomCss', {id: this._id, css});
} }
clearAutoPlayTimer() { async clearAutoPlayTimer() {
this._invoke('clearAutoPlayTimer', {id: this._id}); try {
await this._invoke('clearAutoPlayTimer', {id: this._id});
} catch (e) {
// NOP
}
} }
setContentScale(scale) { setContentScale(scale) {

View File

@ -83,6 +83,7 @@ class Popup {
this._frame.addEventListener('mousedown', (e) => e.stopPropagation()); this._frame.addEventListener('mousedown', (e) => e.stopPropagation());
this._frame.addEventListener('scroll', (e) => e.stopPropagation()); this._frame.addEventListener('scroll', (e) => e.stopPropagation());
this._frame.addEventListener('load', this._onFrameLoad.bind(this)); this._frame.addEventListener('load', this._onFrameLoad.bind(this));
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
} }
isProxy() { isProxy() {
@ -149,8 +150,12 @@ class Popup {
this._invokeApi('setCustomCss', {css}); this._invokeApi('setCustomCss', {css});
} }
clearAutoPlayTimer() { async clearAutoPlayTimer() {
this._invokeApi('clearAutoPlayTimer'); try {
await this._invokeApi('clearAutoPlayTimer');
} catch (e) {
// NOP
}
} }
setContentScale(scale) { setContentScale(scale) {
@ -447,6 +452,18 @@ class Popup {
return await api.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message); return await api.crossFrame.invoke(this._frameClient.frameId, 'popupMessage', message);
} }
_invokeWindowApi(action, params={}) {
const contentWindow = this._frame.contentWindow;
if (this._frameClient === null || !this._frameClient.isConnected() || contentWindow === null) { return; }
const message = this._frameClient.createMessage({action, params});
contentWindow.postMessage(message, this._targetOrigin);
}
_onExtensionUnloaded() {
this._invokeWindowApi('extensionUnloaded');
}
_getFrameParentElement() { _getFrameParentElement() {
const defaultParent = document.body; const defaultParent = document.body;
const fullscreenElement = DOM.getFullscreenElement(); const fullscreenElement = DOM.getFullscreenElement();
@ -636,15 +653,4 @@ class Popup {
bottom: window.innerHeight bottom: window.innerHeight
}; };
} }
static isFrameAboutBlank(frame) {
try {
const contentDocument = frame.contentDocument;
if (contentDocument === null) { return false; }
const url = contentDocument.location.href;
return /^about:blank(?:[#?]|$)/.test(url);
} catch (e) {
return false;
}
}
} }

View File

@ -97,14 +97,12 @@ class Display {
this._setInteractive(true); this._setInteractive(true);
await yomichan.ready(); await yomichan.ready();
await this._displayGenerator.prepare(); await this._displayGenerator.prepare();
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
} }
onError(error) { onError(error) {
if (yomichan.isExtensionUnloaded) { if (yomichan.isExtensionUnloaded) { return; }
this.setContent('extensionUnloaded'); yomichan.logError(error);
} else {
yomichan.logError(error);
}
} }
onEscape() { onEscape() {
@ -176,9 +174,6 @@ class Display {
case 'kanji': case 'kanji':
await this._setContentKanji(details.definitions, details.context, token); await this._setContentKanji(details.definitions, details.context, token);
break; break;
case 'extensionUnloaded':
this._setContentExtensionUnloaded();
break;
} }
} catch (e) { } catch (e) {
this.onError(e); this.onError(e);
@ -236,6 +231,10 @@ class Display {
// Private // Private
_onExtensionUnloaded() {
this._setContentExtensionUnloaded();
}
_onSourceTermView(e) { _onSourceTermView(e) {
e.preventDefault(); e.preventDefault();
this._sourceTermView(); this._sourceTermView();

View File

@ -196,7 +196,7 @@ const yomichan = (() => {
try { try {
return chrome.runtime.sendMessage(...args); return chrome.runtime.sendMessage(...args);
} catch (e) { } catch (e) {
this._onExtensionUnloaded(e); this.triggerExtensionUnloaded();
throw e; throw e;
} }
} }
@ -205,7 +205,7 @@ const yomichan = (() => {
try { try {
return chrome.runtime.connect(...args); return chrome.runtime.connect(...args);
} catch (e) { } catch (e) {
this._onExtensionUnloaded(e); this.triggerExtensionUnloaded();
throw e; throw e;
} }
} }
@ -247,13 +247,13 @@ const yomichan = (() => {
} }
} }
// Private triggerExtensionUnloaded() {
_onExtensionUnloaded(error) {
this._isExtensionUnloaded = true; this._isExtensionUnloaded = true;
this.trigger('extensionUnloaded', {error}); this.trigger('extensionUnloaded');
} }
// Private
_getUrl() { _getUrl() {
return (typeof window === 'object' && window !== null ? window.location.href : ''); return (typeof window === 'object' && window !== null ? window.location.href : '');
} }