Simplify loading of javascript in render iframe

Also improves performance by in-lining javascript into index.html
This commit is contained in:
Kovid Goyal 2016-03-24 23:41:57 +05:30
parent 35d2b9fda9
commit c56ef9c087
9 changed files with 60 additions and 106 deletions

3
.gitignore vendored
View File

@ -20,8 +20,7 @@ resources/builtin_recipes.zip
resources/template-functions.json
resources/editor-functions.json
resources/user-manual-translation-stats.json
resources/content-server/main.js
resources/content-server/iframe.js
resources/content-server/index-generated.html
resources/content-server/locales.zip
resources/mozilla-ca-certs.pem
icons/icns/*.iconset

View File

@ -6,8 +6,6 @@
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/png" href="favicon.png">
<script>window.calibre_entry_point = 'ENTRY_POINT'; window.calibre_default_library = DEFAULT_LIBRARY;</script>
<script type="text/javascript" src="static/main.js"></script>
<link rel="stylesheet" href="static/reset.css"></link>
<link rel="stylesheet" href="static/font-awesome/fa.css"></link>
</head>
@ -15,7 +13,7 @@
<div id="page_load_progress">
<progress>
</progress>
<div>LOADING_MSG&hellip;<div>
<div>Loading, please wait&hellip;<div>
<style type="text/css">
#page_load_progress {
position:relative;
@ -25,5 +23,6 @@
}
</style>
</div>
<script id="main_js">MAIN_JS</script>
</body>
</html>

View File

@ -4,12 +4,10 @@
from __future__ import (unicode_literals, division, absolute_import,
print_function)
import re, hashlib, random, zipfile
from functools import partial
from threading import Lock
from json import load as load_json_file, dumps as json_dumps
import hashlib, random, zipfile
from json import load as load_json_file
from calibre import prepare_string_for_xml, as_unicode
from calibre import as_unicode
from calibre.customize.ui import available_input_formats
from calibre.db.view import sanitize_sort_field_name
from calibre.srv.ajax import search_result
@ -22,45 +20,18 @@ from calibre.utils.icu import sort_key
from calibre.utils.localization import get_lang
from calibre.utils.search_query_parser import ParseException
html_cache = {}
cache_lock = Lock()
autoreload_js = None
POSTABLE = frozenset({'GET', 'POST', 'HEAD'})
def get_html(name, auto_reload_port, **replacements):
global autoreload_js
key = (name, auto_reload_port, tuple(replacements.iteritems()))
with cache_lock:
try:
return html_cache[key]
except KeyError:
with lopen(P(name), 'rb') as f:
raw = f.read()
for k, val in key[-1]:
if isinstance(val, type('')):
val = val.encode('utf-8')
if isinstance(k, type('')):
k = k.encode('utf-8')
raw = raw.replace(k, val)
if auto_reload_port > 0:
if autoreload_js is None:
autoreload_js = P('content-server/autoreload.js', data=True, allow_user_override=False).replace(
b'AUTORELOAD_PORT', bytes(str(auto_reload_port)))
raw = re.sub(
br'(<\s*/\s*head\s*>)', br'<script type="text/javascript">%s</script>\1' % autoreload_js,
raw, flags=re.IGNORECASE)
html_cache[key] = raw
return raw
@endpoint('', auth_required=False)
def index(ctx, rd):
default_library = ctx.library_info(rd)[1]
return rd.generate_static_output('/', partial(
get_html, 'content-server/index.html', getattr(rd.opts, 'auto_reload_port', 0),
ENTRY_POINT='book list',
LOADING_MSG=prepare_string_for_xml(_('Loading library, please wait')),
DEFAULT_LIBRARY=json_dumps(default_library)
))
return lopen(P('content-server/index-generated.html'), 'rb')
@endpoint('/auto-reload', auth_required=False)
def auto_reload(ctx, rd):
auto_reload_port = getattr(rd.opts, 'auto_reload_port', 0)
if auto_reload_port > 0:
rd.outheaders.set('Calibre-Auto-Reload-Port', type('')(auto_reload_port), replace_all=True)
return lopen(P('content-server/autoreload.js'), 'rb')
def get_basic_query_data(ctx, rd):
db, library_id, library_map, default_library = get_library_data(ctx, rd)

View File

@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import,
__license__ = 'GPL v3'
__copyright__ = '2015, Kovid Goyal <kovid at kovidgoyal.net>'
import os, sys, atexit, errno, subprocess, glob, shutil, json, hashlib, re
import os, sys, atexit, errno, subprocess, glob, shutil, json, re
from io import BytesIO
from threading import local
from functools import partial
@ -105,17 +105,13 @@ def compile_srv():
with lopen(rb, 'rb') as f:
rv = str(int(re.search(br'^RENDER_VERSION\s+=\s+(\d+)', f.read(), re.M).group(1)))
base = P('content-server', allow_user_override=False)
fname = os.path.join(rapydscript_dir, 'reader.pyj')
with lopen(fname, 'rb') as f:
reader = compile_pyj(f.read(), fname)
sha = hashlib.sha1(reader).hexdigest()
with lopen(os.path.join(base, 'iframe.js'), 'wb') as f:
f.write(reader.encode('utf-8'))
fname = os.path.join(rapydscript_dir, 'srv.pyj')
with lopen(fname, 'rb') as f:
raw = compile_pyj(f.read(), fname).replace("__IFRAME_SCRIPT_HASH__", sha).replace('__RENDER_VERSION__', rv)
with lopen(os.path.join(base, 'main.js'), 'wb') as f:
f.write(raw.encode('utf-8'))
js = compile_pyj(f.read(), fname).replace('__RENDER_VERSION__', rv).encode('utf-8')
with lopen(os.path.join(base, 'index.html'), 'rb') as f:
html = f.read().replace(b'MAIN_JS', js)
with lopen(os.path.join(base, 'index-generated.html'), 'wb') as f:
f.write(html)
# }}}

View File

@ -1,7 +1,6 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from ajax import ajax
from gettext import gettext as _
from utils import base64encode, base64decode
@ -24,17 +23,14 @@ def get_error_details(event):
elif desc.errorCode:
desc = desc.errorCode
IFRAME_SCRIPT_HASH = "__IFRAME_SCRIPT_HASH__"
DB_NAME = 'calibre-books-db-testing' # TODO: Remove test suffix and change version back to 1
DB_VERSION = 1
class DB:
def __init__(self, idb, ui, supports_blobs, iframe_script):
def __init__(self, idb, ui, supports_blobs):
self.interface_data = ui.interface_data
self.idb = idb
self.iframe_script = iframe_script
self.supports_blobs = supports_blobs
if not supports_blobs:
print('IndexedDB does not support Blob storage, using base64 encoding instead')
@ -192,29 +188,9 @@ def create_db(ui, interface_data):
req = idb.transaction(['files'], 'readwrite').objectStore('files').put(blob, ':-test-blob-:')
except Exception:
print('WARNING: browser does not support blob storage, calibre falling back to base64 encoding')
create_db_stage2(idb, ui, interface_data, False)
return
return ui.db_initialized(DB(idb, ui, False))
req.onsuccess = def(event):
create_db_stage2(idb, ui, interface_data, True)
ui.db_initialized(DB(idb, ui, True))
req.onerror = def(event):
print('WARNING: browser does not support blob storage, calibre falling back to base64 encoding')
create_db_stage2(idb, ui, interface_data, False)
def create_db_stage2(idb, ui, interface_data, supports_blobs):
req = idb.transaction(['objects']).objectStore('objects').get('iframe.js')
req.onerror = def(event):
ui.db_initialized(_('Failed to initialize books database: ') + get_error_details(event))
req.onsuccess = def(event):
s = event.result
if s and s.script_hash is IFRAME_SCRIPT_HASH:
return ui.db_initialized(DB(idb, ui, supports_blobs, s.src))
ajax('static/iframe.js', def(end_type, xhr, event):
if end_type != 'load':
return ui.db_initialized('<div>' + _('Failed to load book reader script') + '</div>' + xhr.error_html)
obj = {'key':'iframe.js', 'script_hash': IFRAME_SCRIPT_HASH, 'src':xhr.responseText}
req = idb.transaction(['objects'], 'readwrite').objectStore('objects').put(obj)
req.onerror = def(event):
ui.db_initialized(_('Failed to store book reader script in database: ') + get_error_details(event))
req.onsuccess = def(event):
ui.db_initialized(DB(idb, ui, supports_blobs, obj.src))
).send()
ui.db_initialized(DB(idb, ui, False))

View File

@ -93,8 +93,6 @@ class ReadUI:
def db_initialized(self, db):
self.db = db
if type(self.db) is not 'string':
self.view.create_src_doc(self.db.iframe_script)
if self.pending_load is not None:
pl, self.pending_load = self.pending_load, None
self.start_load(*pl)

View File

@ -11,13 +11,13 @@ LOADING_DOC = '''
<head>
<script type="text/javascript" id="bootstrap" data-key="__KEY__">
__SCRIPT__
</script>
end_script
</head>
<body>
__BS__
</body>
</html>
'''
'''.replace('end_script', '<' + '/script>') # cannot have a closing script tag as this is embedded inside a script tag in index.html
class View:
@ -35,13 +35,16 @@ class View:
self.src_doc = None
self.iframe_ready = False
self.pending_spine_load = None
self.create_src_doc()
window.addEventListener('message', self.handle_message.bind(self), False)
@property
def iframe(self):
return document.getElementById(iframe_id)
def create_src_doc(self, iframe_script):
def create_src_doc(self):
iframe_script = self.ui.interface_data.main_js.replace(/is_running_in_iframe\s*=\s*false/, 'is_running_in_iframe = true')
self.ui.interface_data.main_js = None
self.src_doc = self.iframe.srcdoc = LOADING_DOC.replace(
'__SCRIPT__', iframe_script).replace(
'__BS__', _('Bootstrapping book reader...')).replace(

View File

@ -1,5 +0,0 @@
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2016, Kovid Goyal <kovid at kovidgoyal.net>
from read_book.iframe import init
init()

View File

@ -9,16 +9,22 @@ from utils import parse_url_params
from book_list.boss import Boss
from book_list.globals import set_session_data
from read_book.iframe import init
def on_library_loaded(end_type, xhr, ev):
p = document.getElementById('page_load_progress')
p.parentNode.removeChild(p)
if end_type is 'load':
interface_data = JSON.parse(xhr.responseText)
interface_data.main_js = main_js
script = document.getElementById('main_js')
if script:
script.parentNode.removeChild(script) # Free up some memory
if interface_data.translations:
install(interface_data.translations)
sd = UserSessionData(interface_data['username'], interface_data['user_session_data'])
sd = UserSessionData(interface_data.username, interface_data.user_session_data)
set_session_data(sd)
sd.set('library_id', interface_data.library_id)
Boss(interface_data)
else:
p = E.p(style='color:red; font-weight: bold; font-size:1.5em')
@ -36,25 +42,36 @@ def on_library_load_progress(loaded, total):
def load_book_list():
temp = UserSessionData(None, {}) # So that settings for anonymous users are preserved
query = {}
default_lib = window.calibre_default_library
v'delete window.calibre_default_library'
query.sort = temp.get_library_option(default_lib, 'sort')
library_id = temp.get('library_id')
if library_id:
query.library_id = library_id
query.sort = temp.get_library_option(library_id, 'sort')
url_query = parse_url_params()
for key in url_query:
query[key] = url_query[key]
ajax('interface-data/init', on_library_loaded, on_library_load_progress, query=query).send()
def on_load():
ep = window.calibre_entry_point
v'delete window.calibre_entry_point'
if ep is 'book list':
print('calibre loaded at:', Date().toString())
load_book_list()
# We wait for all page elements to load, since this is a single page app
# with a largely empty starting document, we can use this to preload any resources
# we know are going to be needed immediately.
window.addEventListener('load', on_load)
is_running_in_iframe = False # Changed before script is loaded in the iframe
if is_running_in_iframe:
init()
else:
main_js = document.scripts[0].textContent
# We wait for all page elements to load, since this is a single page app
# with a largely empty starting document, we can use this to preload any resources
# we know are going to be needed immediately.
window.addEventListener('load', on_load)
ajax('auto-reload', def(end_type, xhr, event):
if end_type is 'load':
port = xhr.getResponseHeader('Calibre-Auto-Reload-Port')
if port:
src = xhr.responseText.replace('AUTORELOAD_PORT', port)
eval(src)
, bypass_cache=False).send()