feat(mac): remove mime and extension whitelist
Some checks failed
Keyman Build Summary / Summarize build status checks (push) Has been cancelled

instead check whether content is in the directory
with the rest of the package
This commit is contained in:
Shawn Schantz 2026-09-03 21:32:01 -04:00
parent f608764000
commit 221c07a47a

View file

@ -13,9 +13,7 @@ import WebKit
import KeymanSettings
public struct PackageContentWebView: NSViewRepresentable {
private static let allowedLocalExtensions = ["pdf", "txt", "png", "jpg", "jpeg"]
private static let allowedMimeTypes = ["application/pdf", "text/plain", "image/jpeg", "image/png"]
let packageFileUrl: URL
// create the AppKit view instance
@ -24,12 +22,18 @@ public struct PackageContentWebView: NSViewRepresentable {
// Connect the delegate to catch link clicks
webView.navigationDelegate = context.coordinator
return webView
}
// update the view when SwiftUI state changes
/**
* update the view when SwiftUI state changes
*/
public func updateNSView(_ nsView: WKWebView, context: Context) {
// first update the parent in the coordinator so we know which
// package we are displaying content for
context.coordinator.parent = self
let request = URLRequest(url: packageFileUrl)
// only load the request if it's not already loading/loaded to prevent infinite loops
@ -44,19 +48,27 @@ public struct PackageContentWebView: NSViewRepresentable {
* Coordinator acts as the WKNavigationDelegate
*/
public func makeCoordinator() -> Coordinator {
Coordinator()
Coordinator(self)
}
/**
* If a url links to the web rather than locally, open it in the default browser
*/
@MainActor
public class Coordinator: NSObject, WKNavigationDelegate {
var parent: PackageContentWebView
init(_ parent: PackageContentWebView) {
self.parent = parent
}
/**
* If a url links to the web rather than locally, open it in the default browser
* Must force load links in the package content that change the main frame
* because WKWebView's security sandbox will block loading of new pages
*/
public func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping @MainActor @Sendable (WKNavigationActionPolicy) -> Void) {
// if not url, cancel
// if no url, cancel
guard let url = navigationAction.request.url else {
decisionHandler(.cancel)
return
@ -64,59 +76,47 @@ public struct PackageContentWebView: NSViewRepresentable {
// if not user-activated, pass through, e.g. for redirects
guard navigationAction.navigationType == .linkActivated else {
decisionHandler(.allow)
return
}
// load local files in webview, force-loading some common file types
if url.isFileURL {
let fileExtension = url.pathExtension.lowercased()
if PackageContentWebView.allowedLocalExtensions.contains(fileExtension) {
// cancel the automatic navigation (which fails silently)
decisionHandler(.cancel)
// force-load the file into the web view frame
webView.loadFileURL(url, allowingReadAccessTo: url.deletingLastPathComponent())
return
}
print("url not user activated \(url.path())")
decisionHandler(.allow)
return
}
// handle external links by opening in web browser
// load local files in webview, force-loading files that are in the same directory
if url.isFileURL {
let standardizedIncomingUrl = url.standardizedFileURL
let packageDirectoryUrl = parent.packageFileUrl.deletingLastPathComponent()
let standardizedPackageDirectoryUrl = packageDirectoryUrl.standardizedFileURL
// check whether the file is from the current package
if standardizedIncomingUrl.path.hasPrefix(standardizedPackageDirectoryUrl.path) {
decisionHandler(.cancel) // Cancel regular navigation
// Force load with read permissions of package directory
webView.loadFileURL(standardizedIncomingUrl, allowingReadAccessTo: standardizedPackageDirectoryUrl)
return
} else {
// block local files outside of your target directory, not sure how we would receive one
decisionHandler(.cancel)
return
}
}
// not a file URL: handle external links by opening in web browser
decisionHandler(.cancel)
var externalUrl = url
// strip "link:" prefix if present
let urlString = url.absoluteString
if urlString.hasPrefix("link:https://") || urlString.hasPrefix("link:http://") {
let cleanString = urlString.replacingOccurrences(of: "link:", with: "")
if let cleanUrl = URL(string: cleanString) {
externalUrl = cleanUrl
}
let cleanString = urlString.replacingOccurrences(of: "link:", with: "")
if let cleanUrl = URL(string: cleanString) {
externalUrl = cleanUrl
}
}
// Open the external link in the default browser
NSWorkspace.shared.open(externalUrl)
}
}
/**
* Allow display of some common file types that may be linked in the package help
*/
public func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
if let mimeType = navigationResponse.response.mimeType {
if PackageContentWebView.allowedMimeTypes.contains(mimeType) {
decisionHandler(.allow)
return
}
}
decisionHandler(.allow)
}
}