feat(reader): upgrade epubjs & add restrictive iframe CSP
This commit is contained in:
parent
5865fe3c13
commit
da1baeb4cd
2
assets/lib/epub.min.js
vendored
2
assets/lib/epub.min.js
vendored
File diff suppressed because one or more lines are too long
@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
@ -97,16 +97,18 @@ class EBookReader {
|
|||||||
flow: "paginated",
|
flow: "paginated",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
allowScriptedContent: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Setup Reader
|
// Setup Reader
|
||||||
this.book.ready.then(this.setupReader.bind(this));
|
this.book.ready.then(this.setupReader.bind(this));
|
||||||
|
|
||||||
// Initialize
|
// Initialize
|
||||||
|
this.initCSP();
|
||||||
this.initDevice();
|
this.initDevice();
|
||||||
this.initWakeLock();
|
this.initWakeLock();
|
||||||
this.initThemes();
|
this.initThemes();
|
||||||
this.initRenditionListeners();
|
this.initViewerListeners();
|
||||||
this.initDocumentListeners();
|
this.initDocumentListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +281,36 @@ class EBookReader {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* EpubJS will set iframe sandbox when settings "allowScriptedContent: false".
|
||||||
|
* However, Safari completely blocks us from attaching listeners to the iframe
|
||||||
|
* document. So instead we just inject a restrictive CSP rule.
|
||||||
|
*
|
||||||
|
* This effectively blocks all script content within the iframe while still
|
||||||
|
* allowing us to attach listeners to the iframe document.
|
||||||
|
**/
|
||||||
|
initCSP() {
|
||||||
|
// Derive CSP Host
|
||||||
|
var protocol = document.location.protocol;
|
||||||
|
var host = document.location.host;
|
||||||
|
var cspURL = `${protocol}//${host}`;
|
||||||
|
|
||||||
|
// Add CSP Policy
|
||||||
|
this.book.spine.hooks.content.register((output, section) => {
|
||||||
|
let cspWrapper = document.createElement("div");
|
||||||
|
cspWrapper.innerHTML = `
|
||||||
|
<meta
|
||||||
|
http-equiv="Content-Security-Policy"
|
||||||
|
content="require-trusted-types-for 'script';
|
||||||
|
style-src 'self' blob: 'unsafe-inline' ${cspURL};
|
||||||
|
object-src 'none';
|
||||||
|
script-src 'none';"
|
||||||
|
>`;
|
||||||
|
let cspMeta = cspWrapper.children[0];
|
||||||
|
output.head.append(cspMeta);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set theme & meta theme color
|
* Set theme & meta theme color
|
||||||
**/
|
**/
|
||||||
@ -371,9 +403,9 @@ class EBookReader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rendition hooks
|
* Viewer Listeners
|
||||||
**/
|
**/
|
||||||
initRenditionListeners() {
|
initViewerListeners() {
|
||||||
/**
|
/**
|
||||||
* Initiate the debounce when the given function returns true.
|
* Initiate the debounce when the given function returns true.
|
||||||
* Don't run it again until the timeout lapses.
|
* Don't run it again until the timeout lapses.
|
||||||
@ -401,15 +433,52 @@ class EBookReader {
|
|||||||
let bottomBar = document.querySelector("#bottom-bar");
|
let bottomBar = document.querySelector("#bottom-bar");
|
||||||
|
|
||||||
// Local Functions
|
// Local Functions
|
||||||
let getCFIFromXPath = this.getCFIFromXPath.bind(this);
|
|
||||||
let setPosition = this.setPosition.bind(this);
|
|
||||||
let nextPage = this.nextPage.bind(this);
|
let nextPage = this.nextPage.bind(this);
|
||||||
let prevPage = this.prevPage.bind(this);
|
let prevPage = this.prevPage.bind(this);
|
||||||
let saveSettings = this.saveSettings.bind(this);
|
|
||||||
|
|
||||||
// Local Vars
|
// ------------------------------------------------ //
|
||||||
let readerSettings = this.readerSettings;
|
// ----------------- Swipe Helpers ---------------- //
|
||||||
let bookState = this.bookState;
|
// ------------------------------------------------ //
|
||||||
|
let touchStartX,
|
||||||
|
touchStartY,
|
||||||
|
touchEndX,
|
||||||
|
touchEndY = undefined;
|
||||||
|
|
||||||
|
function handleGesture(event) {
|
||||||
|
let drasticity = 75;
|
||||||
|
|
||||||
|
// Swipe Down
|
||||||
|
if (touchEndY - drasticity > touchStartY) {
|
||||||
|
return handleSwipeDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swipe Up
|
||||||
|
if (touchEndY + drasticity < touchStartY) {
|
||||||
|
// Prioritize Down & Up Swipes
|
||||||
|
return handleSwipeUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swipe Left
|
||||||
|
if (touchEndX + drasticity < touchStartX) {
|
||||||
|
nextPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swipe Right
|
||||||
|
if (touchEndX - drasticity > touchStartX) {
|
||||||
|
prevPage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSwipeDown() {
|
||||||
|
if (bottomBar.classList.contains("bottom-0"))
|
||||||
|
bottomBar.classList.remove("bottom-0");
|
||||||
|
else topBar.classList.add("top-0");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleSwipeUp() {
|
||||||
|
if (topBar.classList.contains("top-0")) topBar.classList.remove("top-0");
|
||||||
|
else bottomBar.classList.add("bottom-0");
|
||||||
|
}
|
||||||
|
|
||||||
this.rendition.hooks.render.register(function (doc, data) {
|
this.rendition.hooks.render.register(function (doc, data) {
|
||||||
let renderDoc = doc.document;
|
let renderDoc = doc.document;
|
||||||
@ -418,66 +487,14 @@ class EBookReader {
|
|||||||
// ---------------- Wake Lock Hack ---------------- //
|
// ---------------- Wake Lock Hack ---------------- //
|
||||||
// ------------------------------------------------ //
|
// ------------------------------------------------ //
|
||||||
let wakeLockListener = function () {
|
let wakeLockListener = function () {
|
||||||
doc.window.parent.document.dispatchEvent(new CustomEvent("wakelock"));
|
renderDoc.dispatchEvent(new CustomEvent("wakelock"));
|
||||||
};
|
};
|
||||||
renderDoc.addEventListener("click", wakeLockListener);
|
renderDoc.addEventListener("click", wakeLockListener);
|
||||||
renderDoc.addEventListener("gesturechange", wakeLockListener);
|
renderDoc.addEventListener("gesturechange", wakeLockListener);
|
||||||
renderDoc.addEventListener("touchstart", wakeLockListener);
|
renderDoc.addEventListener("touchstart", wakeLockListener);
|
||||||
|
|
||||||
// ------------------------------------------------ //
|
// ------------------------------------------------ //
|
||||||
// --------------- Swipe Pagination --------------- //
|
// --------------- Bars & Page Turn --------------- //
|
||||||
// ------------------------------------------------ //
|
|
||||||
let touchStartX,
|
|
||||||
touchStartY,
|
|
||||||
touchEndX,
|
|
||||||
touchEndY = undefined;
|
|
||||||
|
|
||||||
renderDoc.addEventListener(
|
|
||||||
"touchstart",
|
|
||||||
function (event) {
|
|
||||||
touchStartX = event.changedTouches[0].screenX;
|
|
||||||
touchStartY = event.changedTouches[0].screenY;
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
renderDoc.addEventListener(
|
|
||||||
"touchend",
|
|
||||||
function (event) {
|
|
||||||
touchEndX = event.changedTouches[0].screenX;
|
|
||||||
touchEndY = event.changedTouches[0].screenY;
|
|
||||||
handleGesture(event);
|
|
||||||
},
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
function handleGesture(event) {
|
|
||||||
let drasticity = 75;
|
|
||||||
|
|
||||||
// Swipe Down
|
|
||||||
if (touchEndY - drasticity > touchStartY) {
|
|
||||||
return handleSwipeDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swipe Up
|
|
||||||
if (touchEndY + drasticity < touchStartY) {
|
|
||||||
// Prioritize Down & Up Swipes
|
|
||||||
return handleSwipeUp();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swipe Left
|
|
||||||
if (touchEndX + drasticity < touchStartX) {
|
|
||||||
nextPage();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Swipe Right
|
|
||||||
if (touchEndX - drasticity > touchStartX) {
|
|
||||||
prevPage();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------ //
|
|
||||||
// --------------- Bottom & Top Bar --------------- //
|
|
||||||
// ------------------------------------------------ //
|
// ------------------------------------------------ //
|
||||||
renderDoc.addEventListener(
|
renderDoc.addEventListener(
|
||||||
"click",
|
"click",
|
||||||
@ -529,45 +546,25 @@ class EBookReader {
|
|||||||
}, 400),
|
}, 400),
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSwipeDown() {
|
|
||||||
if (bottomBar.classList.contains("bottom-0"))
|
|
||||||
bottomBar.classList.remove("bottom-0");
|
|
||||||
else topBar.classList.add("top-0");
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleSwipeUp() {
|
|
||||||
if (topBar.classList.contains("top-0"))
|
|
||||||
topBar.classList.remove("top-0");
|
|
||||||
else bottomBar.classList.add("bottom-0");
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------------------------------ //
|
// ------------------------------------------------ //
|
||||||
// -------------- Keyboard Shortcuts -------------- //
|
// ------------------- Gestures ------------------- //
|
||||||
// ------------------------------------------------ //
|
// ------------------------------------------------ //
|
||||||
|
|
||||||
renderDoc.addEventListener(
|
renderDoc.addEventListener(
|
||||||
"keyup",
|
"touchstart",
|
||||||
function (e) {
|
function (event) {
|
||||||
// Left Key (Previous Page)
|
touchStartX = event.changedTouches[0].screenX;
|
||||||
if ((e.keyCode || e.which) == 37) {
|
touchStartY = event.changedTouches[0].screenY;
|
||||||
prevPage();
|
},
|
||||||
}
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
// Right Key (Next Page)
|
renderDoc.addEventListener(
|
||||||
if ((e.keyCode || e.which) == 39) {
|
"touchend",
|
||||||
nextPage();
|
function (event) {
|
||||||
}
|
touchEndX = event.changedTouches[0].screenX;
|
||||||
|
touchEndY = event.changedTouches[0].screenY;
|
||||||
// "t" Key (Theme Cycle)
|
handleGesture(event);
|
||||||
if ((e.keyCode || e.which) == 84) {
|
|
||||||
let currentThemeIdx = THEMES.indexOf(
|
|
||||||
readerSettings.theme.colorScheme,
|
|
||||||
);
|
|
||||||
let colorScheme =
|
|
||||||
THEMES.length == currentThemeIdx + 1
|
|
||||||
? THEMES[0]
|
|
||||||
: THEMES[currentThemeIdx + 1];
|
|
||||||
setTheme({ colorScheme });
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
@ -584,7 +581,9 @@ class EBookReader {
|
|||||||
let nextPage = this.nextPage.bind(this);
|
let nextPage = this.nextPage.bind(this);
|
||||||
let prevPage = this.prevPage.bind(this);
|
let prevPage = this.prevPage.bind(this);
|
||||||
|
|
||||||
// Keyboard Shortcuts
|
// ------------------------------------------------ //
|
||||||
|
// -------------- Keyboard Shortcuts -------------- //
|
||||||
|
// ------------------------------------------------ //
|
||||||
document.addEventListener(
|
document.addEventListener(
|
||||||
"keyup",
|
"keyup",
|
||||||
function (e) {
|
function (e) {
|
||||||
|
Loading…
Reference in New Issue
Block a user