* Fix browser panel webview lifecycle after web content crashes * Fix BrowserPanel observer lifecycle during webview replacement * Fix WebKit termination delegate and harden lifecycle regression check
94 lines
3.5 KiB
Python
Executable file
94 lines
3.5 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""Static regression checks for deterministic browser lifecycle architecture.
|
|
|
|
Guards the long-term browser mounting design:
|
|
1) BrowserPanelView updateNSView must use a single portal-based mount path.
|
|
2) Legacy attach-retry and direct attach/detach churn helpers stay removed.
|
|
3) BrowserPanel handles WebContent termination via deterministic webview replacement,
|
|
not blind `webView.reload()`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import subprocess
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
|
|
def repo_root() -> Path:
|
|
git_path = shutil.which("git")
|
|
git_command = git_path if git_path else "git"
|
|
result = subprocess.run(
|
|
[git_command, "rev-parse", "--show-toplevel"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
if result.returncode == 0:
|
|
return Path(result.stdout.strip())
|
|
return Path(__file__).resolve().parents[1]
|
|
|
|
|
|
def extract_block(source: str, signature: str) -> str:
|
|
start = source.find(signature)
|
|
if start < 0:
|
|
raise ValueError(f"Missing signature: {signature}")
|
|
brace_start = source.find("{", start)
|
|
if brace_start < 0:
|
|
raise ValueError(f"Missing opening brace for: {signature}")
|
|
depth = 0
|
|
for idx in range(brace_start, len(source)):
|
|
char = source[idx]
|
|
if char == "{":
|
|
depth += 1
|
|
elif char == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[brace_start : idx + 1]
|
|
raise ValueError(f"Unbalanced braces for: {signature}")
|
|
|
|
|
|
def main() -> int:
|
|
root = repo_root()
|
|
failures: list[str] = []
|
|
|
|
view_path = root / "Sources" / "Panels" / "BrowserPanelView.swift"
|
|
view_source = view_path.read_text(encoding="utf-8")
|
|
|
|
if "updateUsingWindowPortal(nsView, context: context, webView: webView)" not in view_source:
|
|
failures.append("updateNSView no longer routes through updateUsingWindowPortal")
|
|
if "scheduleAttachRetry(" in view_source:
|
|
failures.append("Legacy attach retry helper still present in BrowserPanelView")
|
|
if "attachRetryWorkItem" in view_source:
|
|
failures.append("Legacy attachRetryWorkItem state still present in BrowserPanelView")
|
|
if "usesWindowPortal" in view_source:
|
|
failures.append("Dual portal/non-portal lifecycle state still present in BrowserPanelView")
|
|
|
|
panel_path = root / "Sources" / "Panels" / "BrowserPanel.swift"
|
|
panel_source = panel_path.read_text(encoding="utf-8")
|
|
|
|
if "@Published private(set) var webViewInstanceID" not in panel_source:
|
|
failures.append("BrowserPanel is missing webViewInstanceID for deterministic instance remounting")
|
|
if "replaceWebViewAfterContentProcessTermination" not in panel_source:
|
|
failures.append("BrowserPanel is missing deterministic WebContent termination replacement path")
|
|
|
|
terminate_delegate = extract_block(
|
|
panel_source,
|
|
"func webViewWebContentProcessDidTerminate(_ webView: WKWebView)",
|
|
)
|
|
if "didTerminateWebContentProcess?(webView)" not in terminate_delegate:
|
|
failures.append("webContentProcessDidTerminate no longer delegates to deterministic replacement handler")
|
|
if "webView.reload()" in terminate_delegate:
|
|
failures.append("webContentProcessDidTerminate still does blind webView.reload()")
|
|
|
|
if failures:
|
|
print("FAIL: browser lifecycle architecture regression guards failed")
|
|
for item in failures:
|
|
print(f" - {item}")
|
|
return 1
|
|
|
|
print("PASS: browser lifecycle architecture regression guards are in place")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|