diff --git a/.gitignore b/.gitignore index 5aaeaa8b9c..563089287d 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/resources/content-server/index.html b/resources/content-server/index.html index b4e56a6b49..11d4d1b6ee 100644 --- a/resources/content-server/index.html +++ b/resources/content-server/index.html @@ -6,8 +6,6 @@ - - @@ -15,7 +13,7 @@
-
LOADING_MSG…
+
Loading, please wait…
+ diff --git a/src/calibre/srv/code.py b/src/calibre/srv/code.py index e72a580bd7..40f4cedd4a 100644 --- a/src/calibre/srv/code.py +++ b/src/calibre/srv/code.py @@ -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'\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) diff --git a/src/calibre/utils/rapydscript.py b/src/calibre/utils/rapydscript.py index 3fc89b1e64..60627f1134 100644 --- a/src/calibre/utils/rapydscript.py +++ b/src/calibre/utils/rapydscript.py @@ -6,7 +6,7 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2015, Kovid Goyal ' -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) # }}} diff --git a/src/pyj/read_book/db.pyj b/src/pyj/read_book/db.pyj index d33ea0a836..5c70dee0e2 100644 --- a/src/pyj/read_book/db.pyj +++ b/src/pyj/read_book/db.pyj @@ -1,7 +1,6 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal -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('
' + _('Failed to load book reader script') + '
' + 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)) diff --git a/src/pyj/read_book/ui.pyj b/src/pyj/read_book/ui.pyj index bf2b26f674..7498ee43fc 100644 --- a/src/pyj/read_book/ui.pyj +++ b/src/pyj/read_book/ui.pyj @@ -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) diff --git a/src/pyj/read_book/view.pyj b/src/pyj/read_book/view.pyj index 451f3ffa3d..0d5ef5c856 100644 --- a/src/pyj/read_book/view.pyj +++ b/src/pyj/read_book/view.pyj @@ -11,13 +11,13 @@ LOADING_DOC = ''' +end_script __BS__ -''' +'''.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( diff --git a/src/pyj/reader.pyj b/src/pyj/reader.pyj deleted file mode 100644 index 52f95608b1..0000000000 --- a/src/pyj/reader.pyj +++ /dev/null @@ -1,5 +0,0 @@ -# vim:fileencoding=utf-8 -# License: GPL v3 Copyright: 2016, Kovid Goyal - -from read_book.iframe import init -init() diff --git a/src/pyj/srv.pyj b/src/pyj/srv.pyj index 49c410e3d5..98577233a5 100644 --- a/src/pyj/srv.pyj +++ b/src/pyj/srv.pyj @@ -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() + print('calibre loaded at:', Date().toString()) + load_book_list() + +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() + -# 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)