summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortoasted-nutbread <toasted-nutbread@users.noreply.github.com>2020-08-23 15:18:41 -0400
committerGitHub <noreply@github.com>2020-08-23 15:18:41 -0400
commit773e28aa3c23c1cb93d5b19b10c49fcec19487a9 (patch)
treefcf3c775c3f97d1852848253149a47b55fa0a753
parent934355dd09aa8b7e8993759b678af063b56b9fc6 (diff)
Fix screenshot popup hide (#753)
* Refactor Popup.setVisibleOverride * Use event to observe visibility changes * Add setAllVisibleOverride/clearAllVisibleOverride * Add setAllVisibleOverride/clearAllVisibleOverride cross frame handlers * Update how visibility is changed * Wait for next frame to ensure visibility has been updated
-rw-r--r--ext/fg/js/frontend.js30
-rw-r--r--ext/fg/js/popup-factory.js60
-rw-r--r--ext/fg/js/popup-proxy.js8
-rw-r--r--ext/fg/js/popup.js24
-rw-r--r--ext/mixed/js/display.js15
5 files changed, 104 insertions, 33 deletions
diff --git a/ext/fg/js/frontend.js b/ext/fg/js/frontend.js
index caa2bd3e..61789473 100644
--- a/ext/fg/js/frontend.js
+++ b/ext/fg/js/frontend.js
@@ -62,7 +62,6 @@ class Frontend {
this._updatePopupToken = null;
this._runtimeMessageHandlers = new Map([
- ['popupSetVisibleOverride', {async: false, handler: this._onMessagePopupSetVisibleOverride.bind(this)}],
['requestFrontendReadyBroadcast', {async: false, handler: this._onMessageRequestFrontendReadyBroadcast.bind(this)}]
]);
}
@@ -108,11 +107,13 @@ class Frontend {
this._textScanner.on('activeModifiersChanged', this._onActiveModifiersChanged.bind(this));
api.crossFrame.registerHandlers([
- ['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}],
- ['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}],
- ['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
- ['getPopupInfo', {async: false, handler: this._onApiGetPopupInfo.bind(this)}],
- ['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}]
+ ['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}],
+ ['closePopup', {async: false, handler: this._onApiClosePopup.bind(this)}],
+ ['copySelection', {async: false, handler: this._onApiCopySelection.bind(this)}],
+ ['getPopupInfo', {async: false, handler: this._onApiGetPopupInfo.bind(this)}],
+ ['getDocumentInformation', {async: false, handler: this._onApiGetDocumentInformation.bind(this)}],
+ ['setAllVisibleOverride', {async: true, handler: this._onApiSetAllVisibleOverride.bind(this)}],
+ ['clearAllVisibleOverride', {async: true, handler: this._onApiClearAllVisibleOverride.bind(this)}]
]);
this._updateContentScale();
@@ -161,11 +162,6 @@ class Frontend {
// Message handlers
- _onMessagePopupSetVisibleOverride({visible}) {
- if (this._popup === null) { return; }
- this._popup.setVisibleOverride(visible);
- }
-
_onMessageRequestFrontendReadyBroadcast({frameId}) {
this._signalFrontendReady(frameId);
}
@@ -196,6 +192,18 @@ class Frontend {
};
}
+ async _onApiSetAllVisibleOverride({value, priority, awaitFrame}) {
+ const result = await this._popupFactory.setAllVisibleOverride(value, priority);
+ if (awaitFrame) {
+ await promiseAnimationFrame(100);
+ }
+ return result;
+ }
+
+ async _onApiClearAllVisibleOverride({token}) {
+ return await this._popupFactory.clearAllVisibleOverride(token);
+ }
+
// Private
_onResize() {
diff --git a/ext/fg/js/popup-factory.js b/ext/fg/js/popup-factory.js
index 03ac603b..27bd49cd 100644
--- a/ext/fg/js/popup-factory.js
+++ b/ext/fg/js/popup-factory.js
@@ -27,6 +27,7 @@ class PopupFactory {
this._frameId = frameId;
this._frameOffsetForwarder = new FrameOffsetForwarder(frameId);
this._popups = new Map();
+ this._allPopupVisibilityTokenMap = new Map();
}
// Public functions
@@ -39,6 +40,7 @@ class PopupFactory {
['hide', {async: false, handler: this._onApiHide.bind(this)}],
['isVisible', {async: true, handler: this._onApiIsVisibleAsync.bind(this)}],
['setVisibleOverride', {async: true, handler: this._onApiSetVisibleOverride.bind(this)}],
+ ['clearVisibleOverride', {async: true, handler: this._onApiClearVisibleOverride.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)}],
@@ -108,6 +110,40 @@ class PopupFactory {
}
}
+ async setAllVisibleOverride(value, priority) {
+ const promises = [];
+ const errors = [];
+ for (const popup of this._popups.values()) {
+ const promise = popup.setVisibleOverride(value, priority)
+ .then(
+ (token) => ({popup, token}),
+ (error) => { errors.push(error); return null; }
+ );
+ promises.push(promise);
+ }
+
+ const results = await Promise.all(promises);
+
+ if (errors.length === 0) {
+ const token = generateId(16);
+ this._allPopupVisibilityTokenMap.set(token, results);
+ return token;
+ }
+
+ // Revert on error
+ await this._revertPopupVisibilityOverrides(results);
+ throw errors[0];
+ }
+
+ async clearAllVisibleOverride(token) {
+ const results = this._allPopupVisibilityTokenMap.get(token);
+ if (typeof results === 'undefined') { return false; }
+
+ this._allPopupVisibilityTokenMap.delete(token);
+ await this._revertPopupVisibilityOverrides(results);
+ return true;
+ }
+
// API message handlers
async _onApiGetOrCreatePopup({id, parentPopupId, frameId, ownerFrameId}) {
@@ -134,9 +170,14 @@ class PopupFactory {
return await popup.isVisible();
}
- async _onApiSetVisibleOverride({id, visible}) {
+ async _onApiSetVisibleOverride({id, value, priority}) {
+ const popup = this._getPopup(id);
+ return await popup.setVisibleOverride(value, priority);
+ }
+
+ async _onApiClearVisibleOverride({id, token}) {
const popup = this._getPopup(id);
- return await popup.setVisibleOverride(visible);
+ return await popup.clearVisibleOverride(token);
}
async _onApiContainsPoint({id, x, y}) {
@@ -216,4 +257,19 @@ class PopupFactory {
const parent = popup.parent;
return parent === null || parent.isVisibleSync();
}
+
+ async _revertPopupVisibilityOverrides(overrides) {
+ const promises = [];
+ for (const value of overrides) {
+ if (value === null) { continue; }
+ const {popup, token} = value;
+ const promise = popup.clearVisibleOverride(token)
+ .then(
+ (v) => v,
+ () => false
+ );
+ promises.push(promise);
+ }
+ return await Promise.all(promises);
+ }
}
diff --git a/ext/fg/js/popup-proxy.js b/ext/fg/js/popup-proxy.js
index f1b3ab21..efbc72a6 100644
--- a/ext/fg/js/popup-proxy.js
+++ b/ext/fg/js/popup-proxy.js
@@ -86,8 +86,12 @@ class PopupProxy extends EventDispatcher {
return this._invokeSafe('isVisible', {id: this._id}, false);
}
- setVisibleOverride(visible) {
- return this._invokeSafe('setVisibleOverride', {id: this._id, visible});
+ async setVisibleOverride(value, priority) {
+ return this._invokeSafe('setVisibleOverride', {id: this._id, value, priority});
+ }
+
+ async clearVisibleOverride(token) {
+ return this._invokeSafe('clearVisibleOverride', {id: this._id, token});
}
async containsPoint(x, y) {
diff --git a/ext/fg/js/popup.js b/ext/fg/js/popup.js
index bbf39c4e..f644ee98 100644
--- a/ext/fg/js/popup.js
+++ b/ext/fg/js/popup.js
@@ -34,8 +34,7 @@ class Popup extends EventDispatcher {
this._childrenSupported = true;
this._injectPromise = null;
this._injectPromiseComplete = false;
- this._visible = false;
- this._visibleOverride = null;
+ this._visible = new DynamicProperty(false);
this._options = null;
this._optionsContext = null;
this._contentScale = 1.0;
@@ -96,11 +95,12 @@ class Popup extends EventDispatcher {
// Public functions
prepare() {
- this._updateVisibility();
this._frame.addEventListener('mousedown', (e) => e.stopPropagation());
this._frame.addEventListener('scroll', (e) => e.stopPropagation());
this._frame.addEventListener('load', this._onFrameLoad.bind(this));
+ this._visible.on('change', this._onVisibleChange.bind(this));
yomichan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this));
+ this._onVisibleChange({value: this.isVisibleSync()});
}
async setOptionsContext(optionsContext, source) {
@@ -131,9 +131,12 @@ class Popup extends EventDispatcher {
return this.isVisibleSync();
}
- setVisibleOverride(visible) {
- this._visibleOverride = visible;
- this._updateVisibility();
+ async setVisibleOverride(value, priority) {
+ return this._visible.setOverride(value, priority);
+ }
+
+ async clearVisibleOverride(token) {
+ return this._visible.clearOverride(token);
}
async containsPoint(x, y) {
@@ -177,7 +180,7 @@ class Popup extends EventDispatcher {
}
isVisibleSync() {
- return (this._visibleOverride !== null ? this._visibleOverride : this._visible);
+ return this._visible.value;
}
updateTheme() {
@@ -392,12 +395,11 @@ class Popup extends EventDispatcher {
}
_setVisible(visible) {
- this._visible = visible;
- this._updateVisibility();
+ this._visible.defaultValue = visible;
}
- _updateVisibility() {
- this._frame.style.setProperty('visibility', this.isVisibleSync() ? 'visible' : 'hidden', 'important');
+ _onVisibleChange({value}) {
+ this._frame.style.setProperty('visibility', value ? 'visible' : 'hidden', 'important');
}
_focusParent() {
diff --git a/ext/mixed/js/display.js b/ext/mixed/js/display.js
index c3498e85..6e41a190 100644
--- a/ext/mixed/js/display.js
+++ b/ext/mixed/js/display.js
@@ -1145,9 +1145,12 @@ class Display extends EventDispatcher {
}
async _getScreenshot() {
+ const ownerFrameId = this._ownerFrameId;
+ let token = null;
try {
- await this._setPopupVisibleOverride(false);
- await promiseTimeout(1); // Wait for popup to be hidden.
+ if (ownerFrameId !== null) {
+ token = await api.crossFrame.invoke(ownerFrameId, 'setAllVisibleOverride', {value: false, priority: 0, awaitFrame: true});
+ }
const {format, quality} = this._options.anki.screenshot;
const dataUrl = await api.screenshotGet({format, quality});
@@ -1155,7 +1158,9 @@ class Display extends EventDispatcher {
return {dataUrl, format};
} finally {
- await this._setPopupVisibleOverride(null);
+ if (token !== null) {
+ await api.crossFrame.invoke(ownerFrameId, 'clearAllVisibleOverride', {token});
+ }
}
}
@@ -1163,10 +1168,6 @@ class Display extends EventDispatcher {
return this._options.general.resultOutputMode === 'merge' ? 0 : -1;
}
- _setPopupVisibleOverride(visible) {
- return api.broadcastTab('popupSetVisibleOverride', {visible});
- }
-
_getEntry(index) {
const entries = this._container.querySelectorAll('.entry');
return index >= 0 && index < entries.length ? entries[index] : null;