Skip to content

Commit

Permalink
E-book viewer: Speed up initialisation of WebEngine
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
kovidgoyal committed May 1, 2021
1 parent 9a3a8bd commit b8e1282
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 69 deletions.
20 changes: 13 additions & 7 deletions src/pyj/book_list/comments_editor.pyj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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

Expand Down
33 changes: 24 additions & 9 deletions src/pyj/iframe_comm.pyj
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
38 changes: 23 additions & 15 deletions src/pyj/read_book/content_popup.pyj
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down
80 changes: 42 additions & 38 deletions src/pyj/read_book/view.pyj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit b8e1282

Please sign in to comment.