Popup window (#773)

* Add option usePopupWindow

* Add PopupWindow class

* Add support for creating PopupWindow
This commit is contained in:
toasted-nutbread 2020-09-05 22:03:35 -04:00 committed by GitHub
parent 55770934f8
commit 44f38c4dea
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 205 additions and 5 deletions

View File

@ -53,6 +53,7 @@
"fg/js/popup-factory.js",
"fg/js/frame-offset-forwarder.js",
"fg/js/popup-proxy.js",
"fg/js/popup-window.js",
"fg/js/frontend.js",
"fg/js/content-script-main.js"
],

View File

@ -111,7 +111,8 @@
"showPitchAccentGraph",
"showIframePopupsInRootFrame",
"useSecurePopupFrameUrl",
"usePopupShadowDom"
"usePopupShadowDom",
"usePopupWindow"
],
"properties": {
"enable": {
@ -257,6 +258,10 @@
"usePopupShadowDom": {
"type": "boolean",
"default": true
},
"usePopupWindow": {
"type": "boolean",
"default": false
}
}
},

View File

@ -467,6 +467,7 @@ class OptionsUtil {
static _updateVersion4(options) {
// Version 4 changes:
// Options conditions converted to string representations.
// Added usePopupWindow.
for (const {conditionGroups} of options.profiles) {
for (const {conditions} of conditionGroups) {
for (const condition of conditions) {
@ -479,6 +480,9 @@ class OptionsUtil {
}
}
}
for (const {options: profileOptions} of options.profiles) {
profileOptions.general.usePopupWindow = false;
}
return options;
}
}

View File

@ -180,6 +180,10 @@
<label><input type="checkbox" id="show-iframe-popups-in-root-frame" data-setting="general.showIframePopupsInRootFrame"> Show iframe popups in root frame</label>
</div>
<div class="checkbox options-advanced">
<label><input type="checkbox" data-setting="general.usePopupWindow"> Use a native popup window when scanning text on web pages</label>
</div>
<div class="checkbox options-advanced">
<label><input type="checkbox" data-setting="general.useSecurePopupFrameUrl"> Use secure popup frame URL</label>
</div>

View File

@ -307,11 +307,17 @@ class Frontend {
}
async _updatePopup() {
const showIframePopupsInRootFrame = this._options.general.showIframePopupsInRootFrame;
const {usePopupWindow, showIframePopupsInRootFrame} = this._options.general;
const isIframe = !this._useProxyPopup && (window !== window.parent);
let popupPromise;
if (
if (usePopupWindow) {
popupPromise = this._popupCache.get('window');
if (typeof popupPromise === 'undefined') {
popupPromise = this._getPopupWindow();
this._popupCache.set('window', popupPromise);
}
} else if (
isIframe &&
showIframePopupsInRootFrame &&
DocumentUtil.getFullscreenElement() === null &&
@ -404,6 +410,14 @@ class Frontend {
return popup;
}
async _getPopupWindow() {
return await this._popupFactory.getOrCreatePopup({
ownerFrameId: this._frameId,
depth: this._depth,
popupWindow: true
});
}
_ignoreElements() {
if (this._popup !== null) {
const container = this._popup.container;

View File

@ -19,6 +19,7 @@
* FrameOffsetForwarder
* Popup
* PopupProxy
* PopupWindow
* api
*/
@ -52,7 +53,7 @@ class PopupFactory {
]);
}
async getOrCreatePopup({frameId=null, ownerFrameId=null, id=null, parentPopupId=null, depth=null}) {
async getOrCreatePopup({frameId=null, ownerFrameId=null, id=null, parentPopupId=null, depth=null, popupWindow=false}) {
// Find by existing id
if (id !== null) {
const popup = this._popups.get(id);
@ -85,7 +86,15 @@ class PopupFactory {
depth = 0;
}
if (frameId === this._frameId) {
if (popupWindow) {
// New unique id
if (id === null) {
id = generateId(16);
}
const popup = new PopupWindow(id, depth, this._frameId, ownerFrameId);
this._popups.set(id, popup);
return popup;
} else if (frameId === this._frameId) {
// New unique id
if (id === null) {
id = generateId(16);

161
ext/fg/js/popup-window.js Normal file
View File

@ -0,0 +1,161 @@
/*
* Copyright (C) 2020 Yomichan Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/* global
* api
*/
class PopupWindow extends EventDispatcher {
constructor(id, depth, frameId, ownerFrameId) {
super();
this._id = id;
this._depth = depth;
this._frameId = frameId;
this._ownerFrameId = ownerFrameId;
this._popupTabId = null;
}
// Public properties
get id() {
return this._id;
}
get parent() {
return null;
}
set parent(value) {
throw new Error('Not supported on PopupProxy');
}
get child() {
return null;
}
set child(value) {
throw new Error('Not supported on PopupProxy');
}
get depth() {
return this._depth;
}
get frameContentWindow() {
return null;
}
get container() {
return null;
}
get frameId() {
return this._frameId;
}
// Public functions
setOptionsContext(optionsContext, source) {
return this._invoke(false, 'setOptionsContext', {id: this._id, optionsContext, source});
}
hide(_changeFocus) {
// NOP
}
async isVisible() {
return (this._popupTabId !== null && await api.isTabSearchPopup(this._popupTabId));
}
setVisibleOverride(_value, _priority) {
return null;
}
clearVisibleOverride(_token) {
return false;
}
async containsPoint(_x, _y) {
return false;
}
showContent(_details, displayDetails) {
return this._invoke(true, 'setContent', {id: this._id, details: displayDetails});
}
setCustomCss(css) {
return this._invoke(false, 'setCustomCss', {id: this._id, css});
}
clearAutoPlayTimer() {
return this._invoke(false, 'clearAutoPlayTimer', {id: this._id});
}
setContentScale(_scale) {
// NOP
}
isVisibleSync() {
throw new Error('Not supported on PopupWindow');
}
updateTheme() {
// NOP
}
async setCustomOuterCss(_css, _useWebExtensionApi) {
// NOP
}
async setChildrenSupported(_value) {
// NOP
}
getFrameRect() {
return new DOMRect(0, 0, 0, 0);
}
// Private
async _invoke(open, action, params={}, defaultReturnValue) {
if (yomichan.isExtensionUnloaded) {
return defaultReturnValue;
}
const frameId = 0;
if (this._popupTabId !== null) {
try {
return await api.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
} catch (e) {
if (yomichan.isExtensionUnloaded) {
open = false;
}
}
this._popupTabId = null;
}
if (!open) {
return defaultReturnValue;
}
const {tabId} = await api.getOrCreateSearchPopup({focus: 'ifCreated'});
this._popupTabId = tabId;
return await api.crossFrame.invokeTab(this._popupTabId, frameId, 'popupMessage', {action, params});
}
}

View File

@ -52,6 +52,7 @@
"fg/js/popup-factory.js",
"fg/js/frame-offset-forwarder.js",
"fg/js/popup-proxy.js",
"fg/js/popup-window.js",
"fg/js/frontend.js",
"fg/js/content-script-main.js"
],

View File

@ -363,6 +363,7 @@ class Display extends EventDispatcher {
'/mixed/js/frame-client.js',
'/fg/js/popup.js',
'/fg/js/popup-proxy.js',
'/fg/js/popup-window.js',
'/fg/js/popup-factory.js',
'/fg/js/frame-offset-forwarder.js',
'/fg/js/frontend.js'