Frontend initialization refactor (#610)

* Create member functions for ignoreElements and ignorePoint

* Create addFullscreenChangeEventListener utility

* Move popup creation management into Frontend

* Move getUrl implementation

* Remove old setup

* Remove try/catch block

* Error wrap

* Add prepare call to TextScanner

* Update depth when popup changes

* Refactor how Frontend gets PopupFactory and frameId

* Update popup preview to work

* Update popup preview frame to use the frontend's popup

* Update how nested popups are set up

* Error wrap

* Update how popups are set up on the search page

* Error wrap

* Error unwrap

* Add missing prepare

* Remove use of frontendInitializationData

* Catch and log errors
This commit is contained in:
toasted-nutbread 2020-06-21 16:14:05 -04:00 committed by GitHub
parent 244ab31bb2
commit f2991fb9ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 447 additions and 341 deletions

View File

@ -18,42 +18,16 @@
/* global /* global
* DisplaySearch * DisplaySearch
* api * api
* dynamicLoader
*/ */
async function injectSearchFrontend() {
await dynamicLoader.loadScripts([
'/mixed/js/text-scanner.js',
'/fg/js/frame-offset-forwarder.js',
'/fg/js/popup.js',
'/fg/js/popup-factory.js',
'/fg/js/frontend.js',
'/fg/js/content-script-main.js'
]);
}
(async () => { (async () => {
try {
api.forwardLogsToBackend(); api.forwardLogsToBackend();
await yomichan.prepare(); await yomichan.prepare();
const displaySearch = new DisplaySearch(); const displaySearch = new DisplaySearch();
await displaySearch.prepare(); await displaySearch.prepare();
} catch (e) {
let optionsApplied = false; yomichan.logError(e);
}
const applyOptions = async () => {
const optionsContext = {depth: 0, url: window.location.href};
const options = await api.optionsGet(optionsContext);
if (!options.scanning.enableOnSearchPage || optionsApplied) { return; }
optionsApplied = true;
yomichan.off('optionsUpdated', applyOptions);
window.frontendInitializationData = {depth: 1, proxy: false, isSearchPage: true};
await injectSearchFrontend();
};
yomichan.on('optionsUpdated', applyOptions);
await applyOptions();
})(); })();

View File

@ -42,6 +42,7 @@ class QueryParser {
async prepare() { async prepare() {
await this._queryParserGenerator.prepare(); await this._queryParserGenerator.prepare();
this._textScanner.prepare();
this._queryParser.addEventListener('click', this._onClick.bind(this)); this._queryParser.addEventListener('click', this._onClick.bind(this));
} }

View File

@ -19,8 +19,11 @@
* ClipboardMonitor * ClipboardMonitor
* DOM * DOM
* Display * Display
* Frontend
* PopupFactory
* QueryParser * QueryParser
* api * api
* dynamicLoader
* wanakana * wanakana
*/ */
@ -73,7 +76,6 @@ class DisplaySearch extends Display {
} }
async prepare() { async prepare() {
try {
await super.prepare(); await super.prepare();
await this.updateOptions(); await this.updateOptions();
yomichan.on('optionsUpdated', () => this.updateOptions()); yomichan.on('optionsUpdated', () => this.updateOptions());
@ -114,10 +116,9 @@ class DisplaySearch extends Display {
this.updateSearchButton(); this.updateSearchButton();
await this._prepareNestedPopups();
this._isPrepared = true; this._isPrepared = true;
} catch (e) {
this.onError(e);
}
} }
onError(error) { onError(error) {
@ -401,4 +402,53 @@ class DisplaySearch extends Display {
document.title = `${text} - Yomichan Search`; document.title = `${text} - Yomichan Search`;
} }
} }
async _prepareNestedPopups() {
let complete = false;
const onOptionsUpdated = async () => {
const optionsContext = this.getOptionsContext();
const options = await api.optionsGet(optionsContext);
if (!options.scanning.enableOnSearchPage || complete) { return; }
complete = true;
yomichan.off('optionsUpdated', onOptionsUpdated);
try {
await this._setupNestedPopups();
} catch (e) {
yomichan.logError(e);
}
};
yomichan.on('optionsUpdated', onOptionsUpdated);
await onOptionsUpdated();
}
async _setupNestedPopups() {
await dynamicLoader.loadScripts([
'/mixed/js/text-scanner.js',
'/fg/js/frame-offset-forwarder.js',
'/fg/js/popup.js',
'/fg/js/popup-factory.js',
'/fg/js/frontend.js'
]);
const {frameId} = await api.frameInformationGet();
const popupFactory = new PopupFactory(frameId);
await popupFactory.prepare();
const frontend = new Frontend(
frameId,
popupFactory,
{
depth: 1,
proxy: false,
isSearchPage: true
}
);
await frontend.prepare();
}
} }

View File

@ -16,12 +16,23 @@
*/ */
/* global /* global
* PopupFactory
* PopupPreviewFrame * PopupPreviewFrame
* api * api
*/ */
(async () => { (async () => {
try {
api.forwardLogsToBackend(); api.forwardLogsToBackend();
const preview = new PopupPreviewFrame();
const {frameId} = await api.frameInformationGet();
const popupFactory = new PopupFactory(frameId);
await popupFactory.prepare();
const preview = new PopupPreviewFrame(frameId, popupFactory);
await preview.prepare(); await preview.prepare();
} catch (e) {
yomichan.logError(e);
}
})(); })();

View File

@ -18,17 +18,17 @@
/* global /* global
* Frontend * Frontend
* Popup * Popup
* PopupFactory
* TextSourceRange * TextSourceRange
* api * api
*/ */
class PopupPreviewFrame { class PopupPreviewFrame {
constructor() { constructor(frameId, popupFactory) {
this._frameId = frameId;
this._popupFactory = popupFactory;
this._frontend = null; this._frontend = null;
this._frontendGetOptionsContextOld = null; this._frontendGetOptionsContextOld = null;
this._apiOptionsGetOld = null; this._apiOptionsGetOld = null;
this._popup = null;
this._popupSetCustomOuterCssOld = null; this._popupSetCustomOuterCssOld = null;
this._popupShown = false; this._popupShown = false;
this._themeChangeTimeout = null; this._themeChangeTimeout = null;
@ -55,24 +55,25 @@ class PopupPreviewFrame {
api.optionsGet = this._apiOptionsGet.bind(this); api.optionsGet = this._apiOptionsGet.bind(this);
// Overwrite frontend // Overwrite frontend
const {frameId} = await api.frameInformationGet(); this._frontend = new Frontend(
this._frameId,
const popupFactory = new PopupFactory(frameId); this._popupFactory,
await popupFactory.prepare(); {
allowRootFramePopupProxy: false
this._popup = popupFactory.getOrCreatePopup(); }
this._popup.setChildrenSupported(false); );
this._popupSetCustomOuterCssOld = this._popup.setCustomOuterCss.bind(this._popup);
this._popup.setCustomOuterCss = this._popupSetCustomOuterCss.bind(this);
this._frontend = new Frontend(this._popup);
this._frontendGetOptionsContextOld = this._frontend.getOptionsContext.bind(this._frontend); this._frontendGetOptionsContextOld = this._frontend.getOptionsContext.bind(this._frontend);
this._frontend.getOptionsContext = this._getOptionsContext.bind(this); this._frontend.getOptionsContext = this._getOptionsContext.bind(this);
await this._frontend.prepare(); await this._frontend.prepare();
this._frontend.setDisabledOverride(true); this._frontend.setDisabledOverride(true);
this._frontend.canClearSelection = false; this._frontend.canClearSelection = false;
const popup = this._frontend.popup;
popup.setChildrenSupported(false);
this._popupSetCustomOuterCssOld = popup.setCustomOuterCss.bind(popup);
popup.setCustomOuterCss = this._popupSetCustomOuterCss.bind(this);
// Update search // Update search
this._updateSearch(); this._updateSearch();
} }
@ -132,7 +133,9 @@ class PopupPreviewFrame {
} }
this._themeChangeTimeout = setTimeout(() => { this._themeChangeTimeout = setTimeout(() => {
this._themeChangeTimeout = null; this._themeChangeTimeout = null;
this._popup.updateTheme(); const popup = this._frontend.popup;
if (popup === null) { return; }
popup.updateTheme();
}, 300); }, 300);
} }
@ -154,12 +157,16 @@ class PopupPreviewFrame {
_setCustomCss({css}) { _setCustomCss({css}) {
if (this._frontend === null) { return; } if (this._frontend === null) { return; }
this._popup.setCustomCss(css); const popup = this._frontend.popup;
if (popup === null) { return; }
popup.setCustomCss(css);
} }
_setCustomOuterCss({css}) { _setCustomOuterCss({css}) {
if (this._frontend === null) { return; } if (this._frontend === null) { return; }
this._popup.setCustomOuterCss(css, false); const popup = this._frontend.popup;
if (popup === null) { return; }
popup.setCustomOuterCss(css, false);
} }
async _updateOptionsContext({optionsContext}) { async _updateOptionsContext({optionsContext}) {
@ -188,7 +195,8 @@ class PopupPreviewFrame {
this._textSource = source; this._textSource = source;
await this._frontend.showContentCompleted(); await this._frontend.showContentCompleted();
if (this._popup.isVisibleSync()) { const popup = this._frontend.popup;
if (popup !== null && popup.isVisibleSync()) {
this._popupShown = true; this._popupShown = true;
} }

View File

@ -131,6 +131,7 @@
<script src="/fg/js/source.js"></script> <script src="/fg/js/source.js"></script>
<script src="/fg/js/popup-factory.js"></script> <script src="/fg/js/popup-factory.js"></script>
<script src="/fg/js/frontend.js"></script> <script src="/fg/js/frontend.js"></script>
<script src="/fg/js/frame-offset-forwarder.js"></script>
<script src="/bg/js/settings/popup-preview-frame.js"></script> <script src="/bg/js/settings/popup-preview-frame.js"></script>
<script src="/bg/js/settings/popup-preview-frame-main.js"></script> <script src="/bg/js/settings/popup-preview-frame-main.js"></script>

View File

@ -16,147 +16,31 @@
*/ */
/* global /* global
* DOM
* FrameOffsetForwarder
* Frontend * Frontend
* PopupFactory * PopupFactory
* PopupProxy
* api * api
*/ */
async function createPopupFactory() { (async () => {
try {
api.forwardLogsToBackend();
await yomichan.prepare();
const {frameId} = await api.frameInformationGet(); const {frameId} = await api.frameInformationGet();
if (typeof frameId !== 'number') { if (typeof frameId !== 'number') {
const error = new Error('Failed to get frameId'); throw new Error('Failed to get frameId');
yomichan.logError(error);
throw error;
} }
const popupFactory = new PopupFactory(frameId); const popupFactory = new PopupFactory(frameId);
await popupFactory.prepare(); await popupFactory.prepare();
return popupFactory;
}
async function createIframePopupProxy(frameOffsetForwarder, setDisabled) { const frontend = new Frontend(
const rootPopupInformationPromise = yomichan.getTemporaryListenerResult( frameId,
chrome.runtime.onMessage, popupFactory,
({action, params}, {resolve}) => { {}
if (action === 'rootPopupInformation') {
resolve(params);
}
}
); );
api.broadcastTab('rootPopupRequestInformationBroadcast'); await frontend.prepare();
const {popupId, frameId: parentFrameId} = await rootPopupInformationPromise; } catch (e) {
yomichan.logError(e);
const popup = new PopupProxy(popupId, 0, null, parentFrameId, frameOffsetForwarder);
popup.on('offsetNotFound', setDisabled);
await popup.prepare();
return popup;
} }
async function getOrCreatePopup(depth, popupFactory) {
return popupFactory.getOrCreatePopup(null, null, depth);
}
async function createPopupProxy(depth, id, parentFrameId) {
const popup = new PopupProxy(null, depth + 1, id, parentFrameId);
await popup.prepare();
return popup;
}
(async () => {
api.forwardLogsToBackend();
await yomichan.prepare();
const data = window.frontendInitializationData || {};
const {id, depth=0, parentFrameId, url=window.location.href, proxy=false, isSearchPage=false} = data;
const isIframe = !proxy && (window !== window.parent);
const popups = {
iframe: null,
proxy: null,
normal: null
};
let frontend = null;
let frontendPreparePromise = null;
let frameOffsetForwarder = null;
let popupFactoryPromise = null;
let iframePopupsInRootFrameAvailable = true;
const disableIframePopupsInRootFrame = () => {
iframePopupsInRootFrameAvailable = false;
applyOptions();
};
let urlUpdatedAt = 0;
let popupProxyUrlCached = url;
const getPopupProxyUrl = async () => {
const now = Date.now();
if (popups.proxy !== null && now - urlUpdatedAt > 500) {
popupProxyUrlCached = await popups.proxy.getUrl();
urlUpdatedAt = now;
}
return popupProxyUrlCached;
};
const applyOptions = async () => {
const optionsContext = {
depth: isSearchPage ? 0 : depth,
url: proxy ? await getPopupProxyUrl() : window.location.href
};
const options = await api.optionsGet(optionsContext);
if (!proxy && frameOffsetForwarder === null) {
frameOffsetForwarder = new FrameOffsetForwarder();
frameOffsetForwarder.prepare();
}
let popup;
if (isIframe && options.general.showIframePopupsInRootFrame && DOM.getFullscreenElement() === null && iframePopupsInRootFrameAvailable) {
popup = popups.iframe || await createIframePopupProxy(frameOffsetForwarder, disableIframePopupsInRootFrame);
popups.iframe = popup;
} else if (proxy) {
popup = popups.proxy || await createPopupProxy(depth, id, parentFrameId);
popups.proxy = popup;
} else {
popup = popups.normal;
if (!popup) {
if (popupFactoryPromise === null) {
popupFactoryPromise = createPopupFactory();
}
const popupFactory = await popupFactoryPromise;
const popupNormal = await getOrCreatePopup(depth, popupFactory);
popups.normal = popupNormal;
popup = popupNormal;
}
}
if (frontend === null) {
const getUrl = proxy ? getPopupProxyUrl : null;
frontend = new Frontend(popup, getUrl);
frontendPreparePromise = frontend.prepare();
await frontendPreparePromise;
} else {
await frontendPreparePromise;
if (isSearchPage) {
const disabled = !options.scanning.enableOnSearchPage;
frontend.setDisabledOverride(disabled);
}
if (isIframe) {
await frontend.setPopup(popup);
}
}
};
yomichan.on('optionsUpdated', applyOptions);
window.addEventListener('fullscreenchange', applyOptions, false);
await applyOptions();
})(); })();

View File

@ -18,42 +18,15 @@
/* global /* global
* DisplayFloat * DisplayFloat
* api * api
* dynamicLoader
*/ */
async function injectPopupNested() {
await dynamicLoader.loadScripts([
'/mixed/js/text-scanner.js',
'/fg/js/popup.js',
'/fg/js/popup-proxy.js',
'/fg/js/frontend.js',
'/fg/js/content-script-main.js'
]);
}
async function popupNestedInitialize(id, depth, parentFrameId, url) {
let optionsApplied = false;
const applyOptions = async () => {
const optionsContext = {depth, url};
const options = await api.optionsGet(optionsContext);
const maxPopupDepthExceeded = !(typeof depth === 'number' && depth < options.scanning.popupNestingMaxDepth);
if (maxPopupDepthExceeded || optionsApplied) { return; }
optionsApplied = true;
yomichan.off('optionsUpdated', applyOptions);
window.frontendInitializationData = {id, depth, parentFrameId, url, proxy: true};
await injectPopupNested();
};
yomichan.on('optionsUpdated', applyOptions);
await applyOptions();
}
(async () => { (async () => {
try {
api.forwardLogsToBackend(); api.forwardLogsToBackend();
const display = new DisplayFloat(); const display = new DisplayFloat();
await display.prepare(); await display.prepare();
} catch (e) {
yomichan.logError(e);
}
})(); })();

View File

@ -17,8 +17,10 @@
/* global /* global
* Display * Display
* Frontend
* PopupFactory
* api * api
* popupNestedInitialize * dynamicLoader
*/ */
class DisplayFloat extends Display { class DisplayFloat extends Display {
@ -30,7 +32,7 @@ class DisplayFloat extends Display {
this._token = null; this._token = null;
this._orphaned = false; this._orphaned = false;
this._initializedNestedPopups = false; this._nestedPopupsPrepared = false;
this._onKeyDownHandlers = new Map([ this._onKeyDownHandlers = new Map([
['C', (e) => { ['C', (e) => {
@ -183,10 +185,10 @@ class DisplayFloat extends Display {
await this.updateOptions(); await this.updateOptions();
if (childrenSupported && !this._initializedNestedPopups) { if (childrenSupported && !this._nestedPopupsPrepared) {
const {depth, url} = optionsContext; const {depth, url} = optionsContext;
popupNestedInitialize(popupId, depth, frameId, url); this._prepareNestedPopups(popupId, depth, frameId, url);
this._initializedNestedPopups = true; this._nestedPopupsPrepared = true;
} }
this.setContentScale(scale); this.setContentScale(scale);
@ -201,4 +203,57 @@ class DisplayFloat extends Display {
this._secret === message.secret this._secret === message.secret
); );
} }
async _prepareNestedPopups(id, depth, parentFrameId, url) {
let complete = false;
const onOptionsUpdated = async () => {
const optionsContext = this.optionsContext;
const options = await api.optionsGet(optionsContext);
const maxPopupDepthExceeded = !(typeof depth === 'number' && depth < options.scanning.popupNestingMaxDepth);
if (maxPopupDepthExceeded || complete) { return; }
complete = true;
yomichan.off('optionsUpdated', onOptionsUpdated);
try {
await this._setupNestedPopups(id, depth, parentFrameId, url);
} catch (e) {
yomichan.logError(e);
}
};
yomichan.on('optionsUpdated', onOptionsUpdated);
await onOptionsUpdated();
}
async _setupNestedPopups(id, depth, parentFrameId, url) {
await dynamicLoader.loadScripts([
'/mixed/js/text-scanner.js',
'/fg/js/popup.js',
'/fg/js/popup-proxy.js',
'/fg/js/popup-factory.js',
'/fg/js/frame-offset-forwarder.js',
'/fg/js/frontend.js'
]);
const {frameId} = await api.frameInformationGet();
const popupFactory = new PopupFactory(frameId);
await popupFactory.prepare();
const frontend = new Frontend(
frameId,
popupFactory,
{
id,
depth,
parentFrameId,
url,
proxy: true
}
);
await frontend.prepare();
}
} }

View File

@ -16,16 +16,18 @@
*/ */
/* global /* global
* DOM
* FrameOffsetForwarder
* PopupProxy
* TextScanner * TextScanner
* api * api
* docSentenceExtract * docSentenceExtract
*/ */
class Frontend { class Frontend {
constructor(popup, getUrl=null) { constructor(frameId, popupFactory, frontendInitializationData) {
this._id = yomichan.generateId(16); this._id = yomichan.generateId(16);
this._popup = popup; this._popup = null;
this._getUrl = getUrl;
this._disabledOverride = false; this._disabledOverride = false;
this._options = null; this._options = null;
this._pageZoomFactor = 1.0; this._pageZoomFactor = 1.0;
@ -37,11 +39,31 @@ class Frontend {
this._optionsUpdatePending = false; this._optionsUpdatePending = false;
this._textScanner = new TextScanner({ this._textScanner = new TextScanner({
node: window, node: window,
ignoreElements: () => this._popup.isProxy() ? [] : [this._popup.getFrame()], ignoreElements: this._ignoreElements.bind(this),
ignorePoint: (x, y) => this._popup.containsPoint(x, y), ignorePoint: this._ignorePoint.bind(this),
search: this._search.bind(this) search: this._search.bind(this)
}); });
const {
depth=0,
id: proxyPopupId,
parentFrameId,
proxy: useProxyPopup=false,
isSearchPage=false,
allowRootFramePopupProxy=true
} = frontendInitializationData;
this._proxyPopupId = proxyPopupId;
this._parentFrameId = parentFrameId;
this._useProxyPopup = useProxyPopup;
this._isSearchPage = isSearchPage;
this._depth = depth;
this._frameId = frameId;
this._frameOffsetForwarder = new FrameOffsetForwarder();
this._popupFactory = popupFactory;
this._allowRootFramePopupProxy = allowRootFramePopupProxy;
this._popupCache = new Map();
this._updatePopupToken = null;
this._windowMessageHandlers = new Map([ this._windowMessageHandlers = new Map([
['popupClose', this._onMessagePopupClose.bind(this)], ['popupClose', this._onMessagePopupClose.bind(this)],
['selectionCopy', this._onMessageSelectionCopy.bind()] ['selectionCopy', this._onMessageSelectionCopy.bind()]
@ -62,8 +84,13 @@ class Frontend {
this._textScanner.canClearSelection = value; this._textScanner.canClearSelection = value;
} }
get popup() {
return this._popup;
}
async prepare() { async prepare() {
try { this._frameOffsetForwarder.prepare();
await this.updateOptions(); await this.updateOptions();
try { try {
const {zoomFactor} = await api.getZoom(); const {zoomFactor} = await api.getZoom();
@ -72,7 +99,10 @@ class Frontend {
// Ignore exceptions which may occur due to being on an unsupported page (e.g. about:blank) // Ignore exceptions which may occur due to being on an unsupported page (e.g. about:blank)
} }
this._textScanner.prepare();
window.addEventListener('resize', this._onResize.bind(this), false); window.addEventListener('resize', this._onResize.bind(this), false);
DOM.addFullscreenChangeEventListener(this._updatePopup.bind(this));
const visualViewport = window.visualViewport; const visualViewport = window.visualViewport;
if (visualViewport !== null && typeof visualViewport === 'object') { if (visualViewport !== null && typeof visualViewport === 'object') {
@ -88,17 +118,12 @@ class Frontend {
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('activeModifiersChanged', this._onActiveModifiersChanged.bind(this));
api.crossFrame.registerHandlers([
['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}]
]);
this._updateContentScale(); this._updateContentScale();
this._broadcastRootPopupInformation(); this._broadcastRootPopupInformation();
} catch (e) {
yomichan.logError(e);
}
}
async setPopup(popup) {
this._textScanner.clearSelection(true);
this._popup = popup;
await popup.setOptionsContext(await this.getOptionsContext(), this._id);
} }
setDisabledOverride(disabled) { setDisabledOverride(disabled) {
@ -112,8 +137,16 @@ class Frontend {
} }
async getOptionsContext() { async getOptionsContext() {
const url = this._getUrl !== null ? await this._getUrl() : window.location.href; let url = window.location.href;
const depth = this._popup.depth; if (this._useProxyPopup) {
try {
url = await api.crossFrame.invoke(this._parentFrameId, 'getUrl', {});
} catch (e) {
// NOP
}
}
const depth = this._depth;
const modifierKeys = [...this._activeModifiers]; const modifierKeys = [...this._activeModifiers];
return {depth, url, modifierKeys}; return {depth, url, modifierKeys};
} }
@ -121,6 +154,9 @@ class Frontend {
async updateOptions() { async updateOptions() {
const optionsContext = await this.getOptionsContext(); const optionsContext = await this.getOptionsContext();
this._options = await api.optionsGet(optionsContext); this._options = await api.optionsGet(optionsContext);
await this._updatePopup();
this._textScanner.setOptions(this._options); this._textScanner.setOptions(this._options);
this._updateTextScannerEnabled(); this._updateTextScannerEnabled();
@ -130,8 +166,6 @@ class Frontend {
} }
this._textScanner.ignoreNodes = ignoreNodes.join(','); this._textScanner.ignoreNodes = ignoreNodes.join(',');
await this._popup.setOptionsContext(optionsContext, this._id);
this._updateContentScale(); this._updateContentScale();
const textSourceCurrent = this._textScanner.getCurrentTextSource(); const textSourceCurrent = this._textScanner.getCurrentTextSource();
@ -167,6 +201,12 @@ class Frontend {
this._broadcastDocumentInformation(uniqueId); this._broadcastDocumentInformation(uniqueId);
} }
// API message handlers
_onApiGetUrl() {
return window.location.href;
}
// Private // Private
_onResize() { _onResize() {
@ -223,6 +263,95 @@ class Frontend {
await this.updateOptions(); await this.updateOptions();
} }
async _updatePopup() {
const showIframePopupsInRootFrame = this._options.general.showIframePopupsInRootFrame;
const isIframe = !this._useProxyPopup && (window !== window.parent);
let popupPromise;
if (
isIframe &&
showIframePopupsInRootFrame &&
DOM.getFullscreenElement() === null &&
this._allowRootFramePopupProxy
) {
popupPromise = this._popupCache.get('iframe');
if (typeof popupPromise === 'undefined') {
popupPromise = this._getIframeProxyPopup();
this._popupCache.set('iframe', popupPromise);
}
} else if (this._useProxyPopup) {
popupPromise = this._popupCache.get('proxy');
if (typeof popupPromise === 'undefined') {
popupPromise = this._getProxyPopup();
this._popupCache.set('proxy', popupPromise);
}
} else {
popupPromise = this._popupCache.get('default');
if (typeof popupPromise === 'undefined') {
popupPromise = this._getDefaultPopup();
this._popupCache.set('default', popupPromise);
}
}
// The token below is used as a unique identifier to ensure that a new _updatePopup call
// hasn't been started during the await.
const token = {};
this._updatePopupToken = token;
const popup = await popupPromise;
const optionsContext = await this.getOptionsContext();
if (this._updatePopupToken !== token) { return; }
await popup.setOptionsContext(optionsContext, this._id);
if (this._updatePopupToken !== token) { return; }
if (this._isSearchPage) {
this.setDisabledOverride(!this._options.scanning.enableOnSearchPage);
}
this._textScanner.clearSelection(true);
this._popup = popup;
this._depth = popup.depth;
}
async _getDefaultPopup() {
return this._popupFactory.getOrCreatePopup(null, null, this._depth);
}
async _getProxyPopup() {
const popup = new PopupProxy(null, this._depth + 1, this._proxyPopupId, this._parentFrameId);
await popup.prepare();
return popup;
}
async _getIframeProxyPopup() {
const rootPopupInformationPromise = yomichan.getTemporaryListenerResult(
chrome.runtime.onMessage,
({action, params}, {resolve}) => {
if (action === 'rootPopupInformation') {
resolve(params);
}
}
);
api.broadcastTab('rootPopupRequestInformationBroadcast');
const {popupId, frameId: parentFrameId} = await rootPopupInformationPromise;
const popup = new PopupProxy(popupId, 0, null, parentFrameId, this._frameOffsetForwarder);
popup.on('offsetNotFound', () => {
this._allowRootFramePopupProxy = false;
this._updatePopup();
});
await popup.prepare();
return popup;
}
_ignoreElements() {
return this._popup === null || this._popup.isProxy() ? [] : [this._popup.getFrame()];
}
_ignorePoint(x, y) {
return this._popup !== null && this._popup.containsPoint(x, y);
}
async _search(textSource, cause) { async _search(textSource, cause) {
await this._updatePendingOptions(); await this._updatePendingOptions();
@ -318,7 +447,7 @@ class Frontend {
_updateTextScannerEnabled() { _updateTextScannerEnabled() {
const enabled = ( const enabled = (
this._options.general.enable && this._options.general.enable &&
this._popup.depth <= this._options.scanning.popupNestingMaxDepth && this._depth <= this._options.scanning.popupNestingMaxDepth &&
!this._disabledOverride !this._disabledOverride
); );
this._enabledEventListeners.removeAllEventListeners(); this._enabledEventListeners.removeAllEventListeners();
@ -342,27 +471,41 @@ class Frontend {
if (contentScale === this._contentScale) { return; } if (contentScale === this._contentScale) { return; }
this._contentScale = contentScale; this._contentScale = contentScale;
if (this._popup !== null) {
this._popup.setContentScale(this._contentScale); this._popup.setContentScale(this._contentScale);
}
this._updatePopupPosition(); this._updatePopupPosition();
} }
async _updatePopupPosition() { async _updatePopupPosition() {
const textSource = this._textScanner.getCurrentTextSource(); const textSource = this._textScanner.getCurrentTextSource();
if (textSource !== null && await this._popup.isVisible()) { if (
textSource !== null &&
this._popup !== null &&
await this._popup.isVisible()
) {
this._showPopupContent(textSource, await this.getOptionsContext()); this._showPopupContent(textSource, await this.getOptionsContext());
} }
} }
_broadcastRootPopupInformation() { _broadcastRootPopupInformation() {
if (!this._popup.isProxy() && this._popup.depth === 0 && this._popup.frameId === 0) { if (
api.broadcastTab('rootPopupInformation', {popupId: this._popup.id, frameId: this._popup.frameId}); this._popup !== null &&
!this._popup.isProxy() &&
this._depth === 0 &&
this._frameId === 0
) {
api.broadcastTab('rootPopupInformation', {
popupId: this._popup.id,
frameId: this._frameId
});
} }
} }
_broadcastDocumentInformation(uniqueId) { _broadcastDocumentInformation(uniqueId) {
api.broadcastTab('documentInformationBroadcast', { api.broadcastTab('documentInformationBroadcast', {
uniqueId, uniqueId,
frameId: this._popup.frameId, frameId: this._frameId,
title: document.title title: document.title
}); });
} }

View File

@ -39,8 +39,7 @@ class PopupFactory {
['showContent', {async: true, handler: this._onApiShowContent.bind(this)}], ['showContent', {async: true, handler: this._onApiShowContent.bind(this)}],
['setCustomCss', {async: false, handler: this._onApiSetCustomCss.bind(this)}], ['setCustomCss', {async: false, handler: this._onApiSetCustomCss.bind(this)}],
['clearAutoPlayTimer', {async: false, handler: this._onApiClearAutoPlayTimer.bind(this)}], ['clearAutoPlayTimer', {async: false, handler: this._onApiClearAutoPlayTimer.bind(this)}],
['setContentScale', {async: false, handler: this._onApiSetContentScale.bind(this)}], ['setContentScale', {async: false, handler: this._onApiSetContentScale.bind(this)}]
['getUrl', {async: false, handler: this._onApiGetUrl.bind(this)}]
]); ]);
} }
@ -147,10 +146,6 @@ class PopupFactory {
return popup.setContentScale(scale); return popup.setContentScale(scale);
} }
_onApiGetUrl() {
return window.location.href;
}
// Private functions // Private functions
_getPopup(id) { _getPopup(id) {

View File

@ -104,10 +104,6 @@ class PopupProxy extends EventDispatcher {
this._invoke('setContentScale', {id: this._id, scale}); this._invoke('setContentScale', {id: this._id, scale});
} }
async getUrl() {
return await this._invoke('getUrl', {});
}
// Private // Private
_invoke(action, params={}) { _invoke(action, params={}) {

View File

@ -415,16 +415,7 @@ class Popup {
return; return;
} }
const fullscreenEvents = [ DOM.addFullscreenChangeEventListener(this._onFullscreenChanged.bind(this), this._fullscreenEventListeners);
'fullscreenchange',
'MSFullscreenChange',
'mozfullscreenchange',
'webkitfullscreenchange'
];
const onFullscreenChanged = this._onFullscreenChanged.bind(this);
for (const eventName of fullscreenEvents) {
this._fullscreenEventListeners.addEventListener(document, eventName, onFullscreenChanged, false);
}
} }
_onFullscreenChanged() { _onFullscreenChanged() {

View File

@ -77,6 +77,24 @@ class DOM {
return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : ''); return (typeof key === 'string' ? (key.length === 1 ? key.toUpperCase() : key) : '');
} }
static addFullscreenChangeEventListener(onFullscreenChanged, eventListenerCollection=null) {
const target = document;
const options = false;
const fullscreenEventNames = [
'fullscreenchange',
'MSFullscreenChange',
'mozfullscreenchange',
'webkitfullscreenchange'
];
for (const eventName of fullscreenEventNames) {
if (eventListenerCollection === null) {
target.addEventListener(eventName, onFullscreenChanged, options);
} else {
eventListenerCollection.addEventListener(target, eventName, onFullscreenChanged, options);
}
}
}
static getFullscreenElement() { static getFullscreenElement() {
return ( return (
document.fullscreenElement || document.fullscreenElement ||

View File

@ -28,6 +28,7 @@ class TextScanner extends EventDispatcher {
this._ignorePoint = ignorePoint; this._ignorePoint = ignorePoint;
this._search = search; this._search = search;
this._isPrepared = false;
this._ignoreNodes = null; this._ignoreNodes = null;
this._causeCurrent = null; this._causeCurrent = null;
@ -69,10 +70,15 @@ class TextScanner extends EventDispatcher {
return this._causeCurrent; return this._causeCurrent;
} }
prepare() {
this._isPrepared = true;
this.setEnabled(this._enabled);
}
setEnabled(enabled) { setEnabled(enabled) {
this._eventListeners.removeAllEventListeners(); this._eventListeners.removeAllEventListeners();
this._enabled = enabled; this._enabled = enabled;
if (this._enabled) { if (this._enabled && this._isPrepared) {
this._hookEvents(); this._hookEvents();
} else { } else {
this.clearSelection(true); this.clearSelection(true);