fancy new browser
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add error page

+120 -17
+1 -3
App/Mere.entitlements
··· 1 1 <?xml version="1.0" encoding="UTF-8"?> 2 2 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> 3 3 <plist version="1.0"> 4 - <dict> 5 - <!-- Sandbox disabled for development - WebKit WebContent process needs too many exceptions --> 6 - </dict> 4 + <dict/> 7 5 </plist>
+28 -2
Mere.xcodeproj/project.pbxproj
··· 275 275 B3A100B001000000 /* Debug */ = { 276 276 isa = XCBuildConfiguration; 277 277 buildSettings = { 278 + AUTOMATION_APPLE_EVENTS = NO; 278 279 CODE_SIGN_ENTITLEMENTS = App/Mere.entitlements; 279 280 CODE_SIGN_STYLE = Automatic; 280 281 COMBINE_HIDPI_IMAGES = YES; 281 282 CURRENT_PROJECT_VERSION = 1; 282 283 DEAD_CODE_STRIPPING = YES; 283 - ENABLE_HARDENED_RUNTIME = YES; 284 + ENABLE_HARDENED_RUNTIME = NO; 285 + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO; 286 + ENABLE_RESOURCE_ACCESS_CALENDARS = NO; 287 + ENABLE_RESOURCE_ACCESS_CAMERA = NO; 288 + ENABLE_RESOURCE_ACCESS_CONTACTS = NO; 289 + ENABLE_RESOURCE_ACCESS_LOCATION = NO; 290 + ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO; 284 291 GENERATE_INFOPLIST_FILE = NO; 285 292 INFOPLIST_FILE = App/Info.plist; 286 293 INFOPLIST_KEY_CFBundleDisplayName = Mere; ··· 293 300 MARKETING_VERSION = 0.1.0; 294 301 PRODUCT_BUNDLE_IDENTIFIER = sh.dunkirk.mere; 295 302 PRODUCT_NAME = Mere; 303 + RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; 304 + RUNTIME_EXCEPTION_ALLOW_JIT = NO; 305 + RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO; 306 + RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO; 307 + RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO; 308 + RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO; 296 309 SWIFT_EMIT_LOC_STRINGS = YES; 297 310 SWIFT_VERSION = 6.0; 298 311 }; ··· 301 314 B3A100B101000000 /* Release */ = { 302 315 isa = XCBuildConfiguration; 303 316 buildSettings = { 317 + AUTOMATION_APPLE_EVENTS = NO; 304 318 CODE_SIGN_ENTITLEMENTS = App/Mere.entitlements; 305 319 CODE_SIGN_STYLE = Automatic; 306 320 COMBINE_HIDPI_IMAGES = YES; 307 321 CURRENT_PROJECT_VERSION = 1; 308 322 DEAD_CODE_STRIPPING = YES; 309 - ENABLE_HARDENED_RUNTIME = YES; 323 + ENABLE_HARDENED_RUNTIME = NO; 324 + ENABLE_RESOURCE_ACCESS_AUDIO_INPUT = NO; 325 + ENABLE_RESOURCE_ACCESS_CALENDARS = NO; 326 + ENABLE_RESOURCE_ACCESS_CAMERA = NO; 327 + ENABLE_RESOURCE_ACCESS_CONTACTS = NO; 328 + ENABLE_RESOURCE_ACCESS_LOCATION = NO; 329 + ENABLE_RESOURCE_ACCESS_PHOTO_LIBRARY = NO; 310 330 GENERATE_INFOPLIST_FILE = NO; 311 331 INFOPLIST_FILE = App/Info.plist; 312 332 INFOPLIST_KEY_CFBundleDisplayName = Mere; ··· 319 339 MARKETING_VERSION = 0.1.0; 320 340 PRODUCT_BUNDLE_IDENTIFIER = sh.dunkirk.mere; 321 341 PRODUCT_NAME = Mere; 342 + RUNTIME_EXCEPTION_ALLOW_DYLD_ENVIRONMENT_VARIABLES = NO; 343 + RUNTIME_EXCEPTION_ALLOW_JIT = NO; 344 + RUNTIME_EXCEPTION_ALLOW_UNSIGNED_EXECUTABLE_MEMORY = NO; 345 + RUNTIME_EXCEPTION_DEBUGGING_TOOL = NO; 346 + RUNTIME_EXCEPTION_DISABLE_EXECUTABLE_PAGE_PROTECTION = NO; 347 + RUNTIME_EXCEPTION_DISABLE_LIBRARY_VALIDATION = NO; 322 348 SWIFT_EMIT_LOC_STRINGS = YES; 323 349 SWIFT_VERSION = 6.0; 324 350 };
+4 -1
Sources/MereCore/Tab.swift
··· 42 42 43 43 // MARK: - Navigation passthrough 44 44 45 - public func loadURL(_ url: URL) { content.loadURL(url) } 45 + public func loadURL(_ url: URL) { 46 + print("🔍 Tab.loadURL: \(url.absoluteString)") 47 + content.loadURL(url) 48 + } 46 49 public func goBack() { content.goBack() } 47 50 public func goForward() { content.goForward() } 48 51 public func reload() { content.reload() }
+18 -11
Sources/MereUI/BrowserWindowView.swift
··· 131 131 132 132 @ViewBuilder 133 133 private var contentArea: some View { 134 - if let tab = window.activeTab, tab.url != nil || tab.isLoading { 135 - ZStack { 134 + if let tab = window.activeTab { 135 + if let error = tab.navigationError { 136 + NavigationErrorView(error: error) 137 + .frame(maxWidth: .infinity, maxHeight: .infinity) 138 + } else if tab.url != nil || tab.isLoading { 136 139 WebContentView(content: tab.content) 137 140 .id(tab.id) 138 - 139 - if let error = tab.navigationError { 140 - NavigationErrorView(error: error) 141 - .padding() 142 - } 141 + } else { 142 + NewTabView(hasBackground: window.newTabBackgroundColor != nil) 143 143 } 144 - } else { 145 - NewTabView(hasBackground: window.newTabBackgroundColor != nil) 146 144 } 147 145 } 148 146 } ··· 401 399 .padding(.horizontal, 10) 402 400 .padding(.vertical, 3) 403 401 .onChange(of: window.activeTab?.url) { _, url in 404 - addressText = url?.absoluteString ?? "" 402 + // Show normalized URL in address bar (0.0.0.0 -> 127.0.0.1) 403 + var urlString = url?.absoluteString ?? "" 404 + urlString = urlString.replacingOccurrences(of: "0.0.0.0", with: "127.0.0.1", options: .caseInsensitive) 405 + addressText = urlString 405 406 } 406 407 .background( 407 408 RoundedRectangle(cornerRadius: 7) ··· 514 515 return false 515 516 } 516 517 518 + func normalizeURL(_ input: String) -> String { 519 + // Replace 0.0.0.0 with 127.0.0.1 (0.0.0.0 is blocked by WebKit) 520 + return input.replacingOccurrences(of: "0.0.0.0", with: "127.0.0.1", options: .caseInsensitive) 521 + } 522 + 517 523 if isURL(raw) { 518 - let urlString = raw.hasPrefix("http") ? raw : "http://\(raw)" 524 + let normalized = normalizeURL(raw) 525 + let urlString = normalized.hasPrefix("http") ? normalized : "http://\(normalized)" 519 526 if let u = URL(string: urlString) { 520 527 url = u 521 528 } else {
+69
Sources/WebKitEngine/WebKitWebContent.swift
··· 40 40 41 41 let webView: WKWebView 42 42 private var observations: [NSKeyValueObservation] = [] 43 + private var pendingReloadURL: URL? 44 + private var reloadAttempt = 0 43 45 44 46 // MARK: - Init 45 47 ··· 70 72 forMainFrameOnly: false 71 73 )) 72 74 75 + // Console logging for debugging 76 + configuration.userContentController.addUserScript(WKUserScript( 77 + source: """ 78 + (function() { 79 + const originalLog = console.log; 80 + const originalError = console.error; 81 + const originalWarn = console.warn; 82 + 83 + console.log = function() { 84 + originalLog.apply(console, arguments); 85 + const args = Array.from(arguments).map(String); 86 + window.webkit.messageHandlers.mereConsole.postMessage('LOG: ' + args.join(' ')); 87 + }; 88 + 89 + console.error = function() { 90 + originalError.apply(console, arguments); 91 + const args = Array.from(arguments).map(String); 92 + window.webkit.messageHandlers.mereConsole.postMessage('ERROR: ' + args.join(' ')); 93 + }; 94 + 95 + console.warn = function() { 96 + originalWarn.apply(console, arguments); 97 + const args = Array.from(arguments).map(String); 98 + window.webkit.messageHandlers.mereConsole.postMessage('WARN: ' + args.join(' ')); 99 + }; 100 + })(); 101 + """, 102 + injectionTime: .atDocumentStart, 103 + forMainFrameOnly: false 104 + )) 105 + 73 106 // Theme colour detection. Priority order: 74 107 // 1. meta[name="theme-color"] — explicit, always wins 75 108 // 2. elementFromPoint walk — finds the actual rendered background ··· 125 158 headObs.observe(document.head, { childList: true }); 126 159 } 127 160 })(); 161 + console.log('📄 Page loaded, document.styleSheets.length:', document.styleSheets.length); 162 + console.log('📄 StyleSheets:', Array.from(document.styleSheets).map(s => s.href)); 128 163 """, 129 164 injectionTime: .atDocumentEnd, 130 165 forMainFrameOnly: true ··· 133 168 self.webView = WKWebView(frame: .zero, configuration: configuration) 134 169 self.webView.allowsBackForwardNavigationGestures = true 135 170 171 + #if DEBUG 172 + // Enable Web Inspector for Safari 173 + self.webView.isInspectable = true 174 + #endif 175 + 136 176 super.init() 137 177 138 178 // Use a weak wrapper to avoid retain cycles through userContentController. 139 179 let weak = WeakScriptMessageHandler(self) 140 180 configuration.userContentController.add(weak, name: "mereAudio") 141 181 configuration.userContentController.add(weak, name: "mereTheme") 182 + configuration.userContentController.add(weak, name: "mereConsole") 142 183 143 184 webView.navigationDelegate = self 144 185 webView.uiDelegate = self ··· 148 189 // MARK: - WebContent 149 190 150 191 public func loadURL(_ url: URL) { 192 + print("🌐 WebKitWebContent.loadURL: \(url.absoluteString)") 151 193 webView.load(URLRequest(url: url)) 152 194 } 153 195 ··· 273 315 274 316 public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { 275 317 if let url = webView.url { eventContinuation.yield(.committed(url: url)) } 318 + reloadAttempt = 0 319 + pendingReloadURL = nil 276 320 } 277 321 278 322 public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { ··· 306 350 eventContinuation.yield(.faviconChanged(url: faviconURL)) 307 351 } 308 352 353 + public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) { 354 + let nsError = error as NSError 355 + let retryableCodes = [-1001, -1002, -1004, -1005] 356 + 357 + print("❌ Provisional load failed: domain=\(nsError.domain) code=\(nsError.code) url=\(webView.url?.absoluteString ?? "nil")") 358 + 359 + // Retry once for transient network errors 360 + if retryableCodes.contains(nsError.code), reloadAttempt == 0, let url = webView.url { 361 + reloadAttempt = 1 362 + pendingReloadURL = url 363 + print("🔄 Attempting retry for url: \(url.absoluteString)") 364 + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { [weak self] in 365 + self?.webView.reload() 366 + } 367 + } else { 368 + reloadAttempt = 0 369 + pendingReloadURL = nil 370 + eventContinuation.yield(.failed(url: webView.url, error: error)) 371 + } 372 + } 373 + 309 374 public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { 310 375 eventContinuation.yield(.failed(url: webView.url, error: error)) 311 376 } ··· 343 408 case "mereTheme": 344 409 if let css = message.body as? String { 345 410 eventContinuation.yield(.themeColorChanged(cssColor: css)) 411 + } 412 + case "mereConsole": 413 + if let log = message.body as? String { 414 + print("🖥️ JS Console: \(log)") 346 415 } 347 416 default: break 348 417 }