Back to loading mathjax via blob urls

Fucking stupid Content Security Policy
This commit is contained in:
Kovid Goyal 2018-12-13 18:07:13 +05:30
parent 8d27d68dff
commit bd99a17186
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
5 changed files with 142 additions and 12 deletions

View File

@ -0,0 +1,58 @@
#!/usr/bin/env python2
# vim:fileencoding=utf-8
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
from __future__ import absolute_import, division, print_function, unicode_literals
PATCHED_MATHJAX = '''
function postprocess_mathjax(link_uid) {
Array.prototype.forEach.call(document.getElementsByTagName('a'), function(a) {
var href = a.getAttribute('href');
if (href && href.startsWith('#')) {
a.setAttribute('href', 'javascript: void(0)')
a.setAttribute('data-' + link_uid, JSON.stringify({'frag':href.slice(1)}))
}
});
document.documentElement.dispatchEvent(new CustomEvent("calibre-mathjax-init-done"));
}
function monkeypatch(mathjax_files) {
var orig = window.MathJax.Ajax.fileURL.bind(window.MathJax.Ajax);
window.MathJax.Ajax.fileURL = function(mathjax_name) {
var ans = orig(mathjax_name);
var name = ans.replace(/^\\//g, '');
if (name.startsWith('../fonts')) name = name.slice(3);
ans = mathjax_files[name];
if (!ans) ans = '';
if (typeof ans !== 'string') {
mathjax_files[name] = window.URL.createObjectURL(ans);
ans = mathjax_files[name];
}
if (ans === name && !name.startsWith('blob:')) {
if (ans.endsWith('.eot') || ans.endsWith('.otf')) return '';
console.log('WARNING: Failed to resolve MathJax file: ' + mathjax_name);
}
return ans;
}
window.MathJax.Ajax.fileRev = function(mathjax_name) { return ''; }
}
function init_mathjax(link_uid, mathjax_files) {
monkeypatch(mathjax_files);
window.MathJax.Hub.Register.StartupHook("End", postprocess_mathjax.bind(this, link_uid))
}
document.documentElement.addEventListener("calibre-mathjax-init", function(ev) {
window.MathJax = ev.detail.MathJax;
window.MathJax.AuthorInit = init_mathjax.bind(this, ev.detail.link_uid, ev.detail.mathjax_files);
__SRC__
});
'''
def monkeypatch_mathjax(src):
return PATCHED_MATHJAX.replace('__SRC__', src, 1)

View File

@ -62,7 +62,7 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
def __init__(self, parent=None):
QWebEngineUrlSchemeHandler.__init__(self, parent)
self.mathjax_tdir = None
self.mathjax_tdir = self.mathjax_manifest = None
def requestStarted(self, rq):
if bytes(rq.requestMethod()) != b'GET':
@ -99,16 +99,28 @@ class UrlSchemeHandler(QWebEngineUrlSchemeHandler):
data = b'[' + manifest + b',' + metadata + b']'
self.send_reply(rq, mime_type, data)
elif name.startswith('mathjax/'):
if self.mathjax_tdir is None:
from calibre.srv.books import get_mathjax_manifest
self.mathjax_tdir = PersistentTemporaryDirectory(prefix='v2mjx-')
get_mathjax_manifest(self.mathjax_tdir)
from calibre.gui2.viewer2.mathjax import monkeypatch_mathjax
if name == 'mathjax/manifest.json':
if self.mathjax_tdir is None:
import json
from calibre.srv.books import get_mathjax_manifest
self.mathjax_tdir = PersistentTemporaryDirectory(prefix='v2mjx-')
self.mathjax_manifest = json.dumps(get_mathjax_manifest(self.mathjax_tdir)['files'])
self.send_reply(rq, 'application/json', self.mathjax_manifest)
return
path = os.path.abspath(os.path.join(self.mathjax_tdir, name))
if path.startswith(self.mathjax_tdir):
mt = guess_type(name)
with lopen(path, 'rb') as f:
raw = f.read()
try:
with lopen(path, 'rb') as f:
raw = f.read()
except EnvironmentError as err:
prints("Failed to get mathjax file: {} with error: {}".format(name, err))
rq.fail(rq.RequestFailed)
return
if 'MathJax.js' in name:
raw = monkeypatch_mathjax(raw.decode('utf-8')).encode('utf-8')
self.send_reply(rq, mt, raw)
def send_reply(self, rq, mime_type, data):

View File

@ -15,6 +15,7 @@ def get_url(mathjax_files, name):
ans = mathjax_files[name] = window.URL.createObjectURL(ans)
return ans
def monkeypatch(mathjax_files):
StyleString = window.MathJax.Ajax.StyleString.bind(window.MathJax.Ajax)
@ -72,7 +73,18 @@ def apply_mathjax(mathjax_files, link_uid, proceed):
script = E.script(type='text/javascript')
document.head.appendChild(script)
if runtime.is_standalone_viewer:
script.src = f'{runtime.FAKE_PROTOCOL}://{runtime.FAKE_HOST}/mathjax/MathJax.js'
script.onload = def():
ev = new CustomEvent("calibre-mathjax-init", {
'detail': {
'MathJax': m,
'link_uid': link_uid,
'mathjax_files': mathjax_files,
}
})
document.documentElement.dispatchEvent(ev)
document.documentElement.addEventListener("calibre-mathjax-init-done", def(ev):
proceed()
)
else:
m.AuthorInit = init_mathjax.bind(this, mathjax_files, link_uid, proceed)
script.src = get_url(mathjax_files, 'MathJax.js')
script.src = get_url(mathjax_files, 'MathJax.js')

View File

@ -168,7 +168,7 @@ class View:
'human_scroll': self.on_human_scroll,
}
entry_point = None if runtime.is_standalone_viewer else 'read_book.iframe'
self.iframe_wrapper = IframeWrapper(handlers, document.getElementById(iframe_id), entry_point, _('Bootstrapping book reader...'))
self.iframe_wrapper = IframeWrapper(handlers, document.getElementById(iframe_id), entry_point, _('Bootstrapping book reader...'), runtime.FAKE_PROTOCOL, runtime.FAKE_HOST)
self.search_overlay = SearchOverlay(self)
self.content_popup_overlay = ContentPopupOverlay(self)
self.overlay = Overlay(self)

View File

@ -59,9 +59,57 @@ def get_file(book, name, proceed):
xhr.responseType = 'blob'
xhr.send()
def mathjax_file_received(name, proceed, end_type, xhr, ev):
if end_type is 'abort':
return
if end_type is not 'load':
show_error(_('Failed to load MathJax file'), _(
'Could not load the file: {} with error: {}').format(name, xhr.error_html))
return
if not xhr.responseType or xhr.responseType is 'text':
result = xhr.responseText
else if xhr.responseType is 'blob' or xhr.responseType is 'json':
result = xhr.response
else:
show_error(_('Failed to load MathJax file'), _(
'Could not load the file: {} unknown response type: {}')
.format(name, xhr.responseType))
return
if name is 'manifest.json':
get_mathjax_files.manifest = result
get_mathjax_files_stage2.files_to_get = list(Object.keys(result))
get_mathjax_files_stage2.file_data = {}
get_mathjax_files_stage2(proceed)
return
get_mathjax_files_stage2.file_data[name] = result
get_mathjax_files_stage2.files_to_get.remove(name)
if not get_mathjax_files_stage2.files_to_get.length:
proceed(get_mathjax_files_stage2.file_data)
def get_mathjax_manifest(proceed):
xhr = ajax('mathjax/manifest.json', mathjax_file_received.bind(None, 'manifest.json', proceed), ok_code=0)
xhr.responseType = 'json'
xhr.send()
def get_mathjax_files_stage2(proceed):
if not get_mathjax_files_stage2.files_to_get.length:
proceed(get_mathjax_files_stage2.file_data)
return
for filename in get_mathjax_files_stage2.files_to_get:
xhr = ajax(f'mathjax/{filename}', mathjax_file_received.bind(None, filename, proceed), ok_code=0)
xhr.responseType = 'blob'
xhr.send()
def get_mathjax_files(proceed):
proceed({})
if not get_mathjax_files.manifest:
get_mathjax_manifest(proceed)
else:
get_mathjax_files_stage2(proceed)
def update_url_state(replace):