E-book viewer: Speed up initialisation of WebEngine

Prevent the viewer JavaScript from being loaded multiple times on the
various iframes. Also only create the iframe used for the footnote
popups on demand.
This commit is contained in:
Kovid Goyal 2021-05-01 10:26:50 +05:30
parent 9a3a8bd271
commit b8e128271d
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 102 additions and 69 deletions

View File

@ -6,8 +6,8 @@ from elementmaker import E
from gettext import gettext as _
from book_list.theme import get_color, get_color_as_rgba
from dom import add_extra_css, build_rule, clear, ensure_id, svgicon
from iframe_comm import IframeClient, IframeWrapper
from dom import add_extra_css, build_rule, clear, svgicon
from iframe_comm import IframeClient, create_wrapped_iframe
from modals import create_custom_dialog
from utils import html_escape
from widgets import create_button
@ -348,17 +348,19 @@ def add_editor(editor):
class Editor:
def __init__(self, iframe):
def __init__(self, iframe_kw):
handlers = {
'ready': self.on_iframe_ready,
'html': self.on_html_received,
'update_state': self.update_state,
}
self.iframe_wrapper = IframeWrapper(handlers, iframe, 'book_list.comments_editor', _('Loading comments editor...'))
self.id = ensure_id(iframe)
iframe, self.iframe_wrapper = create_wrapped_iframe(
handlers, _('Loading comments editor...'), 'book_list.comments_editor', iframe_kw)
self.id = iframe.id
self.ready = False
self.pending_set_html = None
self.get_html_callbacks = v'[]'
self.iframe_obj = iframe
def init(self):
self.iframe_wrapper.init()
@ -421,8 +423,12 @@ class Editor:
def create_editor():
iframe = E.iframe(sandbox='allow-scripts', seamless=True, style='flex-grow: 10; border: solid 1px currentColor', id=self.id)
editor = Editor(iframe)
iframe_kw = {
'sandbox': 'allow-scripts', 'seamless': True, 'style': 'flex-grow: 10; border: solid 1px currentColor'
}
editor = Editor(iframe_kw)
iframe = editor.iframe_obj
v'delete editor.iframe_obj'
add_editor(editor)
return iframe, editor

View File

@ -2,14 +2,14 @@
# License: GPL v3 Copyright: 2017, Kovid Goyal <kovid at kovidgoyal.net>
from __python__ import bound_methods, hash_literals
import traceback
from aes import GCM
from gettext import gettext as _, install
from elementmaker import E
import traceback
from book_list.globals import get_translations, main_js
from book_list.theme import get_font_family
from dom import ensure_id
from gettext import gettext as _, install
LOADING_DOC = '''
<!DOCTYPE html>
@ -58,16 +58,22 @@ class Messenger:
class IframeWrapper:
def __init__(self, handlers, iframe, entry_point, bootstrap_text, url):
def __init__(self, handlers, iframe, entry_point, bootstrap_text):
self.messenger = Messenger()
self.iframe_id = ensure_id(iframe, 'content-iframe')
self.needs_init = True
if ':' in entry_point:
self.needs_init = False
self.srcdoc_created = True
self.constructor_url = entry_point
self.entry_point = None
else:
self.needs_init = True
self.srcdoc_created = False
self.constructor_url = None
self.entry_point = entry_point
self.ready = False
self.encrypted_communications = False
self.srcdoc_created = False
self.constructor_url = url
self.entry_point = entry_point
self.bootstrap_text = bootstrap_text
self.bootstrap_text = bootstrap_text or ''
self.handlers = {k: handlers[k] for k in handlers}
self.on_ready_handler = self.handlers.ready
self.handlers.ready = self.on_iframe_ready
@ -164,6 +170,15 @@ class IframeWrapper:
callback()
def create_wrapped_iframe(handlers, bootstrap_text, entry_point, kw):
if ':' in entry_point:
kw.src = entry_point
kw.sandbox = (kw.sandbox or '') + ' allow-same-origin'
iframe = E.iframe(**kw)
ans = IframeWrapper(handlers, iframe, entry_point, bootstrap_text)
return iframe, ans
class IframeClient:
def __init__(self, handlers):

View File

@ -6,7 +6,7 @@ from elementmaker import E
from gettext import gettext as _
from dom import add_extra_css, build_rule, clear, svgicon
from iframe_comm import IframeWrapper
from iframe_comm import create_wrapped_iframe
from read_book.globals import runtime, ui_operations
from read_book.resources import load_resources
@ -42,29 +42,35 @@ class ContentPopupOverlay:
self.loaded_resources = {}
c = self.container
c.classList.add(CLASS_NAME)
sandbox = 'allow-scripts'
if runtime.is_standalone_viewer:
sandbox += ' allow-same-origin'
iframe = E.iframe(seamless=True, sandbox=sandbox, style='width: 100%; max-height: 70vh')
c.appendChild(E.div(
E.div(),
iframe
))
c.appendChild(E.div(E.div()))
c.addEventListener('click', self.hide)
c.firstChild.addEventListener('click', def(ev):
ev.stopPropagation(), ev.preventDefault()
)
self.pending_load = None
def reset(self):
if self.iframe_wrapper:
self.iframe_wrapper.reset()
def create_iframe(self):
handlers = {
'ready': self.on_iframe_ready,
'error': self.view.on_iframe_error,
'content_loaded': self.on_content_loaded,
}
entry_point = None if runtime.is_standalone_viewer else 'read_book.footnotes'
self.iframe_wrapper = IframeWrapper(
handlers, iframe, entry_point, _('Loading data, please wait...'),
f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/book/__popup__')
self.pending_load = None
iframe_kw = {
'seamless': True, 'sandbox': 'allow-scripts', 'style': 'width: 100%; max-height: 70vh'
}
if runtime.is_standalone_viewer:
entry_point = f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/book/__popup__'
else:
entry_point = 'read_book.footnotes'
iframe, self.iframe_wrapper = create_wrapped_iframe(
handlers, _('Loading data, please wait...'), entry_point, iframe_kw
)
c = self.container
c.firstChild.appendChild(iframe)
@property
def container(self):
@ -117,6 +123,8 @@ class ContentPopupOverlay:
load_resources(self.view.book, name, self.loaded_resources, cb)
def show_footnote(self, data):
if not self.iframe_wrapper:
self.create_iframe()
self.current_footnote_data = data
width = 100 // data.cols_per_screen
c = self.container.firstChild

View File

@ -11,7 +11,7 @@ from book_list.theme import cached_color_to_rgba, get_color, set_ui_colors
from book_list.ui import query_as_href
from dom import add_extra_css, build_rule, clear, set_css, svgicon, unique_id
from gettext import gettext as _
from iframe_comm import IframeWrapper
from iframe_comm import create_wrapped_iframe
from modals import error_dialog, warning_dialog
from read_book.annotations import AnnotationsManager
from read_book.bookmarks import create_new_bookmark
@ -229,38 +229,6 @@ class View:
set_left_margin_handler(left_margin)
right_margin = side_margin_elem(self, sd, 'right')
set_right_margin_handler(right_margin)
iframe_id = unique_id('read-book-iframe')
sandbox = 'allow-popups allow-scripts allow-popups-to-escape-sandbox'
if runtime.is_standalone_viewer:
sandbox += ' allow-same-origin'
container.appendChild(
E.div(style='max-height: 100vh; width: 100vw; height: 100vh; overflow: hidden; display: flex; align-items: stretch', # container for horizontally aligned panels
oncontextmenu=def (ev):
if not default_context_menu_should_be_allowed(ev):
ev.preventDefault()
,
E.div(style='max-height: 100vh; display: flex; flex-direction: column; align-items: stretch; flex-grow:2', # container for iframe and any other panels in the same column
E.div(style='max-height: 100vh; flex-grow: 2; display:flex; align-items: stretch', # container for iframe and its overlay
left_margin,
E.div(style='flex-grow:2; display:flex; align-items:stretch; flex-direction: column', # container for top and bottom margins
margin_elem(sd, 'margin_top', 'book-top-margin', self.top_margin_clicked, self.margin_context_menu.bind(None, 'top')),
E.iframe(id=iframe_id, seamless=True, sandbox=sandbox, style='flex-grow: 2', allowfullscreen='true'),
margin_elem(sd, 'margin_bottom', 'book-bottom-margin', self.bottom_margin_clicked, self.margin_context_menu.bind(None, 'bottom')),
),
right_margin,
self.book_scrollbar.create(),
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none;', id='book-selection-bar-overlay'), # selection bar overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none;', id='book-read-aloud-overlay'), # read aloud overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none;', id='book-hints-overlay'), # hints overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; pointer-events:none; display:none', id='book-search-overlay'), # search overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='book-content-popup-overlay'), # content popup overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; overflow: auto; display:none', id='book-overlay'), # main overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='controls-help-overlay'), # controls help overlay
)
),
),
)
handlers = {
'autoscroll_state_changed': def(data):
self.autoscroll_active = v'!!data.running'
@ -304,15 +272,51 @@ class View:
ui_operations.view_image(data.calibre_src)
,
}
entry_point = None if runtime.is_standalone_viewer else 'read_book.iframe'
iframe_id = unique_id('read-book-iframe')
if runtime.is_standalone_viewer:
entry_point = f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/book/__index__'
else:
entry_point = 'read_book.iframe'
iframe_kw = {
'id': iframe_id, 'seamless': True,
'sandbox': 'allow-popups allow-scripts allow-popups-to-escape-sandbox',
'style': 'flex-grow: 2', 'allowfullscreen': 'true',
}
iframe, self.iframe_wrapper = create_wrapped_iframe(handlers, _('Bootstrapping book reader...'), entry_point, iframe_kw)
container.appendChild(
E.div(style='max-height: 100vh; width: 100vw; height: 100vh; overflow: hidden; display: flex; align-items: stretch', # container for horizontally aligned panels
oncontextmenu=def (ev):
if not default_context_menu_should_be_allowed(ev):
ev.preventDefault()
,
E.div(style='max-height: 100vh; display: flex; flex-direction: column; align-items: stretch; flex-grow:2', # container for iframe and any other panels in the same column
E.div(style='max-height: 100vh; flex-grow: 2; display:flex; align-items: stretch', # container for iframe and its overlay
left_margin,
E.div(style='flex-grow:2; display:flex; align-items:stretch; flex-direction: column', # container for top and bottom margins
margin_elem(sd, 'margin_top', 'book-top-margin', self.top_margin_clicked, self.margin_context_menu.bind(None, 'top')),
iframe,
margin_elem(sd, 'margin_bottom', 'book-bottom-margin', self.bottom_margin_clicked, self.margin_context_menu.bind(None, 'bottom')),
),
right_margin,
self.book_scrollbar.create(),
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none;', id='book-selection-bar-overlay'), # selection bar overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none;', id='book-read-aloud-overlay'), # read aloud overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none;', id='book-hints-overlay'), # hints overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; pointer-events:none; display:none', id='book-search-overlay'), # search overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='book-content-popup-overlay'), # content popup overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; overflow: auto; display:none', id='book-overlay'), # main overlay
E.div(style='position: absolute; top:0; left:0; width: 100%; height: 100%; display:none', id='controls-help-overlay'), # controls help overlay
)
),
),
)
self.current_color_scheme = resolve_color_scheme()
if runtime.is_standalone_viewer:
document.documentElement.addEventListener('keydown', self.handle_keypress, {'passive': False})
set_ui_colors(self.current_color_scheme.is_dark_theme)
is_dark_theme(self.current_color_scheme.is_dark_theme)
self.iframe_wrapper = IframeWrapper(
handlers, document.getElementById(iframe_id), entry_point, _('Bootstrapping book reader...'),
f'{runtime.FAKE_PROTOCOL}://{runtime.SANDBOX_HOST}/book/__index__')
self.search_overlay = SearchOverlay(self)
self.content_popup_overlay = ContentPopupOverlay(self)
self.overlay = Overlay(self)
@ -914,7 +918,7 @@ class View:
self.book_load_started = True
if not is_current_book:
self.iframe_wrapper.reset()
self.content_popup_overlay.iframe_wrapper.reset()
self.content_popup_overlay.reset()
self.loaded_resources = {}
self.content_popup_overlay.loaded_resources = {}
self.timers.start_book(book)