Skip to content

Commit db50d13

Browse files
committed
fix(windows): native Windows build + SDK launch support
Compile: patch 0005 assigned a command-line switch value (std::wstring on Windows, std::string on Linux) into a flat_map<string,string>, which failed to build on Windows. Use GetSwitchValueASCII(), which returns std::string on all platforms. Launcher: force --use-angle=swiftshader so WebGL has a software context on GPU-less/headless hosts (the renderer string is spoofed regardless), matching the Linux launcher. SDK (py+node): a Windows .cmd launcher can't be spawned directly by CreateProcess (WinError 193) — run it via cmd.exe; kill the whole process tree on close() so chrome.exe doesn't orphan behind cmd.exe; bump the CDP timeout to 90s to tolerate first-run AV scanning of the extracted binaries. The native win-x64 asset is published on the v149.0.7827.232 release.
1 parent 00fa0ed commit db50d13

6 files changed

Lines changed: 37 additions & 14 deletions

File tree

packaging/tilion.cmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ if not defined TILION_NO_DEFAULTS set DEF=^
1919
--uxr-webrtc-policy=disable_non_proxied_udp^
2020
--enable-unsafe-webgpu --enable-features=Vulkan --use-webgpu-adapter=swiftshader^
2121
--enable-dawn-features=use_vulkan --uxr-webgpu-vendor=nvidia --uxr-webgpu-architecture=ampere "--uxr-webgpu-description=NVIDIA GeForce RTX 3060"
22-
"%HERE%chrome.exe" %DEF% %*
22+
"%HERE%chrome.exe" --use-angle=swiftshader %DEF% %*
2323
endlocal

patches/0005-content-browser-renderer_host-render_process_host_impl-cc.patch

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content
22
index 4c144f58e7..24acc0ba0d 100644
33
--- a/content/browser/renderer_host/render_process_host_impl.cc
44
+++ b/content/browser/renderer_host/render_process_host_impl.cc
5-
@@ -2011,6 +2011,20 @@ bool RenderProcessHostImpl::Init() {
5+
@@ -2011,6 +2011,22 @@ bool RenderProcessHostImpl::Init() {
66
storage_partition_impl_->cors_exempt_header_list(),
77
GetContentClient()->browser()->GetOriginTrialsSettings(), cpu_tier,
88
trace_id);
@@ -13,7 +13,10 @@ index 4c144f58e7..24acc0ba0d 100644
1313
+ for (const auto& sw :
1414
+ base::CommandLine::ForCurrentProcess()->GetSwitches()) {
1515
+ if (sw.first.compare(0, 4, "uxr-") == 0) {
16-
+ uxr_cfg[sw.first] = sw.second;
16+
+ // GetSwitchValueASCII returns std::string on all platforms; on Windows
17+
+ // sw.second is a std::wstring and won't assign to std::string directly.
18+
+ uxr_cfg[sw.first] =
19+
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(sw.first);
1720
+ }
1821
+ }
1922
+ if (!uxr_cfg.empty()) {

sdk/node/index.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ async function assetExists(plat, host) {
104104
catch { return false; }
105105
}
106106

107-
async function waitCdp(port, timeoutMs = 40000) {
107+
async function waitCdp(port, timeoutMs = 90000) {
108108
const deadline = Date.now() + timeoutMs;
109109
while (Date.now() < deadline) {
110110
try { const r = await fetch(`http://127.0.0.1:${port}/json/version`); if (r.ok) return (await r.json()).webSocketDebuggerUrl; }
@@ -150,7 +150,14 @@ export class Fortress {
150150
}
151151

152152
async close() {
153-
if (this.proc) { this.proc.kill(); this.proc = null; }
153+
if (this.proc) {
154+
// On Windows the launcher runs under cmd.exe (shell:true); proc.kill() would
155+
// only reap the shell and orphan chrome.exe, so kill the whole process tree.
156+
if (process.platform === "win32" && this.proc.pid)
157+
spawnSync("taskkill", ["/F", "/T", "/PID", String(this.proc.pid)], { stdio: "ignore" });
158+
else this.proc.kill();
159+
this.proc = null;
160+
}
154161
if (this.dockerName) { spawnSync("docker", ["rm", "-f", this.dockerName], { stdio: "ignore" }); this.dockerName = null; }
155162
}
156163
}

sdk/node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "tilion-fortress",
3-
"version": "151.0.7909",
3+
"version": "151.0.7910",
44
"description": "Install and drive the Fortress stealth Chromium engine. Prebuilt binary, no source.",
55
"type": "module",
66
"main": "index.js",

sdk/python/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "tilion-fortress"
7-
version = "151.0.7908.0.post3"
7+
version = "151.0.7908.0.post4"
88
description = "Install and drive the Fortress stealth Chromium engine. Prebuilt binary, no source."
99
readme = "README.md"
1010
requires-python = ">=3.8"

sdk/python/tilion_fortress/__init__.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import hashlib, json, os, platform, shutil, subprocess, sys, tarfile, time, urllib.request, zipfile
1515
from pathlib import Path
1616

17-
__version__ = "151.0.7908.0.post3"
17+
__version__ = "151.0.7908.0.post4"
1818
__all__ = ["Fortress", "resolve_platform"]
1919

2020
_REPO = "tiliondev/fortress"
@@ -163,11 +163,17 @@ def _asset_exists(self, plat: str) -> bool:
163163

164164
def _start_native(self, plat: str):
165165
launcher = _download(plat, self._host, self._tag)
166-
args = [str(launcher)]
166+
flags = []
167167
if self.headless:
168-
args += ["--headless=new", "--no-sandbox"]
169-
args += [f"--remote-debugging-port={self.port}", f"--user-data-dir={_CACHE / 'profile'}"]
170-
args += _persona_args(self.persona) + self.extra_args
168+
flags += ["--headless=new", "--no-sandbox"]
169+
flags += [f"--remote-debugging-port={self.port}", f"--user-data-dir={_CACHE / 'profile'}"]
170+
flags += _persona_args(self.persona) + self.extra_args
171+
# A Windows .cmd launcher cannot be spawned directly by CreateProcess
172+
# (WinError 193); run it through cmd.exe. POSIX launchers exec in place.
173+
if str(launcher).lower().endswith(".cmd"):
174+
args = ["cmd", "/c", str(launcher)] + flags
175+
else:
176+
args = [str(launcher)] + flags
171177
self._proc = subprocess.Popen(args, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
172178

173179
def _start_docker(self):
@@ -180,7 +186,7 @@ def _start_docker(self):
180186
"-p", f"{self.port}:9222", self._docker] + _persona_args(self.persona) + self.extra_args
181187
subprocess.run(args, check=True, stdout=subprocess.DEVNULL)
182188

183-
def _wait_cdp(self, timeout: float = 40.0) -> str:
189+
def _wait_cdp(self, timeout: float = 90.0) -> str:
184190
deadline = time.time() + timeout
185191
url = f"http://127.0.0.1:{self.port}/json/version"
186192
while time.time() < deadline:
@@ -193,7 +199,14 @@ def _wait_cdp(self, timeout: float = 40.0) -> str:
193199

194200
def close(self):
195201
if self._proc:
196-
self._proc.terminate(); self._proc = None
202+
# On Windows the launcher runs under cmd.exe; terminate() would only
203+
# kill cmd and orphan chrome.exe, so kill the whole process tree.
204+
if os.name == "nt":
205+
subprocess.run(["taskkill", "/F", "/T", "/PID", str(self._proc.pid)],
206+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
207+
else:
208+
self._proc.terminate()
209+
self._proc = None
197210
if self._docker_name:
198211
subprocess.run(["docker", "rm", "-f", self._docker_name],
199212
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)

0 commit comments

Comments
 (0)