Repository navigation
fix: recover from web content process crashes like Safari - #253
barto95100 wants to merge 40 commits into
Conversation
Moving across child elements of the same anchor re-sent the same URL on every mouseover and re-assigned the @published hoveredLinkURL, re-rendering the link preview. Deduplicate in the injected script and skip identical assignments in Swift. Complements the-ora#246.
urlString, used by open-tab search, was only set when the tab was created, so a tab kept matching the URL it was first opened with. Route navigation updates through Tab.updateURL(_:) which updates both.
The first suggestion could be the active tab itself, so Enter switched to the current tab and nothing happened. Never suggest the active tab, and rank the typed URL before open tabs when the input is a URL.
…s through Clicking the sidebar or toolbar left the URL bar suggestions open, while the dimming overlay over the page swallowed clicks, so links and the context menu did not work while editing. Watch mouse-downs during editing (without consuming them) and dismiss when they land outside the field and its suggestions; keep the overlay purely visual.
…g it blindly The WKUIDelegate method was missing the type parameter, so WebKit never called it (compiler: 'nearly matches optional requirement'). Fix the signature, forward whether camera, microphone or both are requested, and answer with WebKit's own prompt instead of an unconditional grant.
oraTests had no Info.plist and no signing team, so tests could not run locally. Generate the Info.plist and sign with the development team.
Introduce SiteOrigin (scheme, host and port, with default ports normalized), a tri-state ask/allow/deny per capability, and SitePermissionStore scoped per space like website data. Private browsing is kept in memory only. Permissions are cleared when a space is deleted. Replaces the unused SitePermissionSettings.
Capture requests are answered from the saved per-space permissions; when nothing is saved, an in-page prompt asks the user, optionally remembering the choice. Unanswered prompts are denied when the page navigates away, the tab is closed or another request replaces them. Allow has no keyboard shortcut on purpose.
…ttings Add a Site Permissions card to each space in Settings > Spaces to change or forget saved camera and microphone choices. Share the site display rule (host for HTTPS, full origin otherwise) between the prompt and settings, with tests.
The workflow only checked lint and formatting. Build the app and run the unit tests (ad-hoc signing on CI), pin SwiftLint and SwiftFormat to the local versions, cache Swift packages and upload the test results on failure. Upstream Sync now dispatches the CI on its branch, since pull requests opened with GITHUB_TOKEN do not trigger workflows.
ci: build the app and run unit tests
Escape not handled by the page travels up the responder chain as cancelOperation(_:) and reached the window, which exits full screen. Stop it at the page host view, after WebKit, so pages still get the key. Like Safari, it now stops a page that is still loading. Fixes the-ora#179.
fix: keep full screen when Escape is pressed in a web page
The Fullscreen shortcut (Control-Command-F by default) was defined and listed in Settings but never handled, and macOS only offers Globe-F in the View menu, which keyboards without a Globe key cannot type. Handle Ora's customizable shortcut with the other global key handlers, without adding a duplicate menu item.
fix: make the Fullscreen keyboard shortcut work
The app delegate received links before any window was ready, picked an arbitrary window or built one by hand without its environment, and posted a notification the window might not be listening to yet, so links were lost. Let SwiftUI route external events: regular windows accept and prefer them and get them through onOpenURL once ready; settings and private windows refuse them. Remove the now unused manual window factory. Fixes the-ora#178.
openTab required a host, which file URLs do not have, so no tab was created; and the sandbox only lets WebKit read a local file loaded with loadFileURL(_:allowingReadAccessTo:). Use the file name as the initial title and load file URLs with that API.
Pages without a <title> report an empty one, which blanked the tab title. Fall back to the host, or the file name for local files, like Safari.
Fix/178 open links cold launch
Muted autoplay videos (page backgrounds, previews, ads) were marked as played and brought up the mini player, and one code path computed its visibility from all sessions instead of played ones. Only audible media count as played, including a muted video the user unmutes. Ported from the upstream branch tembo/fix-mini-player-trigger-no-media, which no longer applies after upstream's file reorganisation. Fixes the-ora#187.
fix(media): don't show the mini player for silent or muted media
…p with media events - Coalesce DOM scans (at most one every 250 ms) and only report next/previous button changes: busy pages like YouTube sent hundreds of events per minute, each re-rendering the sidebar. - Don't republish unchanged media state. - Automatic Picture in Picture uses the video playing with sound, not the first video of the page, and leaving a tab whose video is already in Picture in Picture keeps it there. - 'Back to tab' from the Picture in Picture window now works: the page of a tab in Picture in Picture stays attached to the window, hidden, so WebKit can bring the video back, and Ora then shows that tab like Safari.
fix(media): reliable Picture in Picture, no more media event flood
WebKit already reports them like Safari (core count capped at 8, no deviceMemory, real language and screen). Overriding them made Ora stand out from Safari users and look inconsistent — language differing from the Accept-Language header, a screen smaller than the window — which YouTube's bot detection flagged, stopping playback after a few minutes. Keep canvas, WebGL, audio and media device protections; tests guard against reintroducing the overrides.
applicationNameForUserAgent is appended by WebKit to its own Mozilla/AppleWebKit prefix but was given a full user agent, so the prefix appeared twice, with a hard-coded, outdated Safari version. Pass only Safari's suffix, using the installed Safari's version (validated, with a fallback).
Canvas and audio noise was a fixed offset on fixed samples, so Ora produced the same unique signature on every site and every session — an identifier in itself. Derive the noise from a secret drawn at each launch, mixed with the space, the browsing mode and, in the page, the top-level site and frame origin. A site reads stable values within a session; sites and sessions cannot be linked.
fix(privacy): make fingerprinting protection blend in like Safari
WebKit reports the end of a page's process (crash or memory reclaim), but Ora ignored it and left the tab blank and unresponsive. The visible page now reloads once with a notice, a background page reloads only when its tab is shown, and a page that ends again within 30 seconds of a reload shows a crash page instead of looping. Media sessions, Picture in Picture, permission prompts and autofill of the dead page are cleared.
Debug builds only. The sandbox forbids Ora to kill a web content process, so the command copies the kill -9 command for the space's pages to the clipboard, to run in Terminal.
[High impact] The PR changes browser behavior across page recovery, permissions, privacy, and app operations. Keep the production app identifier unchanged before merging so release packaging and existing user settings continue to work. Findings
SummaryAdds recovery when a page's WebKit process ends:
Also changes permissions, Picture in Picture, privacy scripts, external links, CI, and app identity. The production identifier change needs fixing before merge. Two smaller issues affect media controls and retries through Reload. Diagram%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Web content process ends] --> B[Clear media, prompts, and autofill]
B --> C{Reload recorded within 30 seconds?}
C -->|Yes| D[Show crash page]
C -->|No| E{Tab selected?}
E -->|Yes| F[Record reload and show notice]
E -->|No| G[Wait until tab is selected]
G --> F
F --> H[Reload existing page]
D --> I[Try Again]
I --> J[Record reload]
J --> H
Reviews (1) · Last reviewed commit: "chore(debug): add a command to simulate ..." · Reviewed by Greptile |
| CURRENT_PROJECT_VERSION: 105 | ||
| PRODUCT_NAME: Ora | ||
| PRODUCT_BUNDLE_IDENTIFIER: com.orabrowser.app | ||
| PRODUCT_BUNDLE_IDENTIFIER: com.barto95100.ora |
There was a problem hiding this comment.
Release identity no longer matches
The release app now uses com.barto95100.ora, but scripts/build.sh still exports with a provisioning profile mapped to com.orabrowser.app. Release packaging no longer has a matching profile entry. The new identifier also moves existing users to a different UserDefaults.standard domain without migrating their settings.
Keep the production identifier unchanged; use a separate identifier only for development builds.
| if (lastCaps && lastCaps.hasNext === hasNext && lastCaps.hasPrevious === hasPrevious) return; | ||
| lastCaps = { hasNext, hasPrevious }; | ||
| post({ type: 'caps', hasNext, hasPrevious }); |
There was a problem hiding this comment.
caps() now caches button states even when MediaController has no session to receive them. If next/previous buttons exist before playback starts, the first message is dropped. Playback then creates a session with both controls disabled, while later scans suppress the unchanged message.
Save these values until a session exists, or resend them when playback creates one.
| if navigationError is PageCrashError { | ||
| crashRecovery.recordReload() | ||
| browserPage?.reload() | ||
| return |
There was a problem hiding this comment.
Reload bypasses the crash clock
retryNavigation() records a crash retry, but the normal Reload command calls Tab.reload() without recording it. After the crash page has been open for more than 30 seconds, pressing Command-R and crashing again immediately makes Ora reload automatically instead of showing the crash page.
Route both retry paths through the same code so every user-requested crash reload updates the clock.
WebKit reports when the process rendering a page ends (crash, or macOS reclaiming memory), but Ora ignored it: the tab stayed blank and unresponsive, and Reload did nothing.
Recovery now follows Safari:
Reloads keep the back/forward history. Media sessions, Picture in Picture, permission prompts and autofill of the dead page are cleared. The navigation error WebKit sends alongside the termination is ignored, so no misleading "Page Not Found" appears.
Debug builds get Navigation › Copy Page Crash Command: the sandbox forbids Ora to kill a page process, so it copies the
kill -9command to run in Terminal.Tests:
PageCrashRecoveryPolicyTests(7 cases) + manual tests (visible tab, background tabs, repeated crash, mini player / PiP, notice dismissal).