Fix blank preview for documents with execute bit set#454
Merged
Conversation
On macOS 26, WebKit silently refuses to load file:// preview content whose base resource is an executable file. Some sync clients (notably OneDrive) set the execute bit (0700) on every synced file and revert any manual `chmod -x`, so externally-originated documents render blank in the preview pane even though the editor reads them fine. Add -[MPDocument previewSafeBaseURL:], which substitutes a non-existent sentinel in the same directory when the document file is executable. The preview WebView no longer sees an executable base resource, while relative resource resolution and the MPURLSecurityPolicy scope check — both keyed off the base URL's parent directory — are unchanged. Related to #405. Claude-Session: https://claude.ai/code/session_01R7hAw42Bbyo9kMrtUGxyN2
Contributor
Code Coverage ReportCurrent Coverage: 60.46% Coverage Details (Summary) |
This was referenced Jun 22, 2026
schuyler
added a commit
that referenced
this pull request
Jun 22, 2026
…RL (#456) ## Summary A **new** document previews fine, but **opening a saved `.md` file** leaves the preview empty while the editor works. On macOS 26, WebKit silently refuses to load the `file://` preview content when its *base resource* is the real document file, based on metadata WebKit inspects but the editor (which reads bytes via `NSDocument`) does not — e.g. the execute bit set by sync clients like OneDrive (#431/#454), or stale TCC/provenance state carried by a file's inode across its history (#431). There's no signal in the document bytes, so there is no reliable runtime detector. This generalizes the previously execute-bit-only `previewSafeBaseURL:` so the **real document file is never handed to WebKit as the base resource**. Whenever the base URL points at a real file, a non-existent sentinel in the *same directory* is substituted. The base URL is only ever needed for the document's directory — relative resource resolution, `MPLocalFilePathsInHTML`, cache busting, and the `MPURLSecurityPolicy` scope check (which keys off the parent directory) — so all of that is unchanged, while the broader blank-preview class is covered. ### Changes - **`MPDocument.m`** — `previewSafeBaseURL:` now substitutes the sentinel for any real file base URL (not just executable ones); directories, non-existent paths, and non-file URLs pass through. Updated the explanatory comment. Single-point change; both preview load paths already route through this method. - **`MPDocumentIOTests.m`** — unit tests for regular file, executable file, directory, non-existent path, non-file URL, and nil. - **`MPDocumentLifecycleTests.m`** — opened-file test asserting `rendererBaseURL:` never returns the real document file when a `fileURL` is set. - **`CHANGELOG.md`** — updated the entry to describe the generalized fix. ## Related Issue Related to #405 (and the same root cause as #431). ## Manual Testing Plan On macOS 26 with a sync client (or any file that previously rendered blank on open): 1. Create a new document, type Markdown, confirm the preview renders. 2. Save it as a `.md` file, close the window, reopen the file → the preview should now render (previously empty). 3. Set the execute bit on a saved `.md` file (`chmod +x file.md`) and open it → preview should render. 4. Open a document that references a relative local image (`[](image.png)` alongside the file) → the image should still load, confirming relative resource resolution is unaffected by the sentinel base URL. 5. Edit an opened document → the DOM-replacement fast path should keep working (base URL is deterministic across renders). ## Review Notes - The key assumption — relative resources still load with a non-existent same-directory sentinel base URL — is already proven and shipped by #454 for executable files; this PR applies the same mechanism to all files. - `MPURLSecurityPolicy.isExecutableOrAppBundleAtURL:` is still used by the resource security checks, so it remains in use. - WebView rendering can't be asserted in headless CI, so coverage is at the base-URL / delegate level. Co-authored-by: Claude <[email protected]>
4 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes an issue where Markdown previews render blank when the document file has the execute bit set. Some sync clients (notably OneDrive) automatically set the execute bit (0700) on synced files, causing WebKit to silently refuse to load the preview content.
Changes
MPDocument.m: Added
previewSafeBaseURL:method that detects executable files and substitutes a non-existent sentinel URL in the same directory. This allows WebKit to render the preview while preserving all path resolution and security scope behavior.renderer:didProduceHTMLOutput:to use the safe base URLrendererBaseURL:to use the safe base URLMPDocumentIOTests.m: Added two test cases to verify the fix:
testPreviewSafeBaseURLLeavesNonExecutableFileUnchanged: Ensures normal files are used as-istestPreviewSafeBaseURLRewritesExecutableFile: Verifies executable files are replaced with a sentinel in the same directoryCHANGELOG.md: Documented the fix
Implementation Details
The solution works by:
MPURLSecurityPolicy.macdown-preview-base) in the same directoryRelated to #431, #405
https://claude.ai/code/session_01R7hAw42Bbyo9kMrtUGxyN2