Skip to content

Address issue #405: never use the document file as the preview base URL#456

Merged
schuyler merged 1 commit into
mainfrom
claude/funny-brahmagupta-waumpb
Jun 22, 2026
Merged

Address issue #405: never use the document file as the preview base URL#456
schuyler merged 1 commit into
mainfrom
claude/funny-brahmagupta-waumpb

Conversation

@schuyler

@schuyler schuyler commented Jun 22, 2026

Copy link
Copy Markdown
Owner

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.mpreviewSafeBaseURL: 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 Fix blank preview for documents with execute bit set #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.

WebKit on macOS 26 can silently blank the preview when its file:// base
resource is the real document file, based on metadata WebKit inspects but
the editor does not (the execute bit set by sync clients, or stale
TCC/provenance state on long-lived files). A new document renders fine, but
reopening a saved file leaves the preview empty.

Generalize previewSafeBaseURL: so the real document file is never handed to
WebKit as the base resource. Whenever the base URL points at a real file,
substitute a non-existent sentinel in the same directory. 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 this leaves all of that
unchanged while covering the broader blank-preview class beyond the
execute-bit-only heuristic.

Add unit tests covering regular files, executable files, directories,
non-existent paths, non-file URLs, and nil, plus an opened-file lifecycle
test asserting rendererBaseURL: never returns the real document file. Update
the changelog to describe the generalized fix.

Related to #405
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Report

Current Coverage: 60.57%

Coverage Details (Summary)
Name                                                                                                                                   Coverage            
-------------------------------------------------------------------------------------------------------------------------------------- ------------------- 
MASPreferences.bundle                                                                                                                  0.00% (0/0)         
MacDown 3000.app                                                                                                                       62.04% (8731/14073) 
    /Users/runner/work/macdown3000/macdown3000/MacDown/Code/Preferences/MPHtmlPreferencesViewController.m                              0.00% (0/113)       
        MPPrismDefaultThemeName                                                                                                        0.00% (0/2)         
        -[MPHtmlPreferencesViewController viewIdentifier]                                                                              0.00% (0/3)         
        -[MPHtmlPreferencesViewController toolbarItemImage]                                                                            0.00% (0/3)         
        -[MPHtmlPreferencesViewController toolbarItemLabel]                                                                            0.00% (0/2)         
        -[MPHtmlPreferencesViewController viewWillAppear]                                                                              0.00% (0/4)         
        -[MPHtmlPreferencesViewController changeStylesheet:]                                                                           0.00% (0/7)         
        -[MPHtmlPreferencesViewController changeHighlightingTheme:]                                                                    0.00% (0/7)         
        -[MPHtmlPreferencesViewController invokeStylesheetFunction:]                                                                   0.00% (0/23)        
        -[MPHtmlPreferencesViewController invokeHighlightingThemeFunction:]                                                            0.00% (0/33)        
        -[MPHtmlPreferencesViewController loadStylesheets]                                                                             0.00% (0/14)        
        -[MPHtmlPreferencesViewController loadHighlightingThemes]                                                                      0.00% (0/15)        
    /Users/runner/work/macdown3000/macdown3000/MacDown/Code/Document/MPAsset.m                                                         93.69% (104/111)    
        -[MPAsset typeName]                                                                                                            100.00% (3/3)       
        -[MPAsset defaultTypeName]                                                                                                     100.00% (3/3)       
        +[MPAsset assetWithURL:andType:]                                                                                               100.00% (3/3)       
        -[MPAsset initWithURL:andType:]                                                                                                87.50% (7/8)        
        -[MPAsset init]                                                                                                                100.00% (3/3)       
        -[MPAsset templateForOption:]                                                                                                  0.00% (0/6)         
        -[MPAsset htmlForOption:]                                                                                                      100.00% (28/28)     
        -[MPStyleSheet defaultTypeName]                                                                                                100.00% (3/3)       
        +[MPStyleSheet CSSWithURL:]                                                                                                    100.00% (3/3)       
        -[MPStyleSheet templateForOption:]                                                                                             100.00% (20/20)     
        -[MPScript defaultTypeName]                                                                                                    100.00% (3/3)       
        +[MPScript javaScriptWithURL:]                                                                                                 100.00% (3/3)       
        -[MPScript templateForOption:]                                                                                                 100.00% (20/20)     
        -[MPEmbeddedScript htmlForOption:]                                                                                             100.00% (5/5)       
    /Users/runner/work/macdown3000/macdown3000/Dependency/peg-markdown-highlight/HGMarkdownHighlightingStyle.m                         87.69% (57/65)      
        +[HGMarkdownHighlightingStyle colorFromARGBColor:]                                                                             100.00% (6/6)       
        -[HGMarkdownHighlightingStyle initWithType:attributesToAdd:toRemove:fontTraitsToAdd:]                                          88.89% (8/9)        
        -[HGMarkdownHighlightingStyle initWithStyleAttributes:baseFont:]                                                               86.00% (43/50)      
    /Users/runner/work/macdown3000/macdown3000/MacDown/Code/Utility/FileURLInlining.m                                                  26.60% (25/94)      
        +[FileURLInlining inlineFromIterable:]                                                                                         0.00% (0/11)        
        -[FileURLInlining initWithURL:]                                                                                                0.00% (0/6)         
        -[FileURLInlining init]                                                                                                        100.00% (3/3)       
        -[FileURLInlining inlineContent]                                                                                               0.00% (0/9)         
        -[FileURLInlining imageContent]                                                                                                0.00% (0/12)        
        +[FileURLInlining mimeTypeForFilePath:]                                                                                        100.00% (22/22)     
        -[FileURLInlining clippingContent]                                                                                             0.00% (0/4)         
        +[FileURLInlining isTextClippingAtPath:]                                                                                       0.00% (0/12)        
        +[FileURLInlining textContentFromClipping:]                                                                                    0.00% (0/15)        
    /Users/runner/work/macdown3000/macdown3000/MacDown/Code/Utility/MPResourceWatcherSet.m                                             93.62% (88/94)      
        -[MPResourceWatcherSet init]                                                                                                   100.00% (6/6)       
        -[MPResourceWatcherSet dealloc]                                                                                                0.00% (0/3)         
        -[MPResourceWatcherSet watchedPaths]                                                                                           100.00% (3/3)       
        -[MPResourceWatcherSet updateWatchedPaths:]                                                                                    100.00% (16/16)     

... (3187 more lines truncated)

📊 **Full coverage report available in workflow artifacts**

@schuyler schuyler merged commit 4ea366e into main Jun 22, 2026
10 checks passed
@schuyler schuyler deleted the claude/funny-brahmagupta-waumpb branch June 22, 2026 07:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants