mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
* perf(mobile): answer a page's ready that arrived before its bridge host A page's ready can reach the shell before the screen has built its bridge host (the host waits on the client and the host profile read). It used to be dropped, so init waited for the page's next retry, up to 2 s later. Keep the latest ready per session and answer it once the host exists. * perf(mobile): keep one prewarmed page document while Home is shown While native Home is on screen, boot one page document for the most recently connected host with a cached build, parked invisibly in the window. Opening a page from that build adopts it instead of building a WebView, which removes the WebView start and script parse from the tap. The spare is dropped when Home is left without a claim (2 s grace), on background, memory pressure, its own failure and before the cache deletes its files. It is never kept beside a shown page, and only phones with more than 3 GiB of RAM (4 GB phones) get one. A parked page is told to stop asking for init; the adopting view answers the ready it sent. Builds on Jinwoo's spare pool and replay work in #32203 and #32204. Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> * fix(mobile): unregister the shell's memory callbacks from the context that registered them --------- Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com>
110 lines
3.6 KiB
Swift
110 lines
3.6 KiB
Swift
import Foundation
|
||
|
||
/// The wire names the TypeScript parser accepts; a swap here is a silent change of meaning.
|
||
enum MobileWebShellFailureReason: String {
|
||
case generationUnreadable = "generation-unreadable"
|
||
case isolationUnavailable = "isolation-unavailable"
|
||
case documentLoadFailed = "document-load-failed"
|
||
case renderProcessGone = "render-process-gone"
|
||
}
|
||
|
||
struct MobileWebShellLoadEmission: Equatable {
|
||
let state: String
|
||
let reason: String?
|
||
/// Renderer losses only: Orca was off screen, so the OS reclaimed the process rather than the page failing.
|
||
var background: Bool? = nil
|
||
|
||
var payload: [String: Any] {
|
||
var payload: [String: Any] = ["state": state]
|
||
if let reason {
|
||
payload["reason"] = reason
|
||
}
|
||
if let background {
|
||
payload["background"] = background
|
||
}
|
||
return payload
|
||
}
|
||
}
|
||
|
||
/// Failure is terminal and duplicate reports are dropped: late error-document/rule-list callbacks must never restore ready.
|
||
final class MobileWebShellLoadStateMachine {
|
||
private(set) var isTerminal = false
|
||
private var last: MobileWebShellLoadEmission?
|
||
|
||
/// Accept messages only after the current props’ document commits; the replaced same-origin document can still speak before then.
|
||
private(set) var hasCommittedDocument = false
|
||
|
||
/// Keep the own-load flag in the state machine so every document-ending path clears it.
|
||
private(set) var isShellLoad = false
|
||
|
||
/// The view is about to load the document itself. The only thing that raises the flag.
|
||
func shellLoadStarted() {
|
||
isShellLoad = true
|
||
}
|
||
|
||
/// Spend the own-load flag at the policy decision: WebKit may decide a second action before the first commits.
|
||
func shellLoadConsumed() {
|
||
isShellLoad = false
|
||
}
|
||
|
||
/// A new prop pair. Nothing else reopens a terminal state: a retry is a remount.
|
||
func reset() {
|
||
isTerminal = false
|
||
last = nil
|
||
documentEnded()
|
||
}
|
||
|
||
func committed() {
|
||
isShellLoad = false
|
||
guard !isTerminal else { return }
|
||
hasCommittedDocument = true
|
||
}
|
||
|
||
/// The committed document is gone: a new load, a failure, or a renderer that died.
|
||
func documentEnded() {
|
||
hasCommittedDocument = false
|
||
isShellLoad = false
|
||
}
|
||
|
||
func started() -> MobileWebShellLoadEmission? {
|
||
emit(MobileWebShellLoadEmission(state: "loading", reason: nil))
|
||
}
|
||
|
||
/// Require a commit, not URL equality: history.replaceState changes / to /h/<hostId> before finish, otherwise ready never arrives.
|
||
func finished() -> MobileWebShellLoadEmission? {
|
||
guard hasCommittedDocument else { return nil }
|
||
return emit(MobileWebShellLoadEmission(state: "ready", reason: nil))
|
||
}
|
||
|
||
func failed(
|
||
_ reason: MobileWebShellFailureReason,
|
||
background: Bool? = nil
|
||
) -> MobileWebShellLoadEmission? {
|
||
let emission = emit(
|
||
MobileWebShellLoadEmission(state: "failed", reason: reason.rawValue, background: background)
|
||
)
|
||
isTerminal = true
|
||
documentEnded()
|
||
return emission
|
||
}
|
||
|
||
private func emit(_ emission: MobileWebShellLoadEmission) -> MobileWebShellLoadEmission? {
|
||
guard !isTerminal, emission != last else { return nil }
|
||
last = emission
|
||
return emission
|
||
}
|
||
}
|
||
|
||
/// Ignore stopLoading/policy cancellations so healthy generations are not redownloaded. Legacy WebKitErrorDomain frame-load codes lack iOS SDK symbols.
|
||
enum MobileWebShellNavigationError {
|
||
static let webKitDomain = "WebKitErrorDomain"
|
||
static let frameLoadInterruptedByPolicyChange = 102
|
||
|
||
static func isIgnorable(domain: String, code: Int) -> Bool {
|
||
if domain == NSURLErrorDomain, code == NSURLErrorCancelled {
|
||
return true
|
||
}
|
||
return domain == webKitDomain && code == frameLoadInterruptedByPolicyChange
|
||
}
|
||
}
|