Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
83e04e5
fix(browser): tie helper lifetime to Runtime
andersalm Oct 6, 2026
77b30ea
fix(browser): repair helper shutdown review findings
andersalm Oct 6, 2026
c7a51d9
fix(browser-engine-supervisor): build and test on every host; only Li…
andersalm Oct 6, 2026
29c0867
test(browser-stream-bridge): keep test socket paths under the macOS s…
andersalm Oct 6, 2026
e6a3390
test(browser): give copy fixture a bounded interpreter startup budget
andersalm Oct 7, 2026
73f7be9
fix(browser): prepare writable launch copies from immutable images
andersalm Oct 7, 2026
beab28b
fix(browser): reconcile terminal native launch cleanup
andersalm Oct 7, 2026
b116b65
fix(browser): report interrupted profile initialization before boot
andersalm Oct 7, 2026
1921326
fix(browser): require exact proof before terminal cleanup
andersalm Oct 7, 2026
a237254
fix(browser): serialize profile Reset with native writers
andersalm Oct 7, 2026
ce8cb6a
Merge current develop into Browser cleanup candidate
andersalm Oct 7, 2026
38dca66
fix(browser): use scoped lock and fork-safe fixture helpers
andersalm Oct 7, 2026
07864ac
fix(browser): confirm profile reset within the capsule
andersalm Oct 8, 2026
52e1616
fix(browser): retain exact terminal cleanup proof across restart
andersalm Oct 8, 2026
d9889d9
fix(browser-vz): retain native owners until cleanup settles
andersalm Oct 8, 2026
484380b
fix(browser): settle control ownership before service exit
andersalm Oct 8, 2026
90fc8e6
fix(browser): scope shutdown to current service owners
andersalm Oct 8, 2026
4e13eab
fix(browser): scope non-Unix profile reset lint expectation
andersalm Oct 8, 2026
0c466f7
fix(browser): simplify terminal cleanup retry condition
andersalm Oct 8, 2026
ec88f95
fix(browser): scope the inline startup payload lint
andersalm Oct 8, 2026
0d4493d
Merge current develop into Browser ownership work
andersalm Oct 8, 2026
7c66ffe
test(browser): bind preadmit barrier to its owned page
andersalm Oct 8, 2026
2e6632a
fix(browser-vz): settle broken-pipe writes during shutdown
andersalm Oct 8, 2026
b2498be
fix(browser): retain remote Engine cleanup proof
andersalm Oct 8, 2026
dbae2a3
Merge current develop into Browser ownership repair
andersalm Oct 8, 2026
ff4c9e4
test(browser): isolate stream bridge socket fixtures
andersalm Oct 8, 2026
5451b28
fix(browser): bind packaged image bytes to verified receipt
andersalm Oct 8, 2026
c5c317a
feat(browser): stage admitted host scripts for release
andersalm Oct 8, 2026
2f4e8ff
Merge remote-tracking branch 'origin/develop' into feat/217-browser-r…
andersalm Oct 8, 2026
82ae48c
build(browser): prepare images and managed host dependencies
andersalm Oct 8, 2026
b499af1
feat(browser): prepare signed Engine through normal Home setup
andersalm Oct 8, 2026
035ca1a
feat(install): offer removable Linux Browser host setup
andersalm Oct 8, 2026
03a0004
Bind the shared ARM64 Browser guest to reusable recipe inputs
andersalm Oct 8, 2026
e5baa8d
Use one VM Browser Engine and explicit remote consumers
andersalm Oct 8, 2026
3206016
Place VM Engine validation tests after production items
andersalm Oct 8, 2026
672944e
Honor the inline Engine selection during preparation
andersalm Oct 8, 2026
be345ad
Format the Engine selection reader
andersalm Oct 8, 2026
e6f3490
Simplify the remaining Browser helper assertion
andersalm Oct 8, 2026
44cd78c
Align delivery fixtures with the shared VM Engine contract
andersalm Oct 9, 2026
ce46c0a
Keep the guest control bridge portable for host checks
andersalm Oct 9, 2026
adfa957
test(install): isolate Linux completion fixtures from host state
andersalm Oct 9, 2026
8cd0157
fix(release): use the shared Browser image staging reserve
andersalm Oct 9, 2026
2e4a71a
Merge remote-tracking branch 'origin/develop' into feat/217-browser-r…
andersalm Oct 9, 2026
d4fdde6
build(browser): select a four GiB shared ARM64 guest candidate
andersalm Oct 9, 2026
0bd9a1a
fix(browser): keep one spare Linux root image for update capacity
andersalm Oct 9, 2026
14f7941
test(home): keep future launch tokens beyond the clock allowance
andersalm Oct 9, 2026
2d8d7fe
test(browser): give parallel relay fixtures exclusive directories
andersalm Oct 9, 2026
3fc37dd
Merge remote-tracking branch 'origin/fix/131-browser-helper-orphan' i…
andersalm Oct 9, 2026
e09f568
Merge remote-tracking branch 'origin/develop' into feat/217-browser-r…
andersalm Oct 9, 2026
282c409
test(browser): match unavailable virtualization status
andersalm Oct 9, 2026
951bda8
build(browser): freeze Debian and Python guest inputs
andersalm Oct 9, 2026
c6d71bd
fix(browser): preserve Linux profiles and repair setup after host access
andersalm Oct 9, 2026
e6fdae8
build(release): stream a pinned common Browser image through Carrier
andersalm Oct 9, 2026
7f84113
refactor(browser): retire the host-native namespace Engine
andersalm Oct 9, 2026
ed1fc17
style(browser): format streamed image space checks
andersalm Oct 9, 2026
bc7700e
ci: pre-push gate runs the CI behaviour suite from one shared script …
andersalm Oct 9, 2026
f9f2ea5
ci: behaviour suite runs offline; inputs install is a separate step (…
andersalm Oct 9, 2026
4026f8e
ci: pre-push gate accepts a whole removed Rust package and checks eve…
andersalm Oct 10, 2026
d2d63af
Merge branch 'ci/prepush-behaviour' into feat/217-browser-role-closure
andersalm Oct 10, 2026
d492f9e
Merge remote-tracking branch 'origin/develop' into feat/217-browser-r…
andersalm Oct 10, 2026
d50adbb
test(browser): require shared VM objective evidence
andersalm Oct 10, 2026
94a6738
build(browser): enforce authenticated Debian bootstrap
andersalm Oct 10, 2026
f36d6cd
fix(ci): scope unit proof after package removal
andersalm Oct 10, 2026
3ca8263
test(vsock): verify provider bridge and format workspace
andersalm Oct 10, 2026
dda2f11
fix(ci): pass explicit Browser tools to source-home
andersalm Oct 10, 2026
2699ff1
fix(browser): verify Linux image kernels and exact cleanup aliases
andersalm Oct 10, 2026
8fd342c
fix(browser): show startup status and open a fresh Dock page
andersalm Oct 10, 2026
7699b71
fix(browser): describe Close honestly during preparation
andersalm Oct 10, 2026
3259d67
fix(ci): support Ubuntu Git in the pre-push merge check
andersalm Oct 10, 2026
c1b99a6
Fix source-home Browser helper verification for each host
andersalm Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -96,17 +96,18 @@ jobs:
timeout-minutes: 5
env:
DEBUG: pw:install
run: |
npm install --prefix "${RUNNER_TEMP}/behaviour-browser" --no-save --package-lock=false playwright@1.60.0
npm install --prefix "${RUNNER_TEMP}/behaviour-core" --no-save --package-lock=false playwright-core@1.59.1
"${RUNNER_TEMP}/behaviour-browser/node_modules/.bin/playwright" install chromium --with-deps --only-shell
run: ./scripts/test-behaviour.sh inputs
- name: JavaScript behavior tests
env:
NODE_PATH: ${{ runner.temp }}/behaviour-browser/node_modules
BROWSER_OPERATOR_PLAYWRIGHT_CORE: ${{ runner.temp }}/behaviour-core/node_modules/playwright-core
run: |
python3 scripts/browser-vm-linux-network.test.py
node --test --test-timeout=60000 elastos/esp/projections.test.mjs scripts/*.test.mjs scripts/build/*.test.mjs scripts/lib/*.test.mjs capsules/*/browser/*.test.mjs capsules/*/browser/src/*.test.mjs scripts/home-fixture-contracts-smoke.mjs scripts/documents-save-conflict-smoke.mjs scripts/gba-save-conflict-smoke.mjs
./scripts/test-behaviour.sh
- name: Browser startup status screenshots
if: always()
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5
with:
name: browser-startup-status
path: ${{ runner.temp }}/browser-status-screenshots/
if-no-files-found: ignore

# One input for every ARM consumer. PRs restore their develop base's cache;
# only a develop push can save a verified recipe result in this namespace.
Expand Down Expand Up @@ -250,15 +251,6 @@ jobs:
cargo test --workspace -- --list | tee "${RUNNER_TEMP}/elastos-test-list.txt"
total=$(awk '/: test$/ {n++} END {print n+0}' "${RUNNER_TEMP}/elastos-test-list.txt")
echo "Workspace listed tests: ${total}" | tee -a "${GITHUB_STEP_SUMMARY}"
- name: native network isolation and loopback proxy
timeout-minutes: 5
run: |
set -euo pipefail
cargo build --quiet --manifest-path elastos/tools/browser-engine-supervisor/Cargo.toml
cargo build --quiet --manifest-path elastos/tools/browser-native-proxy-engine/Cargo.toml
git rev-parse HEAD HEAD^{tree}
sha256sum elastos/tools/browser-engine-supervisor/target/debug/browser-engine-supervisor elastos/tools/browser-native-proxy-engine/target/debug/browser-native-proxy-engine
sudo -n bash scripts/browser-native-supervisor-proxy-smoke.sh --prebuilt --require-isolation
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: '26'
Expand Down Expand Up @@ -600,6 +592,10 @@ jobs:
mkdir -p "${RUNNER_TEMP}/source-home-logs"
USER_HOME="${HOME}"
SOURCE_HOME="${RUNNER_TEMP}/elastos-source-home"
export ELASTOS_NODE_BIN="$(command -v node)"
export ELASTOS_BROWSER_VM_TURNSERVER_BIN="$(command -v turnserver)"
[[ "${ELASTOS_NODE_BIN}" == /* && -x "${ELASTOS_NODE_BIN}" ]] || { echo 'CI requires its installed Node executable.' >&2; exit 1; }
[[ "${ELASTOS_BROWSER_VM_TURNSERVER_BIN}" == /* && -x "${ELASTOS_BROWSER_VM_TURNSERVER_BIN}" ]] || { echo 'CI requires its installed TURN executable.' >&2; exit 1; }
mkdir -p "${SOURCE_HOME}"
export CARGO_HOME="${CARGO_HOME:-${USER_HOME}/.cargo}"
export RUSTUP_HOME="${RUSTUP_HOME:-${USER_HOME}/.rustup}"
Expand Down Expand Up @@ -830,6 +826,10 @@ jobs:
mkdir -p "${RUNNER_TEMP}/source-home-logs"
USER_HOME="${HOME}"
MAC_TEST_HOME="${RUNNER_TEMP}/elastos-mac-test-home"
export ELASTOS_NODE_BIN="$(command -v node)"
export ELASTOS_BROWSER_VM_TURNSERVER_BIN="$(command -v turnserver)"
[[ "${ELASTOS_NODE_BIN}" == /* && -x "${ELASTOS_NODE_BIN}" ]] || { echo 'CI requires its installed Node executable.' >&2; exit 1; }
[[ "${ELASTOS_BROWSER_VM_TURNSERVER_BIN}" == /* && -x "${ELASTOS_BROWSER_VM_TURNSERVER_BIN}" ]] || { echo 'CI requires its installed TURN executable.' >&2; exit 1; }
mkdir -p "${MAC_TEST_HOME}"
export CARGO_HOME="${CARGO_HOME:-${USER_HOME}/.cargo}"
export RUSTUP_HOME="${RUSTUP_HOME:-${USER_HOME}/.rustup}"
Expand Down
40 changes: 36 additions & 4 deletions .github/workflows/release-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ on:
description: Update version N+1, built with N's support
required: true
type: string
browser_image_cid:
description: Qualified common ARM64 guest archive CID (served as its immutable filename)
required: true
type: string
browser_image_sha256:
description: Independent SHA-256 of that guest archive
required: true
type: string
browser_image_size:
description: Exact archive bytes
required: true
type: string
seed_package:
description: Also build the Linux seed package (only to upgrade the seed Runtime)
type: boolean
Expand All @@ -35,6 +47,12 @@ env:
SOURCE_COMMIT: ${{ inputs.source_commit }}
INSTALL_VERSION: ${{ inputs.install_version }}
UPDATE_VERSION: ${{ inputs.update_version }}
BROWSER_IMAGE_CID: ${{ inputs.browser_image_cid }}
BROWSER_IMAGE_SHA256: ${{ inputs.browser_image_sha256 }}
BROWSER_IMAGE_SIZE: ${{ inputs.browser_image_size }}
BROWSER_IMAGE_PUBLISHER_DID: ${{ vars.BROWSER_IMAGE_PUBLISHER_DID }}
BROWSER_IMAGE_PUBLISHER_NODE_ID: ${{ vars.BROWSER_IMAGE_PUBLISHER_NODE_ID }}
BROWSER_IMAGE_CONNECT_TICKET: ${{ secrets.BROWSER_IMAGE_CONNECT_TICKET }}
CARGO_BUILD_JOBS: '4'
PYTHONDONTWRITEBYTECODE: '1'
# Shipped release builds stay hermetic: no compiler wrapper or cache.
Expand Down Expand Up @@ -65,6 +83,16 @@ jobs:
# Preparation may run beside CI. Publication still requires green CI
# for this exact source and the release acceptance gate.
echo "source_tree=$tree" >> "$GITHUB_OUTPUT"
- uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5
with:
ref: ${{ inputs.source_commit }}
persist-credentials: false
- name: admit the common immutable image selector
run: |
set -euo pipefail
python3 -I -S scripts/browser-image-cid-input.py "$BROWSER_IMAGE_CID" "$BROWSER_IMAGE_SHA256" "$BROWSER_IMAGE_SIZE"
[[ "$BROWSER_IMAGE_PUBLISHER_DID" == did:key:* && "$BROWSER_IMAGE_PUBLISHER_NODE_ID" =~ ^[0-9a-f]{64}$ ]] \
|| { echo 'Configure the explicit Browser image publisher DID and Carrier node ID' >&2; exit 1; }

mac:
name: Apple silicon N and N+1
Expand Down Expand Up @@ -107,7 +135,7 @@ jobs:
timeout-minutes: 20
run: |
set -euo pipefail
brew install e2fsprogs coturn pkg-config openssl@3
brew install e2fsprogs coturn pkg-config openssl@3 autoconf automake libtool zstd
python3 -I -S ../workflow/.github/scripts/release-mac-capacity.py
- name: keep the capacity record
if: always() && env.RELEASE_ROOT != ''
Expand All @@ -131,8 +159,10 @@ jobs:
[[ ! -e "$CARGO_TARGET_DIR" && ! -L "$CARGO_TARGET_DIR" && ! -e "$CARGO_BUILD_BUILD_DIR" && ! -L "$CARGO_BUILD_BUILD_DIR" ]]
reuse=()
[[ "$name" == N ]] || reuse=(--reuse-support "$inputs/N")
image=()
[[ "$name" != N ]] || image=(--browser-vm-image-cid "$BROWSER_IMAGE_CID" --browser-vm-image-sha256 "$BROWSER_IMAGE_SHA256" --browser-vm-image-size "$BROWSER_IMAGE_SIZE")
HOME="$RELEASE_ROOT/home" scripts/prepare-release-platform.sh \
--version "$version" --output "$inputs/$name" ${reuse[@]+"${reuse[@]}"}
--version "$version" --output "$inputs/$name" ${reuse[@]+"${reuse[@]}"} ${image[@]+"${image[@]}"}
python3 -I -S scripts/release-platform-input.py verify "$inputs/$name" > /dev/null
rm -rf "$CARGO_TARGET_DIR" "$CARGO_BUILD_BUILD_DIR"
done
Expand Down Expand Up @@ -297,7 +327,7 @@ jobs:
timeout-minutes: 15
run: |
sudo apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 update
sudo apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 install -y build-essential cmake coturn e2fsprogs ffmpeg musl-tools nasm pkg-config
sudo apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 install -y build-essential cmake coturn e2fsprogs ffmpeg musl-tools nasm pkg-config autoconf automake libtool libclang-dev linux-libc-dev zstd
- name: download pinned ARM64 engine input
if: matrix.platform == 'aarch64-linux'
uses: actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5
Expand All @@ -319,8 +349,10 @@ jobs:
[[ ! -e "$CARGO_TARGET_DIR" && ! -L "$CARGO_TARGET_DIR" && ! -e "$CARGO_BUILD_BUILD_DIR" && ! -L "$CARGO_BUILD_BUILD_DIR" ]]
reuse=()
[[ "$name" == N ]] || reuse=(--reuse-support "$inputs/N")
image=()
[[ "$name" != N || "$RELEASE_PLATFORM" != aarch64-linux ]] || image=(--browser-vm-image-cid "$BROWSER_IMAGE_CID" --browser-vm-image-sha256 "$BROWSER_IMAGE_SHA256" --browser-vm-image-size "$BROWSER_IMAGE_SIZE")
HOME="$RELEASE_ROOT/home" scripts/prepare-release-platform.sh \
--version "$version" --output "$inputs/$name" ${reuse[@]+"${reuse[@]}"}
--version "$version" --output "$inputs/$name" ${reuse[@]+"${reuse[@]}"} ${image[@]+"${image[@]}"}
python3 -I -S scripts/release-platform-input.py verify "$inputs/$name" > /dev/null
rm -rf "$CARGO_TARGET_DIR" "$CARGO_BUILD_BUILD_DIR"
done
Expand Down
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -428,12 +428,13 @@ Use target roles consistently:
- Mac: staging, macOS VZ Browser proof, and cross-platform proof;
- Jetson: Linux/crosvm native Browser target and intended main device proof.

Mac staging requires durable SSH before serious testing. `tmate` is acceptable
Browser staging on this local Mac uses direct execution. Remote Mac staging
requires durable SSH before serious testing. `tmate` is acceptable
only as a break-glass bootstrap channel. During that bootstrap, create or reuse a
dedicated staging account, install an agent-owned public key in
the target account's authorized-keys file, disable password assumptions, record a local SSH host
alias, and verify non-interactive commands work. If no durable SSH is available,
say the Mac is blocked instead of implying it was verified.
say remote Mac staging is blocked instead of implying it was verified.

Do not commit staging aliases, private key names, reverse-tunnel ports, local
worktree paths, or operator usernames. Keep those details in private operational
Expand Down
1 change: 1 addition & 0 deletions capsules/browser-engine-adapter/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions capsules/browser-engine-adapter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ edition = "2021"
description = "ElastOS Browser Engine Adapter provider capsule"
license = "MIT"

[features]
default = []
test-engine = []

[[bin]]
name = "browser-engine-adapter"
path = "src/main.rs"
Expand Down
25 changes: 21 additions & 4 deletions capsules/browser-engine-adapter/src/supervisor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,20 @@ use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};

// The adapter owns the writer; transient launchers pass only the reader on.
fn configure_helper_owner(command: &mut Command) -> std::io::Result<()> {
use elastos_common::process_lifetime::ParentLifetime;
use std::sync::OnceLock;
if ParentLifetime::configure_inherited(command)? {
return Ok(());
}
static OWNER: OnceLock<std::io::Result<ParentLifetime>> = OnceLock::new();
match OWNER.get_or_init(ParentLifetime::new) {
Ok(owner) => owner.configure(command),
Err(error) => Err(std::io::Error::new(error.kind(), error.to_string())),
}
}

const MAX_BROWSER_ENGINE_LAUNCH_REQUEST_BYTES: usize = 64 * 1024;

#[derive(Debug)]
Expand Down Expand Up @@ -115,6 +129,8 @@ pub(super) fn run_supervisor_launch(
if transport.is_none() {
command.env("ELASTOS_BROWSER_ENGINE_REQUEST", &serialized);
}
configure_helper_owner(&mut command)
.map_err(|err| SupervisorLaunchError::process(err.to_string()))?;
let mut child = command
.spawn()
.map_err(|err| SupervisorLaunchError::process(err.to_string()))?;
Expand Down Expand Up @@ -203,15 +219,16 @@ fn kill_and_reap(child: &mut std::process::Child) {
}

pub(super) fn run_supervisor_prewarm(supervisor: &EngineSupervisorConfig) -> Result<Value, String> {
let mut child = Command::new(&supervisor.program)
let mut command = Command::new(&supervisor.program);
command
.args(&supervisor.args)
.envs(&supervisor.env)
.env("ELASTOS_BROWSER_VM_PREWARM_CONTROL_SERVICE", "1")
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.map_err(|err| err.to_string())?;
.stderr(Stdio::piped());
configure_helper_owner(&mut command).map_err(|err| err.to_string())?;
let mut child = command.spawn().map_err(|err| err.to_string())?;
let deadline = Instant::now() + Duration::from_millis(supervisor.timeout_ms.min(30_000));
let status = loop {
match child.try_wait() {
Expand Down
36 changes: 36 additions & 0 deletions capsules/browser-engine-adapter/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ pub(super) fn looks_like_bridge_provider_config(config: &Value) -> bool {
}

pub(super) fn validate_adapter(adapter: &AdapterConfig) -> Result<(), String> {
validate_product_engine(adapter.kind, cfg!(any(test, feature = "test-engine")))?;
if adapter.kind == AdapterKind::ChromiumMicrovm
&& adapter.display_modes != vec![BrowserDisplayMode::WebrtcRemoteDisplay]
{
return Err("The VM Browser Engine requires webrtc_remote_display".to_string());
}
if !is_safe_id(&adapter.id) {
return Err("adapter id must be a safe identifier".to_string());
}
Expand Down Expand Up @@ -57,6 +63,13 @@ pub(super) fn validate_adapter(adapter: &AdapterConfig) -> Result<(), String> {
Ok(())
}

fn validate_product_engine(kind: AdapterKind, test_engine: bool) -> Result<(), String> {
if kind != AdapterKind::ChromiumMicrovm && !test_engine {
return Err("Browser uses the VM Engine. Select a compatible local VM or an approved remote Engine. Host-native and Playwright engines are test-only.".to_string());
}
Ok(())
}

pub(super) fn validate_supervisor(supervisor: &EngineSupervisorConfig) -> Result<(), String> {
if supervisor.program.is_empty() || !supervisor.program.starts_with('/') {
return Err("browser engine supervisor program must be absolute".to_string());
Expand Down Expand Up @@ -282,3 +295,26 @@ pub(super) fn validate_relay_ipc(endpoint: &RelayIpcEndpoint) -> Result<(), Stri
}
Ok(())
}

#[cfg(test)]
mod product_engine_tests {
use super::*;

#[test]
fn production_accepts_only_the_vm_engine() {
assert!(validate_product_engine(AdapterKind::ChromiumMicrovm, false).is_ok());
for kind in [
AdapterKind::Cef,
AdapterKind::ChromiumHeadless,
AdapterKind::SelkiesGstreamer,
AdapterKind::HostedRemoteBrowser,
AdapterKind::ContractProof,
AdapterKind::Webview2,
AdapterKind::Geckoview,
AdapterKind::Wkwebview,
] {
assert!(validate_product_engine(kind, false).is_err());
assert!(validate_product_engine(kind, true).is_ok());
}
}
}
10 changes: 9 additions & 1 deletion capsules/browser/browser/browser-status.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export function friendlyOpenError(error) {
case "artifact_invalid":
return "Browser Engine files need repair. Prepare this Engine or choose another approved Engine.";
case "host_unsupported":
return "The selected Engine needs a compatible host. Choose another approved Engine.";
return "Browser virtualization is unavailable on the selected host. Use a host with Apple Virtualization or KVM, or choose another approved Engine.";
case "readiness_unsupported":
return "Browser Engine needs an update to report readiness. Update it or choose another approved Engine.";
case "control_unavailable":
Expand All @@ -94,6 +94,14 @@ export function friendlyOpenError(error) {
return "Browser Engine is unavailable. Choose an available approved Engine.";
}
}
if (
error?.payload?.code === "profile_recovery_required" &&
["terminal_pre_effect_failure", "terminal_post_effect_cleanup"].includes(outcome?.state) &&
outcome.effects.page_acquired === false &&
outcome.effects.vm_acquired === false
) {
return "Browser profile setup was interrupted. In Settings, select Reset profile to start again. Reset clears this account's Browser data.";
}
if (outcome?.state === "terminal_pre_effect_failure") {
if (error.status === 403) {
return "This page was blocked by your Exit Node settings.";
Expand Down
24 changes: 23 additions & 1 deletion capsules/browser/browser/browser-status.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ test("pre-effect profile placement keeps the Runtime transfer sentence", () => {
assert.equal(friendlyOpenError(error), message);
});

test("interrupted profile setup gives reset guidance after terminal cleanup", () => {
for (const state of ["terminal_pre_effect_failure", "terminal_post_effect_cleanup"]) {
const error = openError(state);
error.payload.code = "profile_recovery_required";
assert.equal(
friendlyOpenError(error),
"Browser profile setup was interrupted. In Settings, select Reset profile to start again. Reset clears this account's Browser data.",
);
}
});

test("profile recovery guidance keeps pending or acquired ownership visible", () => {
for (const error of [
openError("cleanup_pending"),
openError("terminal_post_effect_cleanup", { page_acquired: true, vm_acquired: true }),
new Error("profile_recovery_required"),
]) {
error.payload = { ...error.payload, code: "profile_recovery_required" };
assert.doesNotMatch(friendlyOpenError(error), /select Reset profile/);
}
});

test("pre-effect viewer ingress capacity keeps the Runtime capacity sentence", () => {
const message = "Runtime viewer ingress capacity unavailable";
const error = openError("terminal_pre_effect_failure");
Expand Down Expand Up @@ -224,7 +246,7 @@ test("WebRTC diagnostics expose only selected pair types and inbound counters",
test("readiness failures explain the repair while cleanup retains priority", () => {
const expected = new Map([
["artifact_invalid", /files need repair/],
["host_unsupported", /needs a compatible host/],
["host_unsupported", /Browser virtualization is unavailable on the selected host/],
["readiness_unsupported", /update to report readiness/],
["control_unavailable", /Restore its connection/],
["preparation_required", /needs preparation/],
Expand Down
Loading
Loading