From cc89a1e1cab3e347d723421ba1f804da06397798 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Tue, 24 Mar 2026 15:23:33 +0100 Subject: [PATCH] Add check for minimum browser version when running on Web --- .../Binaries/Data/check_browser_version.js | 47 +++++++++++++++++++ .../Platforms/Web/Binaries/Data/check_jspi.js | 5 ++ .../Flax.Build/Platforms/Web/WebToolchain.cs | 37 +++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 Source/Platforms/Web/Binaries/Data/check_browser_version.js create mode 100644 Source/Platforms/Web/Binaries/Data/check_jspi.js diff --git a/Source/Platforms/Web/Binaries/Data/check_browser_version.js b/Source/Platforms/Web/Binaries/Data/check_browser_version.js new file mode 100644 index 000000000..958ea5bea --- /dev/null +++ b/Source/Platforms/Web/Binaries/Data/check_browser_version.js @@ -0,0 +1,47 @@ +/** + * @license + * Copyright 2024 The Emscripten Authors + * SPDX-License-Identifier: MIT + */ + +(function() { + // "30.0.0" -> 300000 + function humanReadableVersionToPacked(str) { + str = str.split('-')[0]; // Remove any trailing part from e.g. "12.53.3-alpha" + var vers = str.split('.').slice(0, 3); + while(vers.length < 3) vers.push('00'); + vers = vers.map((n, i, arr) => n.padStart(2, '0')); + return vers.join(''); + } + // 300000 -> "30.0.0" + var packedVersionToHumanReadable = n => [n / 10000 | 0, (n / 100 | 0) % 100, n % 100].join('.'); + + var userAgent = typeof navigator !== 'undefined' && navigator.userAgent; + if (!userAgent) { + return; + } + + var currentSafariVersion = userAgent.includes("Safari/") && !userAgent.includes("Chrome/") && userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/) ? humanReadableVersionToPacked(userAgent.match(/Version\/(\d+\.?\d*\.?\d*)/)[1]) : TARGET_NOT_SUPPORTED; + if (currentSafariVersion < MIN_SAFARI_VERSION) { + console.log(navigator.userAgent); + var msg = `Required Safari v${ packedVersionToHumanReadable(MIN_SAFARI_VERSION) }, detected v${ packedVersionToHumanReadable(currentSafariVersion) }.`; + alert(msg) + throw new Error(msg); + } + + var currentFirefoxVersion = userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/) ? parseFloat(userAgent.match(/Firefox\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED; + if (currentFirefoxVersion < MIN_FIREFOX_VERSION) { + console.log(navigator.userAgent); + var msg = `Required Firefox vMIN_FIREFOX_VERSION, detected v${currentFirefoxVersion}.`; + alert(msg) + throw new Error(msg); + } + + var currentChromeVersion = userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/) ? parseFloat(userAgent.match(/Chrome\/(\d+(?:\.\d+)?)/)[1]) : TARGET_NOT_SUPPORTED; + if (currentChromeVersion < MIN_CHROME_VERSION) { + console.log(navigator.userAgent); + var msg = `Required Chrome vMIN_CHROME_VERSION, detected v${currentChromeVersion}.`; + alert(msg) + throw new Error(msg); + } +})(); diff --git a/Source/Platforms/Web/Binaries/Data/check_jspi.js b/Source/Platforms/Web/Binaries/Data/check_jspi.js new file mode 100644 index 000000000..692b2099f --- /dev/null +++ b/Source/Platforms/Web/Binaries/Data/check_jspi.js @@ -0,0 +1,5 @@ +if (!('Suspending' in WebAssembly)) { + console.log(navigator.userAgent); + alert(`JSPI is not supported in this browser.`) + throw new Error(`JSPI is not supported in this browser.`); +} diff --git a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs index d539f034e..1b1d7c763 100644 --- a/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs +++ b/Source/Tools/Flax.Build/Platforms/Web/WebToolchain.cs @@ -341,6 +341,43 @@ namespace Flax.Build.Platforms args.Add("-sSIDE_MODULE"); } + // Setup minimum browser versions + // https://webassembly.org/features/?categories=browsers#feature-note-3 + uint minChrome = 0, minFirefox = 0, minSafari = 0; + bool addJSPI = false; + if (args.Any(arg => arg.Contains("-sJSPI") || arg.Contains("-sASYNCIFY=2"))) + { + // JS Promise Integration + minChrome = 137; // May 2025 + minFirefox = 147; // Jan 2026 (requires flag javascript.options.wasm_js_promise_integration in about:config) + minSafari = 260000; // Feb 2026 (Safari Technical Preview 238) + addJSPI = true; // Run check_jspi after check_browser_version + } + else + { + // Fixed-width SIMD, WebGPU support + minChrome = 91; // May 2021 + minFirefox = 89; // Jun 2021 + minSafari = 160400; // March 2023 + } + args.Add("-sMIN_CHROME_VERSION=" + minChrome); + args.Add("-sMIN_FIREFOX_VERSION=" + minFirefox); + args.Add("-sMIN_SAFARI_VERSION=" + minSafari); + if (args.All(arg => !arg.Contains("-sASSERTIONS"))) + { + // minimum_runtime_check.js from Emscripten checks min browser versions only with ASSERTIONS enabled so use custom check + var checkBrowserVersion = File.ReadAllText(Path.Combine(Globals.EngineRoot, "Source/Platforms/Web/Binaries/Data/check_browser_version.js")); + checkBrowserVersion = checkBrowserVersion.Replace("TARGET_NOT_SUPPORTED", "0x7fffffff"); + checkBrowserVersion = checkBrowserVersion.Replace("MIN_CHROME_VERSION", minChrome.ToString()); + checkBrowserVersion = checkBrowserVersion.Replace("MIN_FIREFOX_VERSION", minFirefox.ToString()); + checkBrowserVersion = checkBrowserVersion.Replace("MIN_SAFARI_VERSION", minSafari.ToString()); + var path = Utilities.NormalizePath(Path.Combine(options.IntermediateFolder, "check_browser_version.js")); + File.WriteAllText(path, checkBrowserVersion); + args.Add($"--pre-js \"{path}\""); + } + if (addJSPI) + args.Add($"--pre-js \"{Globals.EngineRoot}/Source/Platforms/Web/Binaries/Data/check_jspi.js\""); + // Customize output HTML shell if (options.LinkEnv.Output == LinkerOutput.Executable) args.Add($"--shell-file \"{Globals.EngineRoot}/Source/Platforms/Web/Binaries/Data/shell.html\"");