fix(preview): make HTML iframe links navigate (zddc-server-backed archive)
User report: opening an .html file with a '../.archive/' hyperlink in
a new tab works (zddc-server intercepts and serves the right file),
but clicking the same link inside the file previewer does nothing.
Two combined causes:
1. The previewer's iframe was loaded from a blob: URL (built from
the file's bytes). Relative URLs in the iframe resolve relative
to the blob URL — '../.archive/X.html' becomes 'blob:.../.archive/
X.html', which is gibberish. The browser never sends a request to
the server, so the .archive interception never fires.
2. sandbox="" disables every iframe capability including popups,
so even <a target=_blank> is silently swallowed.
Fix per tool:
- archive (table.js): for HTML preview, use file.url (the real
server URL) directly when available; fall back to blob only for
File-System-Access-API mode where there's no server to intercept
anyway. Now relative links in archived HTMLs resolve against the
actual server origin and the .archive interception fires as
designed. Sandbox loosens to allow-same-origin + allow-popups +
allow-popups-to-escape-sandbox so resources within the iframe
load and link clicks (default target / target=_blank / middle-
click) work normally. allow-scripts is intentionally NOT set —
archived HTML still cannot run JS in the popup's origin.
- transmittal (files-preview.js) + classifier (preview.js): same
sandbox loosening for consistency. These tools' files are
typically local (FileSystemAccessAPI), so the file.url branch
doesn't apply — relative URLs that depend on a server still
won't resolve in local mode (intrinsic limitation, no server).
Tested behavior preserved:
- PDFs: unchanged (no sandbox, browser's PDF viewer handles).
- Images / docx / xlsx / tiff / zip / text: unchanged.
- HTML in zddc-server-backed archive: relative '../.archive/' links
now navigate the iframe to the correct target file.
This commit is contained in:
parent
2820dffeaa
commit
915ab8a87a
3 changed files with 37 additions and 12 deletions
|
|
@ -347,7 +347,26 @@
|
|||
const ext = file.extension.toLowerCase();
|
||||
|
||||
try {
|
||||
const url = await getFileBlobUrl(file);
|
||||
// For HTML preview, prefer the file's real server URL over a
|
||||
// blob URL when available (zddc-server-backed archives have
|
||||
// file.url set; local FileSystemAccessAPI mode doesn't).
|
||||
//
|
||||
// Why it matters: HTML files in an archive often link to
|
||||
// sibling/parent paths via relative URLs — e.g.
|
||||
// ../.archive/<tracking>.html — which zddc-server intercepts
|
||||
// and resolves. From a blob: URL the relative resolution
|
||||
// produces blob:.../.archive/X.html, which never reaches the
|
||||
// server. Loading the iframe from the actual https://zddc.../
|
||||
// URL means relative links resolve back to the server and the
|
||||
// .archive interception fires as designed.
|
||||
//
|
||||
// Other types (pdf, images rendered via canvas / iframe etc.)
|
||||
// are content-only — they don't depend on relative URLs — so
|
||||
// a blob URL is fine.
|
||||
const isHtml = ext === 'html' || ext === 'htm';
|
||||
const url = (isHtml && file.url)
|
||||
? file.url
|
||||
: await getFileBlobUrl(file);
|
||||
|
||||
// Mirror the parent window's theme in the popup
|
||||
const parentTheme = document.documentElement.getAttribute('data-theme') || '';
|
||||
|
|
@ -511,7 +530,7 @@
|
|||
<button class="btn" onclick="downloadFile()">Download</button>
|
||||
</div>
|
||||
${(ext === 'pdf' || ext === 'html' || ext === 'htm')
|
||||
? '<iframe src="' + url + '"' + (ext === 'pdf' ? '' : ' sandbox=""') + '></iframe>'
|
||||
? '<iframe src="' + url + '"' + (ext === 'pdf' ? '' : ' sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"') + '></iframe>'
|
||||
: '<div id="previewContent"><div class="loading">Loading preview...</div></div>'}
|
||||
<script>
|
||||
var blobUrl = "${url}";
|
||||
|
|
|
|||
|
|
@ -277,10 +277,11 @@
|
|||
case 'pdf':
|
||||
return `<iframe src="${blobUrl}#view=FitV"></iframe>`;
|
||||
case 'html':
|
||||
// Render the HTML natively (not as literal text). sandbox=""
|
||||
// disables scripts / forms / top-level nav / plugins so an
|
||||
// archived HTML file can't run code in the popup's origin.
|
||||
return `<iframe src="${blobUrl}" sandbox=""></iframe>`;
|
||||
// Render the HTML natively (not as literal text). Sandbox
|
||||
// flags allow same-origin resource loads + opening links
|
||||
// in real new tabs (target=_blank / middle-click), but
|
||||
// NOT allow-scripts — archived HTML cannot run JS.
|
||||
return `<iframe src="${blobUrl}" sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"></iframe>`;
|
||||
case 'image':
|
||||
return `<img src="${blobUrl}" alt="${escapeHtml(file.originalFilename)}" />`;
|
||||
case 'text':
|
||||
|
|
|
|||
|
|
@ -126,15 +126,20 @@
|
|||
// PDF and HTML preview natively in an iframe — for HTML this
|
||||
// means the page is RENDERED (not shown as literal source text);
|
||||
// the blob's MIME type ('text/html', see getMimeType) tells the
|
||||
// browser to render. `sandbox=""` on the HTML iframe disables
|
||||
// all dangerous capabilities (scripts, top-level navigation,
|
||||
// forms, plugins, popups) since these are arbitrary archived
|
||||
// files we don't trust to run JS in the parent's origin.
|
||||
// browser to render. The HTML iframe is sandboxed:
|
||||
// - allow-same-origin: needed so the iframe's resource loads
|
||||
// (img / link / etc.) work normally for same-origin paths.
|
||||
// - allow-popups + allow-popups-to-escape-sandbox: clicking
|
||||
// <a target="_blank"> (or middle-click) opens a real new tab
|
||||
// with full browser features. Without these, link clicks
|
||||
// intended for new tabs silently no-op.
|
||||
// - NO allow-scripts: archived HTML cannot run JS in this
|
||||
// popup's origin.
|
||||
var contentHtml;
|
||||
if (ext === 'pdf') {
|
||||
contentHtml = '<iframe src="' + safeHref + '"></iframe>';
|
||||
} else if (ext === 'html' || ext === 'htm') {
|
||||
contentHtml = '<iframe src="' + safeHref + '" sandbox=""></iframe>';
|
||||
contentHtml = '<iframe src="' + safeHref + '" sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox"></iframe>';
|
||||
} else {
|
||||
contentHtml = '<div id="previewContent"><div class="loading">Loading preview...</div></div>';
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue