[2026-06-25] eagle: family/how-to/htpc-emulators-setup.md family/how-to/htpc-gaming-plans.md family/how-to/kraken-access.md family/how-to/openmediavault-rpi5.md family/how-to/time-machine.md family/how-to/wireguard-vpn.md personal/documents/todo-list.md personal/plans/extract-stable-prompt-blocks.md personal/plans/hermes-whale-system-prompt.md personal/plans/thread-scoped-memory.md

This commit is contained in:
Alexey Martemyanov
2026-06-25 11:19:35 +06:00
parent 213ed1a1fa
commit ab51b151ed
30 changed files with 3141 additions and 510 deletions
@@ -0,0 +1,160 @@
---
tags:
- ddg-browser
- bugs
- performance
- ios
- macos
created: '2026-06-23'
status: in-progress
---
# absoluteString O(n) scan — main-thread stall bugs
## Root cause
`String.firstIndex(of: "#")` decodes grapheme clusters across every UTF-8 byte, making it O(n) in string length. `URL.absoluteString` materialises the full raw URL — including multi-megabyte `data:` URIs (e.g. content-scope privacy config JSON). Calling any character-scanning API on this on the **main thread** causes visible stalls and, for large enough payloads, a **SIGKILL from the iOS watchdog**.
**Measured baseline (Apple Silicon M-series, optimised utf8 byte scan, no guard):**
| Payload | Time |
|---|---|
| 1 MB, no `#` | ~15 ms |
| 1 MB, `#` at end | ~15 ms |
| 15 MB, no `#` | **~232 ms** |
On older iPhone A-series (35× slower): 15 MB → **~700 ms1.1 s per call**. Stacked across multiple nav actions per load = watchdog territory.
`Foundation.URL.fragment`, by contrast, parses all URL components eagerly at `URL(string:)` init time — accessing `.fragment` is O(1) regardless of payload size (confirmed with 1 MB `data:` URI test, both with and without `#` at the end: < 1 ms).
---
## Known fixed
### `hashedSuffixRange` — `StringExtension.swift`
**File:** `SharedPackages/Infrastructure/SystemFrameworksExtensions/Sources/FoundationExtensions/StringExtension.swift`
**Fix:** `data:` guard at top of `hashedSuffixRange` short-circuits before any scan. Also replaced `String.firstIndex(of: "#")` with `utf8.firstIndex(of: 0x23)` (byte-level, avoids grapheme decoding).
**Note:** `about:` + `%23` special case intentionally preserved — Foundation percent-encodes `#` to `%23` in opaque `about:` URL absoluteStrings in some construction paths (e.g. `URL(trimmedAddressBarString:)`). Introduced in commit `e880d888b3` ("fix about: scheme fragments dropping", Mar 2023). `URL.fragment` returns nil for these, so the string scan branch is genuinely needed.
**Asana task:** https://app.asana.com/1/137249556945/project/1202406491309510/task/1215403820548743
---
## Known bugs — not yet fixed
### HIGH — navigation hot path
#### `isSameDocument` / `droppingHashedSuffix` — `URLExtension.swift:387`
```swift
// URLExtension.swift:387
public func isSameDocument(_ other: URL) -> Bool {
self.absoluteString.droppingHashedSuffix() == other.absoluteString.droppingHashedSuffix()
}
```
Called from `WKNavigationActionExtension.isSameDocumentNavigation` (main thread, every nav action). Same O(n) scan via `droppingHashedSuffix``hashedSuffixRange`. Will be covered once the `data:` guard lands in `hashedSuffixRange`.
**Fix suggestion:** The `data:` guard in `hashedSuffixRange` propagates here automatically since `droppingHashedSuffix` delegates to it. No separate fix needed — covered by the `StringExtension` fix.
#### `matches(_ other: URL)` — `URLExtension.swift:3236`
```swift
public func matches(_ other: URL) -> Bool {
let string1 = self.absoluteString
let string2 = other.absoluteString
return string1.droppingHashedSuffix().dropping(suffix: "/").appending(string1.hashedSuffix ?? "")
== string2.droppingHashedSuffix().dropping(suffix: "/").appending(string2.hashedSuffix ?? "")
}
```
Used in `FrameInfo.Equatable`, `ContextMenuManager`, `PopupHandlingTabExtension` — all main thread. Also covered by `hashedSuffixRange` `data:` guard.
#### `DistributedNavigationDelegate.swift:673`
`hashedSuffix` + `droppingHashedSuffix` in `willPerformClientRedirect`. Same chain. Covered by guard.
---
### HIGH — JS injection with full absoluteString
#### `TabViewController.swift:4719`
```swift
"window.location.href='" + url.absoluteString + "'"
```
Entire URL embedded in a JS string for `evaluateJavaScript`. If `url` is a large `data:` URI, the JS string is megabytes long.
**Fix suggestion:** Check scheme before embedding. `data:` URIs should never be navigated to via `window.location.href` injection — bail early if `url.scheme == "data"`.
#### `Tab.swift` / `WKWebViewExtension.swift` — `escapedJavaScriptString()`
Same pattern — `absoluteString``escapedJavaScriptString()``evaluateJavaScript(...)`. Used in error-page recovery (`location.replace(...)`) and `window.open`.
**Fix suggestion:** Same — guard on scheme before serialising into JS.
---
### HIGH — blob: credential stripping
#### `macOS/DuckDuckGo/Common/Extensions/URLExtension.swift:843849`
```swift
// strippingUnsupportedCredentials() for blob: URLs
firstIndex(of: "@") + replacingOccurrences(..., .regularExpression)
```
`blob:` URLs can embed large payloads (base64-encoded media). Called from `TabViewModel.addressBarString` on `@MainActor`.
**Fix suggestion:** `blob:` URLs never have `@`-credentials in practice. Add a scheme guard:
```swift
guard scheme == "http" || scheme == "https" else { return self }
```
---
### MEDIUM — address bar rendering
#### `macOS/DuckDuckGo/Tab/ViewModel/TabViewModel.swift:403`
```swift
addressBarString = url.absoluteString // non-blob path
```
`TabViewModel.passiveAddressBarString` already guards `data:` (returns `"data:"`), but `addressBarString` does not.
**Fix suggestion:** Apply same guard — if `url.scheme == "data"`, set `addressBarString = "data:"`.
#### `iOS/DuckDuckGo/AddressDisplayHelper.swift:5574`
`NSMutableAttributedString(string: absoluteString)` + `NSRange` deemphasis. Omnibar rendering on main thread.
**Fix suggestion:** Same — cap or replace `data:` URLs before constructing attributed string.
#### `iOS/DuckDuckGo/BackForwardMenuHistoryItem.swift:4158`
`URL(trimmedAddressBarString: url.absoluteString)`, `dropping`, `.count`, `dropLast` on main thread for back/forward menu construction.
**Fix suggestion:** Guard on `url.scheme != "data"` before passing to `trimmedAddressBarString` initialiser.
---
### MEDIUM — navigation pipeline regex/prefix checks
#### `LinkCleaner.swift:36,6165`
`NSRegularExpression.matches(in: url.absoluteString)` — AMP format matching. Guarded to http/https by the call site but still scans full string length.
**Fix suggestion:** `AMPCanonicalExtractor` already has a `maxURLLength` guard — ensure `LinkCleaner` applies the same.
#### `HistoryViewDataProvider.swift:478`
`absoluteString.localizedCaseInsensitiveContains(searchTerm)` — history search. Runs on main thread. History URLs are rarely `data:` but theoretically possible.
#### `AIChatMentionPickerFilter.swift:57`
`tab.url.absoluteString.lowercased().contains(query)` — AI Chat tab picker filter, main thread.
**Fix suggestion:** Both — early-return `false` if `url.scheme == "data"` before scanning.
---
## Test coverage added (branch `fix-fragment-crash`)
- `HashedSuffixDataURIPerformanceTests` in `StringExtensionTests.swift` — asserts `hashedSuffix` completes in < 1 ms on 1 MB and 15 MB `data:` URIs (with and without `#` at end). Fails without the `data:` guard (15 MB → ~232 ms).
- `URLFragmentDataURIPerformanceTests` in `WKNavigationActionExtensionTests.swift` — asserts `URL.fragment` completes in < 10 ms on 1 MB `data:` URI. Confirms Foundation parses eagerly (O(1) access).
- `WKNavigationActionExtensionTests` — full unit test suite for `isSameDocumentNavigation` covering all nav types, `data:` URLs, `about:%23` edge case.