Skip to content

Commit ceb8249

Browse files
committed
fix: file:// protocol privileges reduced, preload mandatory URL scheme continues to work (tested in prod with PDF and EPUB BrowserWindow/WebView preloads) Follow-up to #2672
1 parent 93b6c9c commit ceb8249

File tree

12 files changed

+117
-51
lines changed

12 files changed

+117
-51
lines changed

scripts/afterPack.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ module.exports = async function afterPack(context) {
8080
[FuseV1Options.LoadBrowserProcessSpecificV8Snapshot]: false,
8181

8282
// GrantFileProtocolExtraPrivileges = 7,
83-
[FuseV1Options.GrantFileProtocolExtraPrivileges]: true,
83+
[FuseV1Options.GrantFileProtocolExtraPrivileges]: false,
8484
});
8585

8686
// if (context.electronPlatformName === 'linux') {

src/main/pdf/extract.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const extractPDFData =
3333
// => unicode chars remain escaped!
3434
// So these must be decodeURIComponent() for filesystem API calls,
3535
// ...but the lonely non-encoded percent char triggers a crash if not handled correctly!
36-
// We double-encode the path in order to work around the registerFileProtocol() decoding behaviour:
36+
// (really, the double-encoding is for "viewer.html?file=" in loadURL() below!)
3737

3838
pdfPath = "pdfjs-extract://host/" + encodeURIComponent_RFC3986(encodeURIComponent_RFC3986(pdfPath));
3939
debug("extractPDFData", pdfPath);

src/main/redux/sagas/app.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,23 @@ export function* init() {
114114

115115
debug("Main app ready");
116116

117+
const streamProtocolHandler_FILEX = (
118+
request: Electron.ProtocolRequest,
119+
callback: (response: (NodeJS.ReadableStream) | (Electron.ProtocolResponse)) => void,
120+
) => {
121+
debug("---streamProtocolHandler_FILEX");
122+
debug(request);
123+
const urlPath = request.url.substring("filex://host/".length);
124+
debug(urlPath);
125+
const urlPathDecoded = urlPath.split("/").map((segment) => {
126+
return segment?.length ? tryDecodeURIComponent(segment) : "";
127+
}).join("/");
128+
debug(urlPathDecoded);
129+
callback(fs.createReadStream(urlPathDecoded));
130+
};
131+
protocol.registerStreamProtocol("filex", streamProtocolHandler_FILEX);
132+
// protocol.unregisterProtocol("filex");
133+
117134
if (IS_DEV) {
118135
// https://github.com/MarshallOfSound/electron-devtools-installer
119136
// https://github.com/facebook/react/issues/25843

src/main/redux/sagas/win/browserWindow/createLibraryWindow.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
66
// ==LICENSE-END=
77

8+
import { encodeURIComponent_RFC3986 } from "@r2-utils-js/_utils/http/UrlUtils";
89
import * as debug_ from "debug";
910
import { BrowserWindow, Event, HandlerDetails, shell } from "electron";
1011
import * as path from "path";
@@ -78,15 +79,20 @@ export function* createLibraryWindow(_action: winActions.library.openRequest.TAc
7879
libWindow.hide();
7980
}
8081

82+
// const baseURLForDataURL: string | undefined = undefined;
83+
// let httpReferrer: string | undefined;
8184
let rendererBaseUrl = _RENDERER_LIBRARY_BASE_URL;
82-
if (rendererBaseUrl === "file://") {
85+
const htmlPath = "index_library.html";
86+
if (rendererBaseUrl === "filex://host/") {
8387
// dist/prod mode (without WebPack HMR Hot Module Reload HTTP server)
84-
rendererBaseUrl += path.normalize(path.join(__dirname, "index_library.html"));
88+
rendererBaseUrl += path.normalize(path.join(__dirname, htmlPath)).replace(/\\/g, "/").split("/").map((segment) => encodeURIComponent_RFC3986(segment)).join("/");
89+
// baseURLForDataURL = rendererBaseUrl; // + "/../";
90+
// httpReferrer = rendererBaseUrl; // + "/../";
8591
} else {
8692
// dev/debug mode (with WebPack HMR Hot Module Reload HTTP server)
87-
rendererBaseUrl += "index_library.html";
93+
rendererBaseUrl += htmlPath;
94+
rendererBaseUrl = rendererBaseUrl.replace(/\\/g, "/");
8895
}
89-
rendererBaseUrl = rendererBaseUrl.replace(/\\/g, "/");
9096

9197
if (true) { // IS_DEV
9298

@@ -128,7 +134,7 @@ export function* createLibraryWindow(_action: winActions.library.openRequest.TAc
128134
}
129135
}
130136

131-
yield* callTyped(() => libWindow.loadURL(rendererBaseUrl));
137+
yield* callTyped(() => libWindow.loadURL(rendererBaseUrl /*, {baseURLForDataURL, httpReferrer} */));
132138
// the promise will resolve when the page has finished loading (see did-finish-load)
133139
// and rejects if the page fails to load (see did-fail-load).
134140

src/main/redux/sagas/win/browserWindow/createReaderWindow.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// that can be found in the LICENSE file exposed on Github (readium) in the project repository.
66
// ==LICENSE-END==
77

8+
import { encodeURIComponent_RFC3986 } from "@r2-utils-js/_utils/http/UrlUtils";
89
import * as debug_ from "debug";
910
import { BrowserWindow } from "electron";
1011
import * as path from "path";
@@ -85,19 +86,16 @@ export function* createReaderWindow(action: winActions.reader.openRequest.TActio
8586
trackBrowserWindow(readerWindow);
8687

8788
let readerUrl = _RENDERER_READER_BASE_URL;
88-
8989
const htmlPath = "index_reader.html";
90-
91-
if (readerUrl === "file://") {
90+
if (readerUrl === "filex://host/") {
9291
// dist/prod mode (without WebPack HMR Hot Module Reload HTTP server)
93-
readerUrl += path.normalize(path.join(__dirname, htmlPath));
92+
readerUrl += path.normalize(path.join(__dirname, htmlPath)).replace(/\\/g, "/").split("/").map((segment) => encodeURIComponent_RFC3986(segment)).join("/");
9493
} else {
9594
// dev/debug mode (with WebPack HMR Hot Module Reload HTTP server)
9695
readerUrl += htmlPath;
96+
readerUrl = readerUrl.replace(/\\/g, "/");
9797
}
9898

99-
readerUrl = readerUrl.replace(/\\/g, "/");
100-
10199
if (true) { // IS_DEV
102100

103101
readerWindow.webContents.on("did-finish-load", () => {

src/main/streamer/streamerNoHttp.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,6 +1152,19 @@ export function initSessions() {
11521152
},
11531153
scheme: "store",
11541154
},
1155+
{
1156+
privileges: {
1157+
allowServiceWorkers: false, // Default false
1158+
bypassCSP: true, // Default false
1159+
corsEnabled: false, // Default false
1160+
secure: true, // Default false
1161+
stream: true, // Default false
1162+
supportFetchAPI: true, // Default false
1163+
standard: true, // Default false
1164+
codeCache: false, // Default false (only works with standard=true)
1165+
},
1166+
scheme: "filex",
1167+
},
11551168
{
11561169
privileges: {
11571170
allowServiceWorkers: false, // Default false

src/renderer/library/components/catalog/AboutThoriumButton.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ class AboutThoriumButton extends React.Component<IProps, IState> {
140140
// }
141141
// console.log("pubView not found, need to generate a new one: ", title);
142142

143+
// let folderPath = path.join(window.location.pathname.replace(/^\/\//, "/"), "..", infoFolderRelativePath);
143144
// let folderPath = path.join((global as any).__dirname, infoFolderRelativePath);
144145
// if (_PACKAGING === "0") {
145146
// folderPath = path.join(process.cwd(), "dist", infoFolderRelativePath);

src/renderer/library/index_library.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ initGlobalConverters_GENERIC();
5656

5757
// console.log(__dirname);
5858
// console.log((global as any).__dirname);
59+
// const lcpNativePluginPath = path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", "external-assets", "lcp.node"));
5960
// const lcpNativePluginPath = path.normalize(path.join((global as any).__dirname, "external-assets", "lcp.node"));
6061
// setLcpNativePluginPath(lcpNativePluginPath);
6162

src/renderer/reader/components/App.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
import "reflect-metadata";
99

10+
import { encodeURIComponent_RFC3986 } from "@r2-utils-js/_utils/http/UrlUtils";
11+
1012
// import "readium-desktop/renderer/assets/styles/partials/variables.scss";
1113
// import * as globalScssStyle from "readium-desktop/renderer/assets/styles/global.scss";
1214
import "readium-desktop/renderer/assets/styles/global.scss";
@@ -43,17 +45,29 @@ export default class App extends React.Component<{}, undefined> {
4345
let el = document.getElementById(readiumCssFontFaceStyleID);
4446
if (!el) {
4547

46-
let rcssPath = "ReadiumCSS";
48+
// (global as any).__dirname
49+
// BROKEN when index_reader.js is not served via file://
50+
// ... so instead window.location.href provides dist/index_reader.html which is co-located:
51+
// path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..")) etc.
52+
53+
const RCSSP = "ReadiumCSS";
54+
let rcssPath = RCSSP;
4755
if (_PACKAGING === "1") {
48-
rcssPath = "file://" + path.normalize(path.join((global as any).__dirname, rcssPath));
56+
rcssPath = "filex://host/" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", RCSSP)).replace(/\\/g, "/").split("/").map((segment) => encodeURIComponent_RFC3986(segment)).join("/");
4957
} else {
5058
rcssPath = "r2-navigator-js/dist/ReadiumCSS";
5159

52-
if (_RENDERER_READER_BASE_URL === "file://") {
60+
if (_RENDERER_READER_BASE_URL === "filex://host/") {
5361

5462
// dist/prod mode (without WebPack HMR Hot Module Reload HTTP server)
55-
rcssPath = "file://" +
56-
path.normalize(path.join((global as any).__dirname, _NODE_MODULE_RELATIVE_URL, rcssPath));
63+
rcssPath = "filex://host/" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", _NODE_MODULE_RELATIVE_URL, rcssPath)).replace(/\\/g, "/").split("/").map((segment) => encodeURIComponent_RFC3986(segment)).join("/");
64+
65+
// const debugStr = `[[APP.TSX ${rcssPath} >>> ${window.location.href} *** ${window.location.pathname} === ${process.cwd()} ^^^ ${(global as any).__dirname} --- ${_NODE_MODULE_RELATIVE_URL} @@@ ${rcssPath}]]`;
66+
// if (document.body.firstElementChild) {
67+
// document.body.innerText = debugStr;
68+
// } else {
69+
// document.body.innerText += debugStr;
70+
// }
5771
} else {
5872
// dev/debug mode (with WebPack HMR Hot Module Reload HTTP server)
5973

@@ -66,9 +80,10 @@ export default class App extends React.Component<{}, undefined> {
6680
// static server (WebPack publicPath)
6781
// rcssPath = "/dist/ReadiumCSS";
6882
rcssPath = "/node_modules/" + rcssPath;
83+
rcssPath = rcssPath.replace(/\\/g, "/");
6984
}
7085
}
71-
rcssPath = rcssPath.replace(/\\/g, "/");
86+
7287
console.log("readium css path:",
7388
rcssPath, _PACKAGING, _NODE_MODULE_RELATIVE_URL, _RENDERER_READER_BASE_URL);
7489

@@ -154,6 +169,9 @@ url("${rcssPath}/fonts/iAWriterDuospace-Regular.ttf") format("truetype");
154169
console.log("PROBLEM LOADING READER FONT FACE? ", e);
155170
}
156171

172+
console.log(Nunito);
173+
console.log(NunitoBold);
174+
157175
// FIXME: try a better way to import Nunito in CSS font face instead of in React render function.
158176
// One possibility is to add css font in ejs html template file from webpack
159177
try {

src/renderer/reader/components/Reader.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { IReaderRootState } from "readium-desktop/common/redux/states/renderer/r
3131
import { ok } from "readium-desktop/common/utils/assert";
3232
import { formatTime } from "readium-desktop/common/utils/time";
3333
import {
34-
_APP_NAME, _APP_VERSION, _NODE_MODULE_RELATIVE_URL, _PACKAGING, _RENDERER_READER_BASE_URL,
34+
_APP_NAME, _APP_VERSION, _DIST_RELATIVE_URL, _NODE_MODULE_RELATIVE_URL, _PACKAGING, _RENDERER_READER_BASE_URL,
3535
} from "readium-desktop/preprocessor-directives";
3636
import * as DoubleArrowDownIcon from "readium-desktop/renderer/assets/icons/double_arrow_down_black_24dp.svg";
3737
import * as DoubleArrowLeftIcon from "readium-desktop/renderer/assets/icons/double_arrow_left_black_24dp.svg";
@@ -2218,25 +2218,39 @@ class Reader extends React.Component<IProps, IState> {
22182218
});
22192219

22202220
} else {
2221-
let preloadPath = "preload.js";
2221+
2222+
// (global as any).__dirname
2223+
// BROKEN when index_reader.js is not served via file://
2224+
// ... so instead window.location.href provides dist/index_reader.html which is co-located:
2225+
// path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..")) etc.
2226+
2227+
const PREPATH = "preload.js";
2228+
let preloadPath = PREPATH;
22222229
if (_PACKAGING === "1") {
2223-
preloadPath = "file://" + path.normalize(path.join((global as any).__dirname, preloadPath));
2230+
preloadPath = "file://" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", PREPATH)).replace(/\\/g, "/");
22242231
} else {
22252232
preloadPath = "r2-navigator-js/dist/" +
22262233
"es8-es2017" +
22272234
"/src/electron/renderer/webview/preload.js";
22282235

2229-
if (_RENDERER_READER_BASE_URL === "file://") {
2236+
if (_RENDERER_READER_BASE_URL === "filex://host/") {
22302237
// dist/prod mode (without WebPack HMR Hot Module Reload HTTP server)
2231-
preloadPath = "file://" +
2232-
path.normalize(path.join((global as any).__dirname, _NODE_MODULE_RELATIVE_URL, preloadPath));
2238+
preloadPath = "file://" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", PREPATH)).replace(/\\/g, "/");
2239+
2240+
// preloadPath = "file://" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", _NODE_MODULE_RELATIVE_URL, preloadPath)).replace(/\\/g, "/");
2241+
2242+
// const debugStr = `[[READER.TSX ${preloadPath} >>> ${window.location.href} *** ${window.location.pathname} === ${process.cwd()} ^^^ ${(global as any).__dirname} --- ${_NODE_MODULE_RELATIVE_URL} @@@ ${preloadPath}]]`;
2243+
// if (document.body.firstElementChild) {
2244+
// document.body.innerText = debugStr;
2245+
// } else {
2246+
// document.body.innerText += debugStr;
2247+
// }
22332248
} else {
22342249
// dev/debug mode (with WebPack HMR Hot Module Reload HTTP server)
2235-
preloadPath = "file://" + path.normalize(path.join(process.cwd(), "node_modules", preloadPath));
2250+
preloadPath = "file://" + path.normalize(path.join(process.cwd(), "node_modules", preloadPath)).replace(/\\/g, "/");
22362251
}
22372252
}
22382253

2239-
preloadPath = preloadPath.replace(/\\/g, "/");
22402254
const locator = this.props.locator?.locator?.href ? this.props.locator.locator : undefined;
22412255
installNavigatorDOM(
22422256
this.props.r2Publication,

src/renderer/reader/pdf/driver.ts

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -109,34 +109,32 @@ export function pdfMount(
109109
createOrGetPdfEventBus().dispatch("start", pdfPath);
110110
});
111111

112-
let preloadPath = "index_pdf.js";
112+
// (global as any).__dirname
113+
// BROKEN when index_reader.js is not served via file://
114+
// ... so instead window.location.href provides dist/index_reader.html which is co-located:
115+
// path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..")) etc.
116+
117+
const PDFPATH = "index_pdf.js";
118+
let preloadPath = PDFPATH;
113119
if (_PACKAGING === "1") {
114-
preloadPath = "file://" + path.normalize(path.join((global as any).__dirname, preloadPath));
120+
preloadPath = "file://" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", PDFPATH)).replace(/\\/g, "/");
115121
} else {
116-
if (_RENDERER_PDF_WEBVIEW_BASE_URL === "file://") {
122+
if (_RENDERER_PDF_WEBVIEW_BASE_URL === "filex://host/") {
117123
// dist/prod mode (without WebPack HMR Hot Module Reload HTTP server)
118-
preloadPath = "file://" +
119-
path.normalize(path.join((global as any).__dirname, _DIST_RELATIVE_URL, preloadPath));
124+
preloadPath = "file://" + path.normalize(path.join(window.location.pathname.replace(/^\/\//, "/"), "..", PDFPATH)).replace(/\\/g, "/");
125+
126+
// const debugStr = `[[PDF DRIVER ${preloadPath} >>> ${window.location.href} *** ${window.location.pathname} === ${process.cwd()} ^^^ ${(global as any).__dirname} --- ${_DIST_RELATIVE_URL} @@@ ${preloadPath}]]`;
127+
// if (document.body.firstElementChild) {
128+
// document.body.innerText = debugStr;
129+
// } else {
130+
// document.body.innerText += debugStr;
131+
// }
120132
} else {
121133
// dev/debug mode (with WebPack HMR Hot Module Reload HTTP server)
122-
preloadPath = "file://" + path.normalize(path.join(process.cwd(), "dist", preloadPath));
134+
preloadPath = "file://" + path.normalize(path.join(process.cwd(), "dist", PDFPATH));
135+
preloadPath = preloadPath.replace(/\\/g, "/");
123136
}
124137
}
125-
preloadPath = preloadPath.replace(/\\/g, "/");
126-
// let htmlPath = "index_pdf.html";
127-
// if (_PACKAGING === "1") {
128-
// htmlPath = "file://" + path.normalize(path.join((global as any).__dirname, htmlPath));
129-
// } else {
130-
// if (_RENDERER_PDF_WEBVIEW_BASE_URL === "file://") {
131-
// // dist/prod mode (without WebPack HMR Hot Module Reload HTTP server)
132-
// htmlPath = "file://" +
133-
// path.normalize(path.join((global as any).__dirname, _DIST_RELATIVE_URL, htmlPath));
134-
// } else {
135-
// // dev/debug mode (with WebPack HMR Hot Module Reload HTTP server)
136-
// htmlPath = "file://" + path.normalize(path.join(process.cwd(), "dist", htmlPath));
137-
// }
138-
// }
139-
// htmlPath = htmlPath.replace(/\\/g, "/");
140138

141139
webview.setAttribute("style",
142140
"display: flex; margin: 0; padding: 0; box-sizing: border-box; position: absolute; left: 0; right: 0; bottom: 0; top: 0;");

webpack.config-preprocessor-directives.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ const isVisualStudioCodeLaunch = process.env.VSCODE_LAUNCH || "false";
1616

1717
const isContinuousIntegrationDeploy = process.env.GITHUB_TOKEN_RELEASE_PUBLISH ? true : false;
1818

19-
const rendererLibraryBaseUrl = isDev ? "http://localhost:" + portApp + "/" : "file://";
19+
const rendererLibraryBaseUrl = isDev ? "http://localhost:" + portApp + "/" : "filex://host/";
2020

21-
const rendererReaderBaseUrl = isDev ? "http://localhost:" + portReader + "/" : "file://";
21+
const rendererReaderBaseUrl = isDev ? "http://localhost:" + portReader + "/" : "filex://host/";
2222

23-
const rendererPdfWebviewBaseUrl = isDev ? "http://localhost:" + portPdfWebview + "/" : "file://";
23+
const rendererPdfWebviewBaseUrl = isDev ? "http://localhost:" + portPdfWebview + "/" : "filex://host/";
2424

2525
const isPackaging = process.env.PACKAGING || "0";
2626

0 commit comments

Comments
 (0)