From 1d6aa3209173b85c6206f23a424aa3a349bd360d Mon Sep 17 00:00:00 2001 From: "kormiltsyn.roman" Date: Fri, 6 Mar 2026 13:59:58 +0300 Subject: [PATCH 1/2] fix: unable to open local pdfs --- js/preload/default.js | 16 ++++++++ main/download.js | 10 +++++ main/localPDFAccess.js | 57 +++++++++++++++++++++++++++++ pages/pdfViewer/documentRequest.mjs | 18 +++++++++ pages/pdfViewer/viewer.js | 39 +++++++++++++++++++- 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 main/localPDFAccess.js create mode 100644 pages/pdfViewer/documentRequest.mjs diff --git a/js/preload/default.js b/js/preload/default.js index 7b767285f..665403823 100644 --- a/js/preload/default.js +++ b/js/preload/default.js @@ -61,4 +61,20 @@ window.addEventListener('message', function (e) { if (e.data?.message === 'downloadFile') { ipc.send('downloadFile', e.data.url) } + + if (e.data?.message === 'readLocalPDF') { + var requestId = e.data.requestId + + if (typeof e.data.url !== 'string') { + window.postMessage({ message: 'readLocalPDFResult', requestId, error: 'invalid PDF URL' }, 'min://app') + return + } + + ipc.invoke('readLocalPDF', e.data.url).then(function (fileData) { + var data = new Uint8Array(fileData) + window.postMessage({ message: 'readLocalPDFResult', requestId, data: data.buffer }, 'min://app') + }).catch(function (err) { + window.postMessage({ message: 'readLocalPDFResult', requestId, error: err?.message || 'unable to read local PDF' }, 'min://app') + }) + } }) diff --git a/main/download.js b/main/download.js index 0400bbb5a..4fbe7c4f5 100644 --- a/main/download.js +++ b/main/download.js @@ -1,4 +1,14 @@ const currrentDownloadItems = {} +var getAllowedLocalPDFPath = require('./main/localPDFAccess.js').getAllowedLocalPDFPath + +ipc.handle('readLocalPDF', async function (event, requestedURL) { + if (!event.senderFrame) { + throw new Error('blocked local PDF read request') + } + + var filePath = getAllowedLocalPDFPath(event.senderFrame.url, requestedURL) + return fs.promises.readFile(filePath) +}) ipc.on('cancelDownload', function (e, path) { if (currrentDownloadItems[path]) { diff --git a/main/localPDFAccess.js b/main/localPDFAccess.js new file mode 100644 index 000000000..e67dd21a7 --- /dev/null +++ b/main/localPDFAccess.js @@ -0,0 +1,57 @@ +var fileURLToPath = require('url').fileURLToPath + +function isPDFViewerURL (url) { + return typeof url === 'string' && url.startsWith('min://app/pages/pdfViewer/index.html') +} + +function getViewerSourceURL (viewerURL) { + if (!isPDFViewerURL(viewerURL)) { + return null + } + + try { + var sourceURL = new URLSearchParams(new URL(viewerURL).search).get('url') + if (!sourceURL) { + return null + } + + return new URL(sourceURL) + } catch (e) { + return null + } +} + +function getAllowedLocalPDFPath (senderFrameURL, requestedURL) { + if (!isPDFViewerURL(senderFrameURL)) { + throw new Error('blocked local PDF read request') + } + + if (typeof requestedURL !== 'string') { + throw new Error('invalid PDF URL') + } + + var requestedFileURL + try { + requestedFileURL = new URL(requestedURL) + } catch (e) { + throw new Error('invalid PDF URL') + } + + if (requestedFileURL.protocol !== 'file:') { + throw new Error('invalid PDF URL protocol') + } + + var sourceURL = getViewerSourceURL(senderFrameURL) + + if (!sourceURL || sourceURL.protocol !== 'file:' || sourceURL.toString() !== requestedFileURL.toString()) { + throw new Error('blocked local PDF read request') + } + + return fileURLToPath(requestedFileURL) +} + +module.exports = { + isPDFViewerURL, + getViewerSourceURL, + getAllowedLocalPDFPath +} diff --git a/pages/pdfViewer/documentRequest.mjs b/pages/pdfViewer/documentRequest.mjs new file mode 100644 index 000000000..a6103f5f1 --- /dev/null +++ b/pages/pdfViewer/documentRequest.mjs @@ -0,0 +1,18 @@ +export function getPDFDocumentRequest (targetURL, requestLocalPDFData) { + if (typeof targetURL !== 'string') { + return Promise.reject(new Error('invalid PDF URL')) + } + + if (targetURL.startsWith('file://')) { + return requestLocalPDFData(targetURL).then(function (fileData) { + return { + data: fileData + } + }) + } + + return Promise.resolve({ + url: targetURL, + withCredentials: true + }) +} diff --git a/pages/pdfViewer/viewer.js b/pages/pdfViewer/viewer.js index cc6106b17..6f91b0779 100644 --- a/pages/pdfViewer/viewer.js +++ b/pages/pdfViewer/viewer.js @@ -1,9 +1,11 @@ import '../../node_modules/pdfjs-dist/build/pdf.min.mjs' import '../../node_modules/pdfjs-dist/web/pdf_viewer.mjs' +import { getPDFDocumentRequest } from './documentRequest.mjs' pdfjsLib.GlobalWorkerOptions.workerSrc = '../../node_modules/pdfjs-dist/build/pdf.worker.mjs' var url = new URLSearchParams(window.location.search.replace('?', '')).get('url') +var localPDFRequestId = 0 var eventBus = new pdfjsViewer.EventBus() @@ -239,7 +241,42 @@ function setUpPageAnnotationLayer (pageView) { } } -pdfjsLib.getDocument({ url: url, withCredentials: true }).promise.then(async function (_pdf) { +function requestLocalPDFData (fileURL) { + return new Promise(function (resolve, reject) { + var requestId = ++localPDFRequestId + + function onResponse (e) { + if (e.origin !== 'min://app') { + return + } + + if (e.data?.message !== 'readLocalPDFResult' || e.data.requestId !== requestId) { + return + } + + window.removeEventListener('message', onResponse) + + if (e.data.error) { + reject(new Error(e.data.error)) + return + } + + if (!e.data.data) { + reject(new Error('missing local PDF data')) + return + } + + resolve(new Uint8Array(e.data.data)) + } + + window.addEventListener('message', onResponse) + window.postMessage({ message: 'readLocalPDF', requestId, url: fileURL }, 'min://app') + }) +} + +getPDFDocumentRequest(url, requestLocalPDFData).then(function (request) { + return pdfjsLib.getDocument(request).promise +}).then(async function (_pdf) { pdf = _pdf pageCount = pdf.numPages From b18a6e1016566554123be3fef247685976765d63 Mon Sep 17 00:00:00 2001 From: "kormiltsyn.roman" Date: Fri, 6 Mar 2026 14:26:58 +0300 Subject: [PATCH 2/2] fix: inline local PDF helpers to avoid missing-module crash in packaged app --- main/download.js | 51 +++++++++++++++++++++++++- main/localPDFAccess.js | 57 ----------------------------- pages/pdfViewer/documentRequest.mjs | 18 --------- pages/pdfViewer/viewer.js | 20 +++++++++- 4 files changed, 69 insertions(+), 77 deletions(-) delete mode 100644 main/localPDFAccess.js delete mode 100644 pages/pdfViewer/documentRequest.mjs diff --git a/main/download.js b/main/download.js index 4fbe7c4f5..e82092a0b 100644 --- a/main/download.js +++ b/main/download.js @@ -1,5 +1,54 @@ const currrentDownloadItems = {} -var getAllowedLocalPDFPath = require('./main/localPDFAccess.js').getAllowedLocalPDFPath + +function isPDFViewerURL (url) { + return typeof url === 'string' && url.startsWith('min://app/pages/pdfViewer/index.html') +} + +function getViewerSourceURL (viewerURL) { + if (!isPDFViewerURL(viewerURL)) { + return null + } + + try { + var sourceURL = new URLSearchParams(new URL(viewerURL).search).get('url') + if (!sourceURL) { + return null + } + + return new URL(sourceURL) + } catch (e) { + return null + } +} + +function getAllowedLocalPDFPath (senderFrameURL, requestedURL) { + if (!isPDFViewerURL(senderFrameURL)) { + throw new Error('blocked local PDF read request') + } + + if (typeof requestedURL !== 'string') { + throw new Error('invalid PDF URL') + } + + var requestedFileURL + try { + requestedFileURL = new URL(requestedURL) + } catch (e) { + throw new Error('invalid PDF URL') + } + + if (requestedFileURL.protocol !== 'file:') { + throw new Error('invalid PDF URL protocol') + } + + var sourceURL = getViewerSourceURL(senderFrameURL) + + if (!sourceURL || sourceURL.protocol !== 'file:' || sourceURL.toString() !== requestedFileURL.toString()) { + throw new Error('blocked local PDF read request') + } + + return require('url').fileURLToPath(requestedFileURL) +} ipc.handle('readLocalPDF', async function (event, requestedURL) { if (!event.senderFrame) { diff --git a/main/localPDFAccess.js b/main/localPDFAccess.js deleted file mode 100644 index e67dd21a7..000000000 --- a/main/localPDFAccess.js +++ /dev/null @@ -1,57 +0,0 @@ -var fileURLToPath = require('url').fileURLToPath - -function isPDFViewerURL (url) { - return typeof url === 'string' && url.startsWith('min://app/pages/pdfViewer/index.html') -} - -function getViewerSourceURL (viewerURL) { - if (!isPDFViewerURL(viewerURL)) { - return null - } - - try { - var sourceURL = new URLSearchParams(new URL(viewerURL).search).get('url') - if (!sourceURL) { - return null - } - - return new URL(sourceURL) - } catch (e) { - return null - } -} - -function getAllowedLocalPDFPath (senderFrameURL, requestedURL) { - if (!isPDFViewerURL(senderFrameURL)) { - throw new Error('blocked local PDF read request') - } - - if (typeof requestedURL !== 'string') { - throw new Error('invalid PDF URL') - } - - var requestedFileURL - try { - requestedFileURL = new URL(requestedURL) - } catch (e) { - throw new Error('invalid PDF URL') - } - - if (requestedFileURL.protocol !== 'file:') { - throw new Error('invalid PDF URL protocol') - } - - var sourceURL = getViewerSourceURL(senderFrameURL) - - if (!sourceURL || sourceURL.protocol !== 'file:' || sourceURL.toString() !== requestedFileURL.toString()) { - throw new Error('blocked local PDF read request') - } - - return fileURLToPath(requestedFileURL) -} - -module.exports = { - isPDFViewerURL, - getViewerSourceURL, - getAllowedLocalPDFPath -} diff --git a/pages/pdfViewer/documentRequest.mjs b/pages/pdfViewer/documentRequest.mjs deleted file mode 100644 index a6103f5f1..000000000 --- a/pages/pdfViewer/documentRequest.mjs +++ /dev/null @@ -1,18 +0,0 @@ -export function getPDFDocumentRequest (targetURL, requestLocalPDFData) { - if (typeof targetURL !== 'string') { - return Promise.reject(new Error('invalid PDF URL')) - } - - if (targetURL.startsWith('file://')) { - return requestLocalPDFData(targetURL).then(function (fileData) { - return { - data: fileData - } - }) - } - - return Promise.resolve({ - url: targetURL, - withCredentials: true - }) -} diff --git a/pages/pdfViewer/viewer.js b/pages/pdfViewer/viewer.js index 6f91b0779..625e308b3 100644 --- a/pages/pdfViewer/viewer.js +++ b/pages/pdfViewer/viewer.js @@ -1,6 +1,5 @@ import '../../node_modules/pdfjs-dist/build/pdf.min.mjs' import '../../node_modules/pdfjs-dist/web/pdf_viewer.mjs' -import { getPDFDocumentRequest } from './documentRequest.mjs' pdfjsLib.GlobalWorkerOptions.workerSrc = '../../node_modules/pdfjs-dist/build/pdf.worker.mjs' @@ -274,6 +273,25 @@ function requestLocalPDFData (fileURL) { }) } +function getPDFDocumentRequest (targetURL, requestLocalPDFData) { + if (typeof targetURL !== 'string') { + return Promise.reject(new Error('invalid PDF URL')) + } + + if (targetURL.startsWith('file://')) { + return requestLocalPDFData(targetURL).then(function (fileData) { + return { + data: fileData + } + }) + } + + return Promise.resolve({ + url: targetURL, + withCredentials: true + }) +} + getPDFDocumentRequest(url, requestLocalPDFData).then(function (request) { return pdfjsLib.getDocument(request).promise }).then(async function (_pdf) {