Skip to content

Complete host-driven native close veto lifecycle #87

Description

@wieslawsoltes

AppScene coordination epic: SceneTech/AppScene#66

Problem

Unchanged browser applications use standard window/document APIs for close, focus, fullscreen, visibility, and external navigation. WebScene already translates HTTP(S) anchors and window.open into bounded host requests, but the Runtime contract has no typed close/focus/fullscreen completion path and visibility changes from the native window are incomplete. Code OSS's browser IHostService calls these APIs directly.

Proposed contract

  • Define versioned, typed host requests for external URL, close, focus, and fullscreen enter/exit, with request IDs and explicit completion where JavaScript awaits a result.
  • Publish native visibility/focus/fullscreen changes back into the correct document realm and dispatch the standard events.
  • Keep reload as WebScene navigation using the current URL and navigation-generation cancellation rules.
  • Preserve external HTTP(S) handoff as a system-browser action. Reject unsupported schemes and oversized/malformed values before host dispatch.
  • Bound pending requests and cancel awaited requests on navigation/teardown.
  • Treat auxiliary browsing contexts as a separate capability under AppScene [GPU 19/20] Qualify WebGPU/WebGL conformance, resilience and support claims #41; window.open continues system-browser handoff until that capability is implemented.

Acceptance criteria

  • DOM/API contracts cover window.close/focus, Element.requestFullscreen, Document.exitFullscreen, fullscreen state/events, focus/blur, document.hidden, visibilityState, and reload.
  • Native contracts cover success, host denial, malformed requests/completions, queue saturation, stale completion, navigation, and teardown.
  • A 10,000-operation request/completion gate and a 10,000 visibility/focus transition gate publish elapsed time and bounded high-water metrics without sleep-based correctness.
  • Hosted and packaged Windows, macOS, and Linux jobs plus cross-RID/consumer checks pass.
  • AppScene qualifies external link, fullscreen round trip, reload readiness, and veto-aware close in the packaged Code OSS app with no VS Code source change.

This is a standard browser/runtime boundary. Platform window operations remain AppScene's responsibility.

Activity

  1. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    First implementation commit: 378dd29d on consolidated draft PR #76, with the Cocoa host in SceneTech/AppScene 029da1fb / draft PR SceneTech/AppScene#65.

    Implemented: bounded HTTP(S) handoff, top-level focus/close requests, scripted beforeunload close veto, location.reload(), Promise-based Element/Document fullscreen entry/exit with state and fullscreenchange, native document.hasFocus() plus focus/blur propagation, existing visibility state/events, navigation cancellation, explicit completion failures, and an oversized JSON request discard contract.

    Repository CI is running. This stays open for native-initiated fullscreen synchronization, close-button veto qualification, typed request structs, transition/queue performance metrics, Windows/Linux packaged host qualification, and the combined Code OSS interaction gate.

  2. wieslawsoltes commented on Sep 15, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Implemented at da6ee30492294b94541ea5137d741916e53e4a2f in draft PR #76. Hosted macOS/Ubuntu/Windows, NativeAOT, native Linux, all three packaged RIDs, installed consumers, cross-RID evidence, and release-package verification pass. Contracts cover bounded focus/close/reload/fullscreen/external-URL requests, close veto, visibility/focus propagation, queue overflow, navigation cancellation, and stale completions.

  3. wieslawsoltes commented on Sep 16, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Reopening for the unchecked native close-button contract. Stack #186 completed the JavaScript-to-host path, but the reverse OS-to-browser lifecycle path is still absent.

    Exact gap:

    • window.close() dispatches cancelable beforeunload in WebScene before it emits WEBSCENE_HOST_REQUEST_WINDOW_CLOSE_V1; AppScene then closes the Cocoa window. This path is veto-aware.
    • A user or automation closing the native NSWindow enters AppScene windowWillClose / begin_close directly. No WebScene ABI lets AppScene ask the active top-level realm to dispatch beforeunload/pagehide and return the allow/veto decision before teardown.
    • Packaged normal-close exit 0 therefore proves safe teardown, but it does not prove Code OSS BrowserLifecycleService shutdown notification, storage flush, or window.confirmBeforeClose veto on an OS close.

    Recommended focused stack order:

    1. WebScene: add a versioned host-driven top-level close-request ABI with an explicit allow/veto/error result; dispatch cancelable beforeunload in the active realm, run the microtask checkpoint, and dispatch the noncancelable terminal lifecycle event only after an allow decision. Cover stale/no-realm/error behavior and repeated requests. Because the browser workbench rejects async vetoes, the current consumer needs a bounded synchronous decision rather than an unbounded callback.
    2. AppScene: consume the optional ABI from windowShouldClose, return NO on veto, and distinguish an already-approved JavaScript window.close() host request so it does not dispatch beforeunload twice. Preserve an explicit compatibility policy when the symbol is absent.
    3. Packaged Code OSS: verify native close veto with window.confirmBeforeClose, allowed close with lifecycle/storage evidence, repeated normal close, and teardown. Keep external-link/fullscreen/reload package checks alongside this gate.

    The existing typed desktop ABI and pending-host teardown from merged Stack #186 remain the prerequisite. SceneTech/AppScene#67 stays open for layer 2 and combined acceptance; AppScene #100 independently fixed composite visibility state.

  4. changed the title [-]Add typed native window lifecycle and fullscreen host contracts[/-] [+]Complete host-driven native close veto lifecycle[/+] on Sep 16, 2026
  5. wieslawsoltes commented on Sep 16, 2026

    @wieslawsoltes
    CollaboratorAuthor

    The generic host-driven close contract is now merged in #202 (c83432769ab41ff14c26aa901b2e713503e76ab1).

    Evidence:

    • webscene_engine_request_window_close_v1 returns explicit error/allow/veto decisions and runs on the owning V8 worker while the host waits synchronously through a bounded queue/wait.
    • The active top-level realm receives cancelable beforeunload; its microtask checkpoint drains before the result. Veto suppresses terminal lifecycle. Allow dispatches one noncancelable pagehide with persisted=false; repeated approved requests coalesce until navigation.
    • The focused lifecycle/error/repeat regression passes. Its 10,000-decision gate completed in about 0.061s locally, and the exact PR head passed portable-v8-contract.
    • The first consumer is merged in SceneTech/AppScene#102 (9d627ea3aeb2444bf5a467370c7eff2950453ca5): Cocoa windowShouldClose honors veto/error and bypasses exactly one decision for an already approved JavaScript window.close() request.

    The remaining acceptance is the packaged Code OSS close-veto/storage/lifecycle qualification tracked by SceneTech/AppScene#67. Leaving this issue open until that cross-repository acceptance is recorded.

  6. wieslawsoltes commented on Sep 17, 2026

    @wieslawsoltes
    CollaboratorAuthor

    Closed by the merged typed host/window lifecycle contracts and exact packaged acceptance.

    Source coverage is complete through PRs #202/#206 and the terminal lifecycle fixes #215 (547395f2) and #217 (13c93852). Focused portable runners passed. Inspector Release gates completed 10,000 native decisions in 0.057832 seconds and 10,000 script closes in 0.00124571 seconds with stable V8 heap.

    Against final WebScene 24db1a2f / AppScene d7cbbaf9, three packaged Code OSS cycles each passed external-link handoff, fullscreen enter/exit, top-level reload, native close veto, and JavaScript-approved close. Every cycle observed beforeunload counts 0→1→2 and exactly one terminal pagehide; recursive/capacity behavior remained bounded and repeated teardown left no processes. This satisfies the browser API, host denial/veto, ordering, capacity, reload, and packaged desktop round-trip scope. Consolidation PR #76 stays draft/open/unmerged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions