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:
parent
f9c76efea0
commit
dac33e6961
@ -31,7 +31,7 @@ class DisplayFloat extends Display {
|
||||
this._nestedPopupsPrepared = false;
|
||||
this._ownerFrameId = null;
|
||||
this._frameEndpoint = new FrameEndpoint();
|
||||
this._windowMessageHandlers = new Map([
|
||||
this._messageHandlers = new Map([
|
||||
['configure', {async: true, handler: this._onMessageConfigure.bind(this)}],
|
||||
['setOptionsContext', {async: false, handler: this._onMessageSetOptionsContext.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)}],
|
||||
['setContentScale', {async: false, handler: this._onMessageSetContentScale.bind(this)}]
|
||||
]);
|
||||
this._windowMessageHandlers = new Map([
|
||||
['extensionUnloaded', {async: false, handler: this._onMessageExtensionUnloaded.bind(this)}]
|
||||
]);
|
||||
|
||||
this.registerActions([
|
||||
['copy-host-selection', () => this._copySelection()]
|
||||
@ -54,6 +57,7 @@ class DisplayFloat extends Display {
|
||||
api.crossFrame.registerHandlers([
|
||||
['popupMessage', {async: 'dynamic', handler: this._onMessage.bind(this)}]
|
||||
]);
|
||||
window.addEventListener('message', this._onWindowMessage.bind(this), false);
|
||||
|
||||
this._frameEndpoint.signal();
|
||||
}
|
||||
@ -107,7 +111,7 @@ class DisplayFloat extends Display {
|
||||
}
|
||||
|
||||
const {action, params} = data.data;
|
||||
const handlerInfo = this._windowMessageHandlers.get(action);
|
||||
const handlerInfo = this._messageHandlers.get(action);
|
||||
if (typeof handlerInfo === 'undefined') {
|
||||
throw new Error(`Invalid action: ${action}`);
|
||||
}
|
||||
@ -117,6 +121,18 @@ class DisplayFloat extends Display {
|
||||
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}) {
|
||||
this._ownerFrameId = ownerFrameId;
|
||||
this.setOptionsContext(optionsContext);
|
||||
@ -152,6 +168,11 @@ class DisplayFloat extends Display {
|
||||
this._setContentScale(scale);
|
||||
}
|
||||
|
||||
_onMessageExtensionUnloaded() {
|
||||
if (yomichan.isExtensionUnloaded) { return; }
|
||||
yomichan.triggerExtensionUnloaded();
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_copySelection() {
|
||||
|
@ -147,26 +147,13 @@ class Frontend {
|
||||
}
|
||||
|
||||
async updateOptions() {
|
||||
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 *');
|
||||
try {
|
||||
await this._updateOptionsInternal();
|
||||
} catch (e) {
|
||||
if (!yomichan.isExtensionUnloaded) {
|
||||
throw e;
|
||||
}
|
||||
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);
|
||||
this._showExtensionUnloaded(null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -243,6 +230,30 @@ class Frontend {
|
||||
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() {
|
||||
const showIframePopupsInRootFrame = this._options.general.showIframePopupsInRootFrame;
|
||||
const isIframe = !this._useProxyPopup && (window !== window.parent);
|
||||
@ -328,8 +339,15 @@ class Frontend {
|
||||
return this._popup === null || this._popup.isProxy() ? [] : [this._popup.getContainer()];
|
||||
}
|
||||
|
||||
_ignorePoint(x, y) {
|
||||
return this._popup !== null && this._popup.containsPoint(x, y);
|
||||
async _ignorePoint(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) {
|
||||
@ -352,7 +370,7 @@ class Frontend {
|
||||
} catch (e) {
|
||||
if (yomichan.isExtensionUnloaded) {
|
||||
if (textSource !== null && this._options.scanning.modifier !== 'none') {
|
||||
this._showPopupContent(textSource, await this.getOptionsContext(), 'extensionUnloaded');
|
||||
this._showExtensionUnloaded(textSource);
|
||||
}
|
||||
} else {
|
||||
yomichan.logError(e);
|
||||
@ -392,6 +410,14 @@ class Frontend {
|
||||
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) {
|
||||
const {url} = optionsContext;
|
||||
const sentenceExtent = this._options.anki.sentenceExt;
|
||||
@ -414,6 +440,10 @@ class Frontend {
|
||||
details,
|
||||
context
|
||||
);
|
||||
this._lastShowPromise.catch((error) => {
|
||||
if (yomichan.isExtensionUnloaded) { return; }
|
||||
yomichan.logError(error);
|
||||
});
|
||||
return this._lastShowPromise;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,11 @@ class PopupProxy extends EventDispatcher {
|
||||
}
|
||||
|
||||
async isVisible() {
|
||||
try {
|
||||
return await this._invoke('isVisible', {id: this._id});
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
setVisibleOverride(visible) {
|
||||
@ -98,8 +102,12 @@ class PopupProxy extends EventDispatcher {
|
||||
this._invoke('setCustomCss', {id: this._id, css});
|
||||
}
|
||||
|
||||
clearAutoPlayTimer() {
|
||||
this._invoke('clearAutoPlayTimer', {id: this._id});
|
||||
async clearAutoPlayTimer() {
|
||||
try {
|
||||
await this._invoke('clearAutoPlayTimer', {id: this._id});
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
||||
setContentScale(scale) {
|
||||
|
@ -83,6 +83,7 @@ class Popup {
|
||||
this._frame.addEventListener('mousedown', (e) => e.stopPropagation());
|
||||
this._frame.addEventListener('scroll', (e) => e.stopPropagation());
|
||||
this._frame.addEventListener('load', this._onFrameLoad.bind(this));
|
||||
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
||||
}
|
||||
|
||||
isProxy() {
|
||||
@ -149,8 +150,12 @@ class Popup {
|
||||
this._invokeApi('setCustomCss', {css});
|
||||
}
|
||||
|
||||
clearAutoPlayTimer() {
|
||||
this._invokeApi('clearAutoPlayTimer');
|
||||
async clearAutoPlayTimer() {
|
||||
try {
|
||||
await this._invokeApi('clearAutoPlayTimer');
|
||||
} catch (e) {
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
|
||||
setContentScale(scale) {
|
||||
@ -447,6 +452,18 @@ class Popup {
|
||||
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() {
|
||||
const defaultParent = document.body;
|
||||
const fullscreenElement = DOM.getFullscreenElement();
|
||||
@ -636,15 +653,4 @@ class Popup {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,15 +97,13 @@ class Display {
|
||||
this._setInteractive(true);
|
||||
await yomichan.ready();
|
||||
await this._displayGenerator.prepare();
|
||||
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
|
||||
}
|
||||
|
||||
onError(error) {
|
||||
if (yomichan.isExtensionUnloaded) {
|
||||
this.setContent('extensionUnloaded');
|
||||
} else {
|
||||
if (yomichan.isExtensionUnloaded) { return; }
|
||||
yomichan.logError(error);
|
||||
}
|
||||
}
|
||||
|
||||
onEscape() {
|
||||
throw new Error('Override me');
|
||||
@ -176,9 +174,6 @@ class Display {
|
||||
case 'kanji':
|
||||
await this._setContentKanji(details.definitions, details.context, token);
|
||||
break;
|
||||
case 'extensionUnloaded':
|
||||
this._setContentExtensionUnloaded();
|
||||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
this.onError(e);
|
||||
@ -236,6 +231,10 @@ class Display {
|
||||
|
||||
// Private
|
||||
|
||||
_onExtensionUnloaded() {
|
||||
this._setContentExtensionUnloaded();
|
||||
}
|
||||
|
||||
_onSourceTermView(e) {
|
||||
e.preventDefault();
|
||||
this._sourceTermView();
|
||||
|
@ -196,7 +196,7 @@ const yomichan = (() => {
|
||||
try {
|
||||
return chrome.runtime.sendMessage(...args);
|
||||
} catch (e) {
|
||||
this._onExtensionUnloaded(e);
|
||||
this.triggerExtensionUnloaded();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@ -205,7 +205,7 @@ const yomichan = (() => {
|
||||
try {
|
||||
return chrome.runtime.connect(...args);
|
||||
} catch (e) {
|
||||
this._onExtensionUnloaded(e);
|
||||
this.triggerExtensionUnloaded();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@ -247,13 +247,13 @@ const yomichan = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_onExtensionUnloaded(error) {
|
||||
triggerExtensionUnloaded() {
|
||||
this._isExtensionUnloaded = true;
|
||||
this.trigger('extensionUnloaded', {error});
|
||||
this.trigger('extensionUnloaded');
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_getUrl() {
|
||||
return (typeof window === 'object' && window !== null ? window.location.href : '');
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user