if (":" == "<!--") then : 0 /*\;:\
@ECHO OFF||:;fi;:||REM<<'EXIT'
GOTO :W
SPDX-FileCopyrightText: 2026 Alexandre Gomes Gaigalas <alganet@gmail.com>
SPDX-License-Identifier: ISC
:W
<NUL SET /P =[1A[K[1A
SETLOCAL ENABLEEXTENSIONS ENABLEDELAYEDEXPANSION
SET "SCRIPT_NAME=%~n0"
SET "SCRIPT_DIR=%~dp0"
SET "APP_FOLDER=%SCRIPT_DIR%%SCRIPT_NAME%"
SET "FX_DIR=%WINDIR%\Microsoft.NET\Framework\v4.0.30319"
SET "JSC=%FX_DIR%\jsc.exe"
SET "WEBVIEW2_ROOT=%APP_FOLDER%\Microsoft.Web.WebView2"
IF NOT EXIST "%JSC%" (
    SET "FX_DIR=%WINDIR%\Microsoft.NET\Framework64\v4.0.30319"
    SET "JSC=%FX_DIR%\jsc.exe"
)
IF NOT EXIST "%JSC%" ( EXIT /B 1 )
SET "FIRST_RUN="
IF NOT EXIST "%APP_FOLDER%" (
    SET "FIRST_RUN=1"
    MKDIR "%APP_FOLDER%"
    IF ERRORLEVEL 1 EXIT /B 1
)
SET "CERTUTIL=%WINDIR%\System32\certutil.exe"
SET "HASH_OUT=%APP_FOLDER%\launcher.hash"
SET "SLOT=%SCRIPT_DIR%%SCRIPT_NAME%.build"
SET "APP_EXE=%SCRIPT_DIR%%SCRIPT_NAME%.exe"
SET "APP_STAMP=%SCRIPT_DIR%%SCRIPT_NAME%.stamp"
SET "MANIFEST=%APP_EXE%.manifest"
SET "SRC_HASH="
SET "CACHED_HASH="
IF NOT EXIST "%SLOT%\" GOTO :BESIDE
SET "APP_EXE=%SLOT%\%SCRIPT_NAME%.exe"
SET "APP_STAMP="
SET "MANIFEST=%SLOT%\%SCRIPT_NAME%.exe.manifest"
SET "SLOT_PROBE=%SLOT%\.w%RANDOM%%RANDOM%"
2>NUL > "%SLOT_PROBE%" ECHO x
IF EXIST "%SLOT_PROBE%" (
    DEL /Q "%SLOT_PROBE%" >NUL 2>&1
    GOTO :NAMES
)
IF EXIST "%APP_EXE%" GOTO :LAUNCH
SET "APP_EXE=%SCRIPT_DIR%%SCRIPT_NAME%.exe"
SET "APP_STAMP=%SCRIPT_DIR%%SCRIPT_NAME%.stamp"
SET "MANIFEST=%APP_EXE%.manifest"
:BESIDE
IF EXIST "%APP_EXE%" IF NOT EXIST "%APP_STAMP%" (
    SET "APP_EXE=%APP_FOLDER%\%SCRIPT_NAME%.exe"
    SET "APP_STAMP="
    SET "MANIFEST=%APP_FOLDER%\%SCRIPT_NAME%.exe.manifest"
)
IF DEFINED APP_STAMP IF EXIST "%APP_STAMP%" (
    FOR /F "usebackq tokens=* delims=" %%S IN ("%APP_STAMP%") DO (
        IF NOT DEFINED CACHED_HASH SET "CACHED_HASH=%%S"
    )
)
:NAMES
FOR %%D IN ("%APP_EXE%") DO SET "EXE_DIR=%%~dpD"
SET "APP_NEW=%EXE_DIR%%SCRIPT_NAME%.new%RANDOM%%RANDOM%.exe"
SET "APP_OLD=%EXE_DIR%%SCRIPT_NAME%.old%RANDOM%%RANDOM%.exe"
DEL /Q "%EXE_DIR%%SCRIPT_NAME%.new*.exe" >NUL 2>&1
DEL /Q "%EXE_DIR%%SCRIPT_NAME%.old*.exe" >NUL 2>&1
IF NOT DEFINED APP_STAMP GOTO :BUILD
IF NOT DEFINED CACHED_HASH GOTO :BUILD
IF NOT EXIST "%APP_EXE%" GOTO :BUILD
CALL :HASH
IF NOT DEFINED SRC_HASH GOTO :BUILD
IF /I NOT "!CACHED_HASH!"=="!SRC_HASH!" GOTO :BUILD
GOTO :RUN
:BUILD
IF DEFINED APP_STAMP (
    2>NUL > "%APP_STAMP%" ECHO building
    IF NOT EXIST "%APP_STAMP%" (
        SET "APP_STAMP="
        SET "APP_EXE=%APP_FOLDER%\%SCRIPT_NAME%.exe"
        SET "MANIFEST=%APP_FOLDER%\%SCRIPT_NAME%.exe.manifest"
        SET "APP_NEW=%APP_FOLDER%\%SCRIPT_NAME%.new%RANDOM%%RANDOM%.exe"
        SET "APP_OLD=%APP_FOLDER%\%SCRIPT_NAME%.old%RANDOM%%RANDOM%.exe"
    )
)
IF NOT DEFINED FIRST_RUN GOTO :COMPILE
SET "MSG=Your application is getting ready to run for the first time..."
SET "N=0"
FOR /F "tokens=2 delims=:" %%A IN ('MODE CON ^| FINDSTR [0-9]') DO (
    SET /A N+=1
    IF !N!==1 SET /A ROWS=%%A
    IF !N!==2 SET /A COLS=%%A
)
SET /A HALF_ROW=ROWS / 2
SET /A PAD="(COLS - 62) / 2"
SET "SPACES="
FOR /L %%I IN (1,1,!PAD!) DO SET "SPACES=!SPACES! "
CLS
<NUL SET /P =[!HALF_ROW!;1H!SPACES!!MSG!
:COMPILE
"%JSC%" /nologo /debug- /t:winexe /out:"%APP_NEW%" ^
    /autoref+ ^
    /lib:"%FX_DIR%" ^
    /r:"%FX_DIR%\mscorlib.dll" ^
    /r:"%FX_DIR%\System.dll" ^
    /r:"%FX_DIR%\System.Configuration.dll" ^
    /r:"%FX_DIR%\Accessibility.dll" ^
    /r:"%FX_DIR%\System.Drawing.dll" ^
    /r:"%FX_DIR%\System.Windows.Forms.dll" ^
    /r:"%FX_DIR%\System.IO.Compression.dll" ^
    /r:"%FX_DIR%\System.IO.Compression.FileSystem.dll" ^
    "%~f0"
    SET "JSC_EXIT=%ERRORLEVEL%"
    IF NOT "%JSC_EXIT%"=="0" EXIT /B %JSC_EXIT%
IF EXIST "%APP_EXE%" MOVE /Y "%APP_EXE%" "%APP_OLD%" >NUL 2>&1
MOVE /Y "%APP_NEW%" "%APP_EXE%" >NUL
IF ERRORLEVEL 1 EXIT /B 1
IF DEFINED APP_STAMP (
    CALL :HASH
    IF DEFINED SRC_HASH > "!APP_STAMP!" ECHO !SRC_HASH!
)
:RUN
IF EXIST "%MANIFEST%" GOTO :LAUNCH
> "%MANIFEST%" (
    ECHO ^<?xml version="1.0" encoding="UTF-8" standalone="yes"?^>
    ECHO ^<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"^>
    ECHO   ^<assemblyIdentity version="1.0.0.0" processorArchitecture="*" name="neutrino.webview" type="win32" /^>
    ECHO   ^<description^>neutrino webview^</description^>
    ECHO   ^<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"^>
    ECHO     ^<application^>
    ECHO       ^<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /^>
    ECHO       ^<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" /^>
    ECHO       ^<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" /^>
    ECHO       ^<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" /^>
    ECHO       ^<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" /^>
    ECHO     ^</application^>
    ECHO   ^</compatibility^>
    ECHO   ^<application xmlns="urn:schemas-microsoft-com:asm.v3"^>
    ECHO     ^<windowsSettings^>
    ECHO       ^<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings"^>true/pm^</dpiAware^>
    ECHO       ^<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"^>PerMonitorV2, PerMonitor^</dpiAwareness^>
    ECHO     ^</windowsSettings^>
    ECHO   ^</application^>
    ECHO ^</assembly^>
)
:LAUNCH
START "" /D "%APP_FOLDER%" "%APP_EXE%"
IF ERRORLEVEL 1 EXIT /B 1
EXIT /B 0
:HASH
IF DEFINED SRC_HASH GOTO :EOF
IF NOT EXIST "%CERTUTIL%" GOTO :EOF
2>NUL >"%HASH_OUT%" "%CERTUTIL%" -hashfile "%~f0" SHA256
IF NOT EXIST "%HASH_OUT%" GOTO :EOF
FOR /F "usebackq skip=1 tokens=* delims=" %%H IN ("%HASH_OUT%") DO (
    IF NOT DEFINED SRC_HASH SET "SRC_HASH=%%H"
)
IF DEFINED SRC_HASH SET "SRC_HASH=!SRC_HASH: =!"
GOTO :EOF
EXIT
script_path="$(cd "$(dirname "$0")" && pwd)/$(basename "$0")"
nt_scrub_loaders() {
    nt_name=""
    nt_names="$(env | sed -n 's/^\([A-Za-z_][A-Za-z0-9_]*\)=.*$/\1/p')"
    for nt_name in $nt_names; do
        case "$nt_name" in
            LD_*|DYLD_*|PYTHON*) ;;
            GTK_*|GDK_*|GIO_*|GSETTINGS_*|GI_*|GJS_*|GST_*|QT_*|QTWEBENGINE_*|\
            QML_*|QML2_*|WEBKIT_*|LIBGL_*|MESA_*|EGL_*|VK_*)
                case "$nt_name" in
                    *MODULE*|*PLUGIN*|*PRELOAD*|*LIBRAR*|*LAYER*|*DRIVER*|*ICD*|\
                    *BUNDLE*|*SANDBOX*|*EXEC*|*LAUNCH*|*PROFIL*|*FLAGS*|*ARGS*|\
                    *PATH*|*PREFIX*|*AUDIT*) ;;
                    *) continue ;;
                esac ;;
            *) continue ;;
        esac
        unset "$nt_name" 2>/dev/null
    done
    unset nt_name nt_names
}
nt_scrub_loaders
find_qt_runtime() {
    for cmd in qml6 qml; do
        if command -v "$cmd" >/dev/null 2>&1; then
            command -v "$cmd"
            return 0
        fi
    done
    for path in /usr/lib/qt6/bin/qml /usr/lib64/qt6/bin/qml; do
        if [ -x "$path" ]; then
            printf '%s\n' "$path"
            return 0
        fi
    done
    for path in /usr/lib/*; do
        if [ -x "$path/qt6/bin/qml" ]; then
            printf '%s\n' "$path/qt6/bin/qml"
            return 0
        fi
    done
    return 1
}
nt_qmlquote() {
    printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g' |
        awk 'NR > 1 { printf "\\n" } { printf "%s", $0 }'
}
run_qt() {
    qml_runner="$1"
    [ -z "$qml_runner" ] && return 1
    script_dir="$(dirname "$script_path")"
    script_name="$(basename "$script_path")"
    script_name="${script_name%.*}"
    app_dir="$script_dir/$script_name"
    mkdir -p "$app_dir" || return 1
    [ -w "$app_dir" ] || {
        echo "neutrino: cannot write $app_dir" >&2
        return 1
    }
    qml_doc="$app_dir/.window.$$.qml"
    set -C
    exec 8>"$qml_doc"
    set +C
    rm -f "$qml_doc"
    qml_url="file://$(nt_qmlquote "$script_path")"
    cat >&8 <<QMLEOF
import QtQuick
import QtWebEngine
Window {
    id: root
    readonly property string ntSource: ntRead()
    readonly property var nt: ntBuild(ntSource)
    readonly property var cfg: nt.config
    readonly property string ntPreload: nt.buildPreloadScript(
        'function(m){console.log("__NEUTRINO__"+encodeURIComponent(m));}',
        "console",
        nt.themeLiteral(root.ntTheme),
        nt.fontsLiteral(root.ntFonts))
    SystemPalette {
        id: sysPalette
        colorGroup: SystemPalette.Active
    }
    function ntHex(c, over) {
        return root.nt.flattenColor(
            root.nt.toHex({ red: c.r, green: c.g, blue: c.b }), c.a, over)
    }
    readonly property var ntTheme: root.ntReadTheme(
        sysPalette.window, sysPalette.windowText, sysPalette.base, sysPalette.text,
        sysPalette.highlight, sysPalette.highlightedText, sysPalette.mid)
    readonly property var ntFonts: root.ntReadFonts(
        Qt.application.font.family,
        Qt.application.font.pointSize,
        Qt.application.font.pixelSize,
        Qt.application.font.weight)
    function ntReadFonts(family, pointSize, pixelSize, weight) {
        var raw = { source: "qt" }
        if (pixelSize > 0) {
            raw.unit = "px"
            raw.uiSize = pixelSize
        } else if (pointSize > 0) {
            raw.unit = "pt"
            raw.uiSize = pointSize
        } else {
            return null
        }
        raw.uiWeight = weight
        var name = String(family || "")
        var alias = root.nt.fontLookup(root.nt.qtFontAliases, name)
        if (alias !== "") {
            raw.uiFamily = ""
            raw.uiGeneric = alias
        } else {
            raw.uiFamily = name
        }
        return root.nt.normalizeFonts(raw)
    }
    function ntReadTheme(bgColor, fgColor, baseColor, textColor,
                         accentColor, accentTextColor, borderColor) {
        var bg = root.nt.toHex({ red: bgColor.r, green: bgColor.g, blue: bgColor.b })
        return root.nt.normalizeTheme({
            source: "qt",
            background: root.ntHex(bgColor, bg),
            foreground: root.ntHex(fgColor, bg),
            base: root.ntHex(baseColor, bg),
            text: root.ntHex(textColor, bg),
            accent: root.ntHex(accentColor, bg),
            accentText: root.ntHex(accentTextColor, bg),
            border: root.ntHex(borderColor, bg)
        })
    }
    onNtThemeChanged: {
        if (!view.documentLoaded) {
            return
        }
        var js = root.nt.buildThemeScript(root.ntTheme)
        if (js) {
            view.runJavaScript(js)
        }
    }
    visible: true
    title: cfg.title
    flags: root.nt.undecorated() ? (Qt.Window | Qt.FramelessWindowHint) : Qt.Window
    color: root.nt.resolveBackground(root.ntTheme)
    function ntRead() {
        var xhr = new XMLHttpRequest()
        xhr.open("GET", "$qml_url", false)
        xhr.send()
        return xhr.responseText
    }
    function ntBuild(src) {
        return (new Function("NeutrinoQml", src + "; return NeutrinoWebview;"))(true)
    }
    property int ntFrameL: 0
    property int ntFrameT: 0
    function ntReadFrame() {
        view.runJavaScript("[window.screenX,window.screenY]", root.ntWorld(),
            function (p) {
                if (!p || p.length !== 2) {
                    console.warn("neutrino: the engine did not say where this"
                        + " window's frame is; a move will place the content")
                    return
                }
                var l = root.x - p[0]
                var t = root.y - p[1]
                if (!isFinite(l) || !isFinite(t) || l < 0 || t < 0
                        || l > 200 || t > 200) {
                    console.warn("neutrino: refused a frame margin of "
                        + l + "," + t + "; a move will place the content")
                    return
                }
                root.ntFrameL = l
                root.ntFrameT = t
                console.warn("neutrino: this frame measures l=" + l + " t=" + t)
            })
    }
    function ntWorld() {
        var id
        try {
            id = WebEngineScript.ApplicationWorld
        } catch (e) {
            id = undefined
        }
        return (typeof id === "number") ? id : 1
    }
    function ntRoute(raw) {
        var msg = root.nt.parseMessage(raw)
        if (!msg) {
            console.warn("neutrino: refused a malformed record")
            return
        }
        if (msg.action === "resize") { root.width = msg.width; root.height = msg.height }
        else if (msg.action === "move") {
            root.x = msg.x + root.ntFrameL
            root.y = msg.y + root.ntFrameT
        }
        else if (msg.action === "resizeBy") {
            root.width = Math.max(1, root.width + msg.width)
            root.height = Math.max(1, root.height + msg.height)
        }
        else if (msg.action === "moveBy") { root.x = root.x + msg.x; root.y = root.y + msg.y }
        else if (msg.action === "close") root.close()
        else if (msg.action === "openExternal") Qt.openUrlExternally(msg.url)
    }
    Component.onCompleted: {
        root.width = cfg.width
        root.height = cfg.height
        root.x = (Screen.width - root.width) / 2
        root.y = (Screen.height - root.height) / 2
    }
    WebEngineView {
        id: view
        anchors.fill: parent
        backgroundColor: root.color
        property bool preloadInjected: false
        property bool documentLoaded: false
        property bool contentLoaded: false
        onTitleChanged: {
            var name = root.nt.acceptDocumentTitle(view.url, view.title)
            if (name !== null) {
                root.title = name
            }
        }
        onLoadingChanged: function(info) {
            if (info.status === WebEngineView.LoadSucceededStatus) {
                if (!preloadInjected) {
                    preloadInjected = true
                    root.nt.rememberTrustedView(view.url)
                    root.ntReadFrame()
                    view.runJavaScript(root.ntPreload)
                    view.runJavaScript(root.nt.extractPageScript(root.ntSource))
                }
                view.documentLoaded = true
            }
        }
        onJavaScriptConsoleMessage: function(level, message, lineNumber, sourceID) {
            if (String(message).indexOf("__NEUTRINO__") !== 0) {
                console.warn("neutrino page: " + message + " (" + sourceID + ":" + lineNumber + ")")
                return
            }
            if (!root.nt.isTrustedView(view.url)) {
                root.nt.note(
                    "refused a message from a document the view was not given")
                return
            }
            root.ntRoute(decodeURIComponent(String(message).substring(12)))
        }
        function ntConnectNewWindow() {
            var names = ["newWindowRequested", "newViewRequested"]
            for (var i = 0; i < names.length; i++) {
                var sig = view[names[i]]
                if (sig && typeof sig.connect === "function") {
                    sig.connect(ntOnNewWindow)
                    console.warn("neutrino: new windows arrive on " + names[i])
                    return
                }
            }
            console.warn("neutrino: this QtWebEngine raises no new-window signal this file knows;"
                + " a link with a target will open nothing and go nowhere")
        }
        function ntOnNewWindow(request) {
            var wanted = String(request.requestedUrl)
            var byUser = "?"
            try { byUser = String(request.userInitiated) } catch (e) { byUser = "?" }
            console.warn("neutrino: refused a new window for " + wanted
                + " (userInitiated=" + byUser + ")")
            if (root.nt.mayOpenExternal(wanted)) {
                Qt.openUrlExternally(request.requestedUrl)
            }
        }
        onNavigationRequested: function(request) {
            var target = String(request.url)
            if (root.nt.isOwnDocument(target)) {
                return
            }
            if (target.indexOf("data:") === 0 && !view.contentLoaded) {
                view.contentLoaded = true
                return
            }
            console.warn("neutrino: refused navigation to " + request.url)
            if (typeof request.reject === "function") {
                request.reject()
            } else {
                request.action = WebEngineNavigationRequest.IgnoreRequest
            }
            if (root.nt.mayOpenExternal(String(request.url))) {
                Qt.openUrlExternally(request.url)
            }
        }
        Component.onCompleted: {
            try { ntConnectNewWindow() } catch (e) {
                console.warn("neutrino: could not connect the new-window signal: " + e)
            }
            view.loadHtml(root.nt.dressedDocument(
                root.nt.titledDocument(
                    root.nt.extractHtmlDocument(root.ntSource),
                    cfg.title),
                root.ntTheme, root.ntFonts))
        }
    }
}
QMLEOF
    qml_fd=""
    for qml_fddir in /dev/fd /proc/self/fd; do
        if [ -r "$qml_fddir/8" ] && exec 9<"$qml_fddir/8"; then
            qml_fd="$qml_fddir/9"
            break
        fi
    done
    exec 8>&-
    if [ -z "$qml_fd" ]; then
        echo "neutrino: cannot hand the engine a document without a name here" >&2
        return "$nt_ex_noengine"
    fi
    QML_XHR_ALLOW_FILE_READ=1 \
    QT_QPA_PLATFORM="${QT_QPA_PLATFORM:-xcb}" \
    LIBGL_ALWAYS_SOFTWARE="${LIBGL_ALWAYS_SOFTWARE:-1}" \
    QTWEBENGINE_CHROMIUM_FLAGS="${QTWEBENGINE_CHROMIUM_FLAGS:---disable-dev-shm-usage}" \
    "$qml_runner" "$qml_fd"
}
neutrino_webkit_sandbox=0
if command -v bwrap >/dev/null 2>&1 &&
   bwrap --unshare-user --ro-bind / / /bin/true >/dev/null 2>&1
then neutrino_webkit_sandbox=1
fi
export NEUTRINO_WEBKIT_SANDBOX="$neutrino_webkit_sandbox"
nt_resolve() {
    ( cd "$1" 2>/dev/null && pwd -P ) || printf '%s\n' "$1"
}
nt_sbquote() { printf '%s' "$1" | sed -e 's/\\/\\\\/g' -e 's/"/\\"/g'; }
nt_macos_profile() {
    appdir_r="$(nt_sbquote "$(nt_resolve "$1")")"
    tmpdir_r="$(nt_sbquote "$(nt_resolve "${TMPDIR:-/tmp}")")"
    home_r="$(nt_sbquote "$(nt_resolve "$HOME")")"
    printf '%s\n' \
'(version 1)' \
'(allow default)' \
'' \
'(deny file-write*)' \
'(allow file-write*' \
"  (subpath \"$appdir_r\")" \
"  (subpath \"$tmpdir_r\")" \
"  (subpath \"$home_r/Library/Caches\")" \
"  (subpath \"$home_r/Library/Preferences\")" \
"  (subpath \"$home_r/Library/WebKit\")" \
"  (subpath \"$home_r/Library/Saved Application State\")" \
'  (subpath "/private/var/folders")' \
'  (regex #"^/dev/(null|zero|random|urandom|tty|dtracehelper)$"))' \
'' \
'(deny process-exec*' \
"  (subpath \"$appdir_r\")" \
"  (subpath \"$tmpdir_r\")" \
"  (subpath \"$home_r/Library/Caches\")" \
"  (subpath \"$home_r/Library/Preferences\")" \
"  (subpath \"$home_r/Library/WebKit\")" \
"  (subpath \"$home_r/Library/Saved Application State\")" \
'  (subpath "/private/var/folders"))' \
'' \
'(deny mach-lookup' \
'  (global-name "com.apple.SecurityServer")' \
'  (global-name "com.apple.securityd.xpc")' \
'  (global-name "com.apple.tccd")' \
'  (global-name "com.apple.tccd.system"))' \
'' \
'; LaunchServices. Both names, and it has to be both -- see the comment above' \
'; this function. Denying either one on its own leaves an .app bundle written' \
'; from inside the sandbox launching normally.' \
'(deny mach-lookup' \
'  (global-name "com.apple.coreservices.launchservicesd")' \
'  (global-name "com.apple.coreservices.quarantine-resolver"))' \
'(deny appleevent-send)' \
'(deny mach-priv-task-port)' \
'(deny signal (target others))' \
''
}
run_macos() {
    script_dir="$(dirname "$script_path")"
    script_name="$(basename "$script_path")"
    script_name="${script_name%.*}"
    app_dir="$script_dir/$script_name"
    mkdir -p "$app_dir" 2>/dev/null
    profile="$(nt_macos_profile "$app_dir")"
    if [ -z "$profile" ]; then
        echo "neutrino: could not build the seatbelt profile; running unconfined" >&2
        NEUTRINO_SCRIPT_PATH="$script_path" exec osascript -l JavaScript "$script_path"
    fi
    NEUTRINO_SCRIPT_PATH="$script_path" NEUTRINO_MACOS_PROFILE="$profile" \
        exec osascript -l JavaScript "$script_path"
}
run_pygobject() {
    script_dir="$(dirname "$script_path")"
    script_name="$(basename "$script_path")"
    script_name="${script_name%.*}"
    app_dir="$script_dir/$script_name"
    mkdir -p "$app_dir" || return 1
    [ -w "$app_dir" ] || {
        echo "neutrino: cannot write $app_dir" >&2
        return 1
    }
    py_doc="$app_dir/.window.$$.py"
    set -C
    exec 8>"$py_doc"
    set +C
    rm -f "$py_doc"
    cat >&8 <<'PYGIEOF'
import json
import os
import sys
NOENGINE = 69
def unavailable(what):
    sys.stderr.write("neutrino: pygobject lane unavailable: %s\n" % (what,))
    sys.exit(NOENGINE)
try:
    import gi
except Exception as exc:
    unavailable("no PyGObject (%s)" % (exc,))
WEBKIT_API = None
for candidate in ("4.1", "4.0"):
    try:
        gi.require_version("WebKit2", candidate)
        gi.require_version("JavaScriptCore", candidate)
        WEBKIT_API = candidate
        break
    except Exception:
        continue
if WEBKIT_API is None:
    unavailable("WebKit2 introspection typelibs not found")
try:
    gi.require_version("Gtk", "3.0")
    gi.require_version("Gdk", "3.0")
    gi.require_version("Pango", "1.0")
    from gi.repository import GLib, Gdk, Gio, Gtk, JavaScriptCore, Pango, WebKit2
except Exception as exc:
    unavailable("%s" % (exc,))
script_path = os.environ.get("NEUTRINO_SCRIPT_PATH", "")
if not script_path:
    unavailable("NEUTRINO_SCRIPT_PATH was not set")
try:
    handle = open(script_path, "rb")
    try:
        source = handle.read().decode("utf-8", "replace")
    finally:
        handle.close()
except Exception as exc:
    unavailable("could not read %s (%s)" % (script_path, exc))
if GLib.getenv("NEUTRINO_WEBKIT_SANDBOX") == "1":
    try:
        WebKit2.WebContext.get_default().set_sandbox_enabled(True)
    except Exception as exc:
        sys.stderr.write("neutrino: webkit sandbox unavailable: %s\n" % (exc,))
else:
    sys.stderr.write(
        "neutrino: webkit sandbox off: this system refused a user namespace\n")
ucm = WebKit2.UserContentManager()
view_holder = {}
committed = {"done": False}
def showing():
    view = view_holder.get("view")
    if view is None:
        return ""
    try:
        return view.get_uri() or ""
    except Exception:
        return ""
def on_message(_ucm, result):
    where = showing()
    if not call("isTrustedView", js_string(where)).to_boolean():
        sys.stderr.write("neutrino: refused a message from %s\n" % (where,))
        return
    try:
        raw = result.get_js_value().to_string()
    except Exception:
        raw = ""
    route(raw)
def route(raw):
    message = call("parseMessage", js_string(raw))
    if message is None or not message.is_object():
        sys.stderr.write("neutrino: refused a malformed record\n")
        return
    action = message.object_get_property("action").to_string()
    if action == "resize":
        window.resize(
            message.object_get_property("width").to_int32(),
            message.object_get_property("height").to_int32(),
        )
    elif action == "move":
        window.move(
            message.object_get_property("x").to_int32(),
            message.object_get_property("y").to_int32(),
        )
    elif action == "resizeBy":
        current_w, current_h = window.get_size()
        window.resize(
            max(1, current_w + message.object_get_property("width").to_int32()),
            max(1, current_h + message.object_get_property("height").to_int32()),
        )
    elif action == "moveBy":
        current_x, current_y = window.get_position()
        window.move(
            current_x + message.object_get_property("x").to_int32(),
            current_y + message.object_get_property("y").to_int32(),
        )
    elif action == "close":
        window.destroy()
    elif action == "openExternal":
        open_external(message.object_get_property("url").to_string())
def open_external(url):
    if not call("mayOpenExternal", js_string(url)).to_boolean():
        return
    try:
        Gio.AppInfo.launch_default_for_uri(url, None)
    except Exception:
        try:
            GLib.spawn_async(
                None, ["xdg-open", url], None, GLib.SpawnFlags.SEARCH_PATH, None)
        except Exception:
            pass
ucm.register_script_message_handler("neutrino")
ucm.connect("script-message-received::neutrino", on_message)
def inject(text, when):
    if not text:
        return
    try:
        ucm.add_script(WebKit2.UserScript.new(
            text, WebKit2.UserContentInjectedFrames.TOP_FRAME, when, None, None))
    except Exception as exc:
        sys.stderr.write("neutrino: could not inject: %s\n" % (exc,))
view = WebKit2.WebView(user_content_manager=ucm)
ctx = JavaScriptCore.Context.new()
def raised():
    exc = ctx.get_exception()
    if exc is None:
        return None
    ctx.clear_exception()
    return exc.get_message()
def js_string(text):
    return JavaScriptCore.Value.new_string(ctx, text)
def js_number(value):
    return JavaScriptCore.Value.new_number(ctx, value)
def js_null():
    return JavaScriptCore.Value.new_null(ctx)
def js_json(value):
    return JavaScriptCore.Value.new_from_json(ctx, json.dumps(value))
ctx.evaluate(
    "var NT = (function (NeutrinoPy) {\n"
    + source
    + "\n; return NeutrinoWebview; })(true);\n"
    "var NTNOTES = [];\n"
    "NT.noteSink = function (m) { NTNOTES.push(String(m)); };\n",
    -1,
)
failed = raised()
if failed is not None:
    unavailable("could not evaluate the app source (%s)" % (failed,))
nt = ctx.get_value("NT")
notes = ctx.get_value("NTNOTES")
if nt is None or not nt.is_object():
    unavailable("the app source did not yield a NeutrinoWebview")
def drain():
    count = notes.object_get_property("length").to_int32()
    for index in range(count):
        sys.stderr.write(notes.object_get_property_at_index(index).to_string() + "\n")
    if count:
        notes.object_invoke_method("splice", [js_number(0), js_number(count)])
def call(name, *args):
    result = nt.object_invoke_method(name, list(args))
    failure = raised()
    drain()
    if failure is not None:
        raise RuntimeError("%s: %s" % (name, failure))
    return result
config = nt.object_get_property("config")
title = config.object_get_property("title").to_string()
width = config.object_get_property("width").to_int32()
height = config.object_get_property("height").to_int32()
decorated = not call("undecorated").to_boolean()
def read_theme():
    try:
        style = Gtk.Box().get_style_context()
    except Exception as exc:
        sys.stderr.write("neutrino: could not read the desktop theme: %s\n" % (exc,))
        return None
    names = nt.object_get_property("gtkColorNames").to_string().split(",")
    keys = nt.object_get_property("themeKeys").to_string().split(",")
    if len(names) != len(keys):
        sys.stderr.write("neutrino: the palette and its GTK names are different lengths\n")
        return None
    hexes = []
    alphas = []
    for name in names:
        found = style.lookup_color(name)
        if not found[0]:
            sys.stderr.write("neutrino: this theme defines no %s\n" % (name,))
            return None
        rgba = found[1]
        hexes.append(call("toHex", js_json({
            "red": rgba.red, "green": rgba.green, "blue": rgba.blue,
        })).to_string())
        alphas.append(rgba.alpha)
    raw = {"source": "gtk"}
    for index in range(len(keys)):
        raw[keys[index]] = call(
            "flattenColor", js_string(hexes[index]),
            js_number(alphas[index]), js_string(hexes[0]),
        ).to_string()
    return raw
theme_state = {"value": js_null()}
def take_theme():
    raw = read_theme()
    if raw is None:
        return False
    taken = call("normalizeTheme", js_json(raw))
    if taken.is_null() or taken.is_undefined():
        return False
    if not call("themesDiffer", theme_state["value"], taken).to_boolean():
        return False
    theme_state["value"] = taken
    nt.object_set_property("theme", taken)
    return True
if not take_theme():
    sys.stderr.write("neutrino: could not read the desktop palette; using %s\n" % (
        call("resolveBackground", theme_state["value"]).to_string(),))
font_settings = []
def open_settings(schema_id):
    try:
        source = Gio.SettingsSchemaSource.get_default()
        if source is None or source.lookup(schema_id, True) is None:
            return None
        settings = Gio.Settings.new(schema_id)
    except Exception as exc:
        sys.stderr.write("neutrino: could not open %s: %s\n" % (schema_id, exc))
        return None
    font_settings.append(settings)
    return settings
def setting_string(settings, key):
    if settings is None or key not in settings.list_keys():
        return ""
    return settings.get_string(key) or ""
def setting_bool(settings, key):
    if settings is None or key not in settings.list_keys():
        return False
    return bool(settings.get_boolean(key))
def gather_font_strings():
    gathered = {"gtkFontName": "", "names": {}, "interface": {},
                "titlebar": "", "titlebarSystem": False}
    settings = Gtk.Settings.get_default()
    if settings is not None:
        gathered["gtkFontName"] = settings.get_property("gtk-font-name") or ""
    ids = nt.object_get_property("gtkFontSchemas").to_string().split(",")
    opened = {}
    for schema_id in ids:
        opened[schema_id] = open_settings(schema_id)
        name = setting_string(opened[schema_id], "font-name")
        if name != "":
            gathered["names"][schema_id] = name
    chose = call("gtkFontSchemaChoice",
                 js_string(gathered["gtkFontName"]),
                 js_json(gathered["names"])).to_string()
    if chose != "" and opened.get(chose) is not None:
        for key in nt.object_get_property("gtkFontKeys").to_string().split(","):
            gathered["interface"][key] = setting_string(opened[chose], key)
    for schema_id in nt.object_get_property("gtkFontWmSchemas").to_string().split(","):
        wm = open_settings(schema_id)
        if wm is None:
            continue
        titlebar = setting_string(wm, "titlebar-font")
        if titlebar == "":
            continue
        gathered["titlebar"] = titlebar
        gathered["titlebarSystem"] = setting_bool(wm, "titlebar-uses-system-font")
        break
    return gathered
def parse_font_strings(chosen):
    out = {}
    for role in nt.object_get_property("fontRoles").to_string().split(","):
        text = chosen.get(role, "")
        if not text:
            continue
        desc = Pango.FontDescription.from_string(text)
        size = desc.get_size() / Pango.SCALE
        if desc.get_size_is_absolute():
            size = size * 72.0 / 96.0
        fields = call("gtkRoleFields", js_string(role), js_json({
            "family": desc.get_family() or "",
            "size": size,
            "weight": int(desc.get_weight()),
        }))
        out[role] = json.loads(fields.to_json(0))
    return out
def read_fonts():
    try:
        gathered = gather_font_strings()
        chosen = json.loads(
            call("gtkChooseFontStrings", js_json(gathered)).to_json(0))
        return json.loads(
            call("gtkFontsFromParsed", js_json(parse_font_strings(chosen))).to_json(0))
    except Exception as exc:
        sys.stderr.write("neutrino: could not read the desktop fonts: %s\n" % (exc,))
        return None
fonts_state = {"value": js_null()}
def take_fonts():
    raw = read_fonts()
    if raw is None:
        return False
    taken = call("normalizeFonts", js_json(raw))
    if taken.is_null() or taken.is_undefined():
        return False
    if not call("fontsDiffer", fonts_state["value"], taken).to_boolean():
        return False
    fonts_state["value"] = taken
    nt.object_set_property("fonts", taken)
    return True
if not take_fonts():
    sys.stderr.write("neutrino: could not read the desktop fonts; "
                     "the page falls back to the engine's own\n")
def force_scheme():
    if not call("gtkPreferDark", theme_state["value"]).to_boolean():
        return
    settings = Gtk.Settings.get_default()
    if settings is None:
        return
    try:
        if settings.get_property("gtk-application-prefer-dark-theme"):
            return
        settings.set_property("gtk-application-prefer-dark-theme", True)
    except Exception as exc:
        sys.stderr.write("neutrino: could not force the colour scheme: %s\n" % (exc,))
force_scheme()
def paint(widget_window, web_view, background):
    rgb = call("parseColor", js_string(background))
    if rgb.is_null() or rgb.is_undefined():
        return
    red = rgb.object_get_property("red").to_double()
    green = rgb.object_get_property("green").to_double()
    blue = rgb.object_get_property("blue").to_double()
    try:
        rgba = Gdk.RGBA()
        rgba.red, rgba.green, rgba.blue, rgba.alpha = red, green, blue, 1.0
        web_view.set_background_color(rgba)
    except Exception as exc:
        sys.stderr.write("neutrino: could not paint the view: %s\n" % (exc,))
    try:
        provider = Gtk.CssProvider()
        provider.load_from_data(
            ("window, .background { background-color: rgb(%d,%d,%d); }" % (
                round(red * 255), round(green * 255), round(blue * 255))).encode("utf-8"))
        widget_window.get_style_context().add_provider(
            provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
    except Exception as exc:
        sys.stderr.write("neutrino: could not paint the window: %s\n" % (exc,))
html = call(
    "dressedDocument",
    call(
        "titledDocument",
        call("extractHtmlDocument", js_string(source)),
        js_string(title),
    ),
    theme_state["value"],
    fonts_state["value"],
).to_string()
page_script = call("extractPageScript", js_string(source)).to_string()
preload = call(
    "buildPreloadScript",
    js_string("window.webkit.messageHandlers.neutrino.postMessage"),
    js_string("scriptmessage"),
    call("themeLiteral", theme_state["value"]),
    call("fontsLiteral", fonts_state["value"]),
).to_string()
inject(preload, WebKit2.UserScriptInjectionTime.START)
inject(page_script, WebKit2.UserScriptInjectionTime.END)
GLib.set_prgname("neutrino")
window = Gtk.Window(
    title=title,
    default_width=width,
    default_height=height,
    decorated=decorated,
)
window.set_position(Gtk.WindowPosition.CENTER)
window.connect("destroy", lambda _w: Gtk.main_quit())
paint(window, view, call("resolveBackground", theme_state["value"]).to_string())
view_holder["view"] = view
def on_style_updated(_widget):
    if not take_theme():
        return
    force_scheme()
    if call("followsTheme").to_boolean():
        paint(window, view, call("resolveBackground", theme_state["value"]).to_string())
    if not committed["done"]:
        return
    js = call("buildThemeScript", theme_state["value"])
    if js.is_null() or js.is_undefined():
        return
    try:
        view.run_javascript(js.to_string(), None, None, None)
    except Exception as exc:
        sys.stderr.write("neutrino: could not deliver the theme: %s\n" % (exc,))
def deliver_fonts():
    if not take_fonts():
        return
    if not committed["done"]:
        return
    js = call("buildFontScript", fonts_state["value"])
    if js.is_null() or js.is_undefined():
        return
    try:
        view.run_javascript(js.to_string(), None, None, None)
    except Exception as exc:
        sys.stderr.write("neutrino: could not deliver the fonts: %s\n" % (exc,))
def on_style_updated_all(widget):
    on_style_updated(widget)
    deliver_fonts()
window.connect("style-updated", on_style_updated_all)
def on_theme_name(_settings, _pspec):
    on_style_updated(None)
def on_font_name(_settings, _pspec):
    deliver_fonts()
try:
    gtk_settings = Gtk.Settings.get_default()
    if gtk_settings is not None:
        gtk_settings.connect("notify::gtk-theme-name", on_theme_name)
        gtk_settings.connect("notify::gtk-font-name", on_font_name)
except Exception as exc:
    sys.stderr.write("neutrino: no settings watcher on this lane: %s\n" % (exc,))
def on_font_key(_settings, _key):
    deliver_fonts()
try:
    watched = (nt.object_get_property("gtkFontKeys").to_string().split(",") +
               nt.object_get_property("gtkFontWmKeys").to_string().split(","))
    for settings in font_settings:
        keys = settings.list_keys()
        for key in watched:
            if key == "font-name" or key not in keys:
                continue
            settings.connect("changed::" + key, on_font_key)
except Exception as exc:
    sys.stderr.write("neutrino: no font-settings watcher on this lane: %s\n" % (exc,))
def on_load_changed(_view, event):
    if event == WebKit2.LoadEvent.COMMITTED:
        committed["done"] = True
        try:
            call("rememberTrustedView", js_string(view.get_uri() or ""))
        except Exception:
            pass
view.connect("load-changed", on_load_changed)
settings = view.get_settings()
try:
    settings.set_enable_developer_extras(False)
    settings.set_allow_file_access_from_file_urls(False)
    settings.set_allow_universal_access_from_file_urls(False)
    settings.set_javascript_can_access_clipboard(False)
    settings.set_enable_write_console_messages_to_stdout(False)
except Exception:
    pass
def on_decide_policy(_view, decision, kind):
    types = WebKit2.PolicyDecisionType
    if kind != types.NAVIGATION_ACTION and kind != types.NEW_WINDOW_ACTION:
        return False
    try:
        uri = decision.get_navigation_action().get_request().get_uri() or ""
    except Exception:
        uri = ""
    if not committed["done"] or call("isOwnDocument", js_string(uri)).to_boolean():
        return False
    decision.ignore()
    sys.stderr.write("neutrino: refused navigation to %s\n" % (uri,))
    open_external(uri)
    return True
view.connect("decide-policy", on_decide_policy)
def on_title(view_object, _pspec):
    name = call("acceptDocumentTitle", js_string(showing()),
                js_string(view_object.get_title() or ""))
    if not name.is_null():
        window.set_title(name.to_string())
view.connect("notify::title", on_title)
view.load_html(html, None)
window.add(view)
window.show_all()
Gtk.main()
PYGIEOF
    py_fd=""
    for py_fddir in /dev/fd /proc/self/fd; do
        if [ -r "$py_fddir/8" ] && exec 9<"$py_fddir/8"; then
            py_fd="$py_fddir/9"
            break
        fi
    done
    exec 8>&-
    if [ -z "$py_fd" ]; then
        echo "neutrino: cannot hand the engine a document without a name here" >&2
        return "$nt_ex_noengine"
    fi
    nt_unset_gtk_loaders
    NEUTRINO_SCRIPT_PATH="$script_path" python3 -I -B "$py_fd"
}
nt_ex_noengine=69
nt_unset_gtk_loaders() {
    unset GTK_PATH GTK_EXE_PREFIX GTK_IM_MODULE_FILE \
          GDK_PIXBUF_MODULE_FILE GDK_PIXBUF_MODULEDIR \
          GIO_MODULE_DIR GSETTINGS_SCHEMA_DIR LOCPATH \
          LD_PRELOAD LD_LIBRARY_PATH
}
for nt_engine in gjs gjs-console cjs cjs-console; do
    command -v "$nt_engine" >/dev/null 2>&1 || continue
    ( nt_unset_gtk_loaders
      NEUTRINO_SCRIPT_PATH="$script_path" exec "$nt_engine" "$script_path" )
    nt_status=$?
    [ "$nt_status" = "$nt_ex_noengine" ] || [ "$nt_status" = 127 ] || exit "$nt_status"
done
if command -v osascript >/dev/null 2>&1
then run_macos
fi
if qt_runner="$(find_qt_runtime)"
then
    run_qt "$qt_runner"
    nt_status=$?
    [ "$nt_status" = "$nt_ex_noengine" ] || exit "$nt_status"
fi
if command -v python3 >/dev/null 2>&1
then
    run_pygobject
    nt_status=$?
    [ "$nt_status" = "$nt_ex_noengine" ] || exit "$nt_status"
fi
echo "neutrino: no runtime here can open a window (looked for gjs, cjs, a Qt QML runtime, osascript, and python3 with PyGObject)" >&2
exit 1
exit $?;:<<'//</script></body></html>' #-->
<!doctype html><html><head><meta charset="utf-8">
<meta http-equiv="Content-Security-Policy" content="script-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'; frame-src 'none'">
<style>
:root {
    --fg:      var(--neutrino-CanvasText, CanvasText);
    --bg:      var(--neutrino-Canvas, Canvas);
    --face:    var(--neutrino-ButtonFace, ButtonFace);
    --facefg:  var(--neutrino-ButtonText, ButtonText);
    --line:    var(--neutrino-ButtonBorder, ButtonBorder);
    --accent:  var(--neutrino-Highlight, Highlight);
    --accentfg: var(--neutrino-HighlightText, HighlightText);
}
html, body { margin: 0; height: 100% }
body {
    background: var(--bg);
    color: var(--fg);
    font: 13px/1.5 -apple-system, Segoe UI, Cantarell, Ubuntu, sans-serif;
}
main {
    box-sizing: border-box;
    height: 100%;
    padding: 22px 24px;
    display: flex;
    flex-direction: column;
    gap: 16px;
}
h1 { margin: 0; font-size: 20px; font-weight: 600; letter-spacing: -0.01em }
header p { margin: 4px 0 0; opacity: 0.7 }
.facts {
    margin: 0;
    display: grid;
    grid-template-columns: 92px 1fr;
    gap: 4px 12px;
    align-content: start;
}
.facts dt {
    text-transform: uppercase;
    font-size: 10px;
    letter-spacing: 0.09em;
    opacity: 0.55;
    align-self: center;
}
.facts dd {
    margin: 0;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 12px;
}
.palette { display: flex; gap: 6px }
.sw {
    display: block;
    width: 100%;
    height: 26px;
    border: 1px solid var(--line);
    border-radius: 4px;
}
.sw.base       { background: var(--bg) }
.sw.text       { background: var(--fg) }
.sw.face       { background: var(--face) }
.sw.facetext   { background: var(--facefg) }
.sw.line       { background: var(--line) }
.sw.accent     { background: var(--accent) }
.sw.accenttext { background: var(--accentfg) }
.controls {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    margin-top: auto;
}
button {
    font: inherit;
    padding: 6px 12px;
    border: 1px solid var(--line);
    border-radius: 6px;
    background: var(--face);
    color: var(--facefg);
    cursor: pointer;
}
button:hover { border-color: var(--accent) }
button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px }
button.primary {
    background: var(--accent);
    color: var(--accentfg);
    border-color: var(--accent);
}
.hint { margin: 0; font-size: 11px; opacity: 0.55 }
code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 11px;
}
</style></head><body>
<main>
  <header>
    <h1>Hello from neutrino</h1>
    <p>One file, no install &mdash; a native window around a web page.</p>
  </header>
  <dl class="facts">
    <dt>engine</dt><dd id="engine">&hellip;</dd>
    <dt>transport</dt><dd id="transport">&hellip;</dd>
    <dt>window</dt><dd id="size">&hellip;</dd>
    <dt>desktop</dt><dd id="scheme">&hellip;</dd>
  </dl>
  <!--
    The palette, and nothing fills it in. Every swatch below takes its colour
    from one of the seven custom properties the launcher writes into this
    document before the first paint, so these are the desktop's own colours on
    the frame the window opens in -- and when the desktop changes, the launcher
    rewrites the properties and they follow with no script involved at all.
  -->
  <div class="palette" id="palette">
    <i class="sw base" title="Canvas"></i>
    <i class="sw text" title="CanvasText"></i>
    <i class="sw face" title="ButtonFace"></i>
    <i class="sw facetext" title="ButtonText"></i>
    <i class="sw line" title="ButtonBorder"></i>
    <i class="sw accent" title="Highlight"></i>
    <i class="sw accenttext" title="HighlightText"></i>
  </div>
  <div class="controls">
    <button id="bigger">Bigger</button>
    <button id="smaller">Smaller</button>
    <button id="nudge">Nudge</button>
    <button id="corner">Corner</button>
    <button id="rename">Rename</button>
    <button id="source">Source</button>
    <button id="close" class="primary">Close</button>
  </div>
  <p class="hint" id="hint">Every button is a standard call: <code>resizeBy</code>,
    <code>moveTo</code>, <code>document.title</code>, <code>open</code>,
    <code>close</code>.</p>
</main>
<script type=text/javascript>//*/

    var NeutrinoWebview = {
        config:
{
    "title": "neutrino",
    "width": 620,
    "height": 372,
    "background": "auto",
    "decorations": "auto"
}
    };
    NeutrinoWebview.parseColor = function (value) {
        var text = String(value || "");
        if (!/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(text)) {
            return null;
        }
        var hex = text.substring(1);
        if (hex.length === 3) {
            hex = hex.charAt(0) + hex.charAt(0) +
                  hex.charAt(1) + hex.charAt(1) +
                  hex.charAt(2) + hex.charAt(2);
        }
        return {
            red: parseInt(hex.substring(0, 2), 16) / 255,
            green: parseInt(hex.substring(2, 4), 16) / 255,
            blue: parseInt(hex.substring(4, 6), 16) / 255
        };
    };
    NeutrinoWebview.theme = null;
    NeutrinoWebview.notedOnce = null;
    NeutrinoWebview.noteOnce = function (message) {
        if (!this.notedOnce) {
            this.notedOnce = {};
        }
        var key = " " + String(message);
        if (this.notedOnce[key]) {
            return;
        }
        this.notedOnce[key] = true;
        this.note(message);
    };
    NeutrinoWebview.toHex = function (rgb) {
        var pair = function (value) {
            var n = Math.round(value * 255);
            n = (n < 0) ? 0 : ((n > 255) ? 255 : n);
            return (n < 16 ? "0" : "") + n.toString(16);
        };
        return "#" + pair(rgb.red) + pair(rgb.green) + pair(rgb.blue);
    };
    NeutrinoWebview.flattenColor = function (hex, alpha, overHex) {
        var top = this.parseColor(hex);
        if (!top) {
            return null;
        }
        var a = Number(alpha);
        if (!(a >= 0 && a <= 1)) {
            a = 1;
        }
        var under = this.parseColor(overHex);
        if (a >= 1 || !under) {
            return this.toHex(top);
        }
        return this.toHex({
            red: top.red * a + under.red * (1 - a),
            green: top.green * a + under.green * (1 - a),
            blue: top.blue * a + under.blue * (1 - a)
        });
    };
    NeutrinoWebview.relativeLuminance = function (rgb) {
        var linear = function (u) {
            return (u <= 0.03928) ? (u / 12.92) : Math.pow((u + 0.055) / 1.055, 2.4);
        };
        return 0.2126 * linear(rgb.red) +
            0.7152 * linear(rgb.green) +
            0.0722 * linear(rgb.blue);
    };
    NeutrinoWebview.isDarkSurface = function (rgb) {
        var lum = this.relativeLuminance(rgb);
        var againstWhite = 1.05 / (lum + 0.05);
        var againstBlack = (lum + 0.05) / 0.05;
        return againstWhite > againstBlack;
    };
    NeutrinoWebview.themeKeys = "background,foreground,base,text,accent,accentText,border";
    NeutrinoWebview.themeSources = "gtk,qt,macos,windows";
    NeutrinoWebview.systemColorNames = "ButtonFace,ButtonText,Canvas,CanvasText," +
        "Highlight,HighlightText,ButtonBorder";
    NeutrinoWebview.inSet = function (names, name) {
        return ("," + names + ",").indexOf("," + String(name) + ",") >= 0;
    };
    NeutrinoWebview.themeKeyList = function () {
        return String(this.themeKeys).split(",");
    };
    NeutrinoWebview.normalizeTheme = function (raw) {
        if (!raw || typeof raw !== "object") {
            return null;
        }
        var source = String(raw.source || "");
        if (!this.inSet(this.themeSources, source)) {
            return null;
        }
        var keys = this.themeKeyList();
        var colors = {};
        for (var i = 0; i < keys.length; i++) {
            var rgb = this.parseColor(raw[keys[i]]);
            if (!rgb) {
                return null;
            }
            colors[keys[i]] = this.toHex(rgb);
        }
        return {
            scheme: this.isDarkSurface(this.parseColor(colors.background)) ? "dark" : "light",
            source: source,
            colors: colors
        };
    };
    NeutrinoWebview.followsTheme = function () {
        return !this.parseColor(this.config.background);
    };
    NeutrinoWebview.undecorated = function () {
        return this.config.decorations === "none";
    };
    NeutrinoWebview.resolveBackground = function (theme) {
        if (!this.followsTheme()) {
            return this.config.background;
        }
        if (theme && theme.colors && this.parseColor(theme.colors.base)) {
            return theme.colors.base;
        }
        return "#ffffff";
    };
    NeutrinoWebview.themesDiffer = function (a, b) {
        if (!a || !b) {
            return a !== b;
        }
        if (a.scheme !== b.scheme || a.source !== b.source) {
            return true;
        }
        var keys = this.themeKeyList();
        for (var i = 0; i < keys.length; i++) {
            if (a.colors[keys[i]] !== b.colors[keys[i]]) {
                return true;
            }
        }
        return false;
    };
    NeutrinoWebview.themeLiteral = function (theme) {
        if (!theme || !theme.colors) {
            return null;
        }
        if (theme.scheme !== "dark" && theme.scheme !== "light") {
            return null;
        }
        if (!this.inSet(this.themeSources, theme.source)) {
            return null;
        }
        var values = this.themeColorList(theme);
        if (!values) {
            return null;
        }
        var keys = this.themeKeyList();
        var parts = [];
        for (var i = 0; i < keys.length; i++) {
            parts[parts.length] = keys[i] + ':"' + values[i] + '"';
        }
        return '{scheme:"' + theme.scheme + '",source:"' + theme.source +
            '",colors:{' + parts.join(",") + '}}';
    };
    NeutrinoWebview.themeColorList = function (theme) {
        if (!theme || !theme.colors) {
            return null;
        }
        var keys = this.themeKeyList();
        var out = [];
        for (var i = 0; i < keys.length; i++) {
            var value = String(theme.colors[keys[i]]);
            if (!/^#[0-9a-f]{6}$/.test(value)) {
                return null;
            }
            out[out.length] = value;
        }
        return out;
    };
    NeutrinoWebview.buildThemeScript = function (theme) {
        var literal = this.themeLiteral(theme);
        if (!literal) {
            return null;
        }
        return "window.neutrino&&window.neutrino._theme&&window.neutrino._theme(" +
            literal + ");";
    };
    NeutrinoWebview.applyTheme = function (driver, win, wv, raw) {
        var next = this.normalizeTheme(raw);
        if (!next) {
            this.noteOnce("could not read the desktop palette");
            return false;
        }
        if (!this.themesDiffer(this.theme, next)) {
            return false;
        }
        this.theme = next;
        if (driver.forceScheme) {
            try {
                driver.forceScheme(next);
            } catch (e0) {
                this.noteOnce("could not force the colour scheme: " + e0);
            }
        }
        if (this.followsTheme() && driver.repaint) {
            try {
                driver.repaint(win, wv, this.resolveBackground(next));
            } catch (e) {
                this.noteOnce("could not repaint for the new theme: " + e);
            }
        }
        var js = this.buildThemeScript(next);
        if (js && driver.evaluate) {
            try {
                driver.evaluate(wv, js);
            } catch (e2) {
                this.noteOnce("could not deliver the theme: " + e2);
            }
        }
        return true;
    };
    NeutrinoWebview.fontRoles = "ui,document,monospace,titlebar,small";
    NeutrinoWebview.fontFields = "family,generic,size,weight";
    NeutrinoWebview.fontCssPrefixes = "font,font-size,font-weight";
    NeutrinoWebview.fontGenerics = "sans-serif,sans-serif,monospace,sans-serif,sans-serif";
    NeutrinoWebview.fontSafeGenerics = "sans-serif,serif,monospace";
    NeutrinoWebview.fontFamilyTokens = "system-ui,sans-serif,serif,monospace";
    NeutrinoWebview.fontDocumentGenerics =
        "gtk:sans-serif,qt:sans-serif,macos:sans-serif,windows:serif";
    NeutrinoWebview.qtFontAliases = "Sans Serif:sans-serif,Serif:serif," +
        "Monospace:monospace,System:system-ui";
    NeutrinoWebview.fontUnits = "pt,px";
    NeutrinoWebview.ptToPx = 4 / 3;
    NeutrinoWebview.fonts = null;
    NeutrinoWebview.fontRoleList = function () {
        return String(this.fontRoles).split(",");
    };
    NeutrinoWebview.fontFieldList = function () {
        return String(this.fontFields).split(",");
    };
    NeutrinoWebview.fontCssNameList = function () {
        var roles = this.fontRoleList();
        var prefixes = String(this.fontCssPrefixes).split(",");
        var out = [];
        for (var i = 0; i < roles.length; i++) {
            for (var j = 0; j < prefixes.length; j++) {
                out[out.length] = "--neutrino-" + prefixes[j] + "-" + roles[i];
            }
        }
        return out;
    };
    NeutrinoWebview.fontLookup = function (pairs, key) {
        var list = String(pairs).split(",");
        for (var i = 0; i < list.length; i++) {
            var cut = list[i].indexOf(":");
            if (cut > 0 && list[i].substring(0, cut) === String(key)) {
                return list[i].substring(cut + 1);
            }
        }
        return "";
    };
    NeutrinoWebview.fontRoleSafeGeneric = function (role) {
        var roles = this.fontRoleList();
        var generics = String(this.fontGenerics).split(",");
        for (var i = 0; i < roles.length; i++) {
            if (roles[i] === role) {
                return generics[i];
            }
        }
        return "sans-serif";
    };
    NeutrinoWebview.fontRoleGeneric = function (role, source) {
        if (role === "document") {
            var byLane = this.fontLookup(this.fontDocumentGenerics, source);
            if (byLane !== "") {
                return byLane;
            }
        }
        return this.fontRoleSafeGeneric(role);
    };
    NeutrinoWebview.fontSizeText = function (px) {
        var hundredths = Math.round(px * 100);
        if (!(hundredths >= 100) || !(hundredths <= 99999)) {
            return null;
        }
        var whole = Math.floor(hundredths / 100);
        var frac = hundredths - whole * 100;
        if (!frac) {
            return String(whole) + "px";
        }
        return String(whole) + "." + (frac < 10 ? "0" : "") + String(frac) + "px";
    };
    NeutrinoWebview.fontFamilyPattern = /^[A-Za-z]([A-Za-z0-9 .\-]{0,61}[A-Za-z0-9.])?$/;
    NeutrinoWebview.fontCssValue = function (family, generic, role) {
        var parts = [];
        if (family !== "") {
            parts[parts.length] = "'" + family + "'";
        }
        parts[parts.length] = generic;
        if (!this.inSet(this.fontSafeGenerics, generic)) {
            parts[parts.length] = this.fontRoleSafeGeneric(role);
        }
        return parts.join(",");
    };
    NeutrinoWebview.normalizeFonts = function (raw) {
        if (!raw || typeof raw !== "object") {
            return null;
        }
        var source = String(raw.source || "");
        if (!this.inSet(this.themeSources, source)) {
            return null;
        }
        var unit = String(raw.unit || "");
        if (!this.inSet(this.fontUnits, unit)) {
            return null;
        }
        var scale = (unit === "pt") ? this.ptToPx : 1;
        var roles = this.fontRoleList();
        var out = { source: source };
        var i;
        for (i = 0; i < roles.length; i++) {
            var role = roles[i];
            var got = { family: null, generic: null, size: null, weight: null };
            var family = raw[role + "Family"];
            if (family !== undefined && family !== null && String(family) !== "") {
                var name = String(family);
                if (!this.fontFamilyPattern.test(name)) {
                    this.noteOnce("this desktop's " + role +
                        " font cannot be delivered by name");
                    return null;
                }
                got.family = name;
            }
            var generic = raw[role + "Generic"];
            if (generic !== undefined && generic !== null && String(generic) !== "") {
                if (!this.inSet(this.fontFamilyTokens, String(generic))) {
                    return null;
                }
                got.generic = String(generic);
            }
            var size = raw[role + "Size"];
            if (size !== undefined && size !== null && String(size) !== "") {
                var px = Number(size) * scale;
                if (!(px > 0) || !(px < 1000)) {
                    return null;
                }
                got.size = this.fontSizeText(px);
                if (!got.size) {
                    return null;
                }
            }
            var weight = raw[role + "Weight"];
            if (weight !== undefined && weight !== null && String(weight) !== "") {
                var w = Math.round(Number(weight));
                if (!(w >= 1) || !(w <= 1000)) {
                    return null;
                }
                got.weight = String(w);
            }
            out[role] = got;
        }
        var ui = out[roles[0]];
        if (!ui.size) {
            return null;
        }
        if (!ui.generic) {
            ui.generic = this.fontRoleGeneric(roles[0], source);
        }
        if (!ui.weight) {
            ui.weight = "400";
        }
        if (ui.family === null) {
            ui.family = "";
        }
        for (i = 1; i < roles.length; i++) {
            var r = out[roles[i]];
            var fromUi = (roles[i] === "titlebar" || roles[i] === "small");
            var unread = (r.family === null);
            if (unread) {
                r.family = fromUi ? ui.family : "";
            }
            if (!r.generic) {
                r.generic = (unread && fromUi)
                    ? ui.generic
                    : this.fontRoleGeneric(roles[i], source);
            }
            if (!r.size) {
                r.size = ui.size;
            }
            if (!r.weight) {
                r.weight = ui.weight;
            }
        }
        for (i = 0; i < roles.length; i++) {
            out[roles[i]].stack = this.fontCssValue(
                out[roles[i]].family, out[roles[i]].generic, roles[i]);
        }
        return out;
    };
    NeutrinoWebview.fontsDiffer = function (a, b) {
        if (!a || !b) {
            return a !== b;
        }
        if (a.source !== b.source) {
            return true;
        }
        var roles = this.fontRoleList();
        var fields = this.fontFieldList();
        for (var i = 0; i < roles.length; i++) {
            var ra = a[roles[i]];
            var rb = b[roles[i]];
            if (!ra || !rb) {
                return true;
            }
            for (var j = 0; j < fields.length; j++) {
                if (ra[fields[j]] !== rb[fields[j]]) {
                    return true;
                }
            }
        }
        return false;
    };
    NeutrinoWebview.fontValueList = function (fonts) {
        if (!fonts) {
            return null;
        }
        var roles = this.fontRoleList();
        var out = [];
        for (var i = 0; i < roles.length; i++) {
            var r = fonts[roles[i]];
            if (!r) {
                return null;
            }
            var family = String(r.family);
            if (family !== "" && !this.fontFamilyPattern.test(family)) {
                return null;
            }
            if (!this.inSet(this.fontFamilyTokens, String(r.generic))) {
                return null;
            }
            var size = String(r.size);
            if (!/^[1-9][0-9]{0,2}(\.[0-9]{1,2})?px$/.test(size)) {
                return null;
            }
            var weight = String(r.weight);
            if (!/^([1-9][0-9]{0,2}|1000)$/.test(weight)) {
                return null;
            }
            out[out.length] = this.fontCssValue(family, String(r.generic), roles[i]);
            out[out.length] = size;
            out[out.length] = weight;
        }
        return out;
    };
    NeutrinoWebview.fontsLiteral = function (fonts) {
        if (!fonts || !this.inSet(this.themeSources, fonts.source)) {
            return null;
        }
        var values = this.fontValueList(fonts);
        if (!values) {
            return null;
        }
        var roles = this.fontRoleList();
        var parts = [];
        var at = 0;
        for (var i = 0; i < roles.length; i++) {
            var r = fonts[roles[i]];
            parts[parts.length] = roles[i] + ':{family:"' + String(r.family) +
                '",generic:"' + String(r.generic) +
                '",stack:"' + values[at] +
                '",size:"' + values[at + 1] +
                '",weight:"' + values[at + 2] + '"}';
            at += 3;
        }
        return '{source:"' + fonts.source + '",' + parts.join(",") + "}";
    };
    NeutrinoWebview.buildFontScript = function (fonts) {
        var literal = this.fontsLiteral(fonts);
        if (!literal) {
            return null;
        }
        return "window.neutrino&&window.neutrino._fonts&&window.neutrino._fonts(" +
            literal + ");";
    };
    NeutrinoWebview.applyFonts = function (driver, wv, raw) {
        var next = this.normalizeFonts(raw);
        if (!next) {
            this.noteOnce("could not read the desktop fonts");
            return false;
        }
        if (!this.fontsDiffer(this.fonts, next)) {
            return false;
        }
        this.fonts = next;
        var js = this.buildFontScript(next);
        if (js && driver.evaluate) {
            try {
                driver.evaluate(wv, js);
            } catch (e) {
                this.noteOnce("could not deliver the fonts: " + e);
            }
        }
        return true;
    };
    NeutrinoWebview.windowsDarkSurfaces = "background:#202020,foreground:#ffffff," +
        "base:#2b2b2b,text:#ffffff,border:#3d3d3d";
    NeutrinoWebview.windowsAppsUseLightTheme = function () {
        try {
            var SystemRef = eval("System");
            var type = SystemRef.Type.GetType("Microsoft.Win32.Registry");
            if (!type) {
                this.noteOnce("no Microsoft.Win32.Registry in this runtime; " +
                    "taking the desktop for light");
                return true;
            }
            var method = type.GetMethod("GetValue");
            if (!method) {
                this.noteOnce("Microsoft.Win32.Registry carries no GetValue here; " +
                    "taking the desktop for light");
                return true;
            }
            var value = method.Invoke(null, [
                "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\" +
                    "CurrentVersion\\Themes\\Personalize",
                "AppsUseLightTheme", null]);
            if (value === null || value === undefined) {
                this.noteOnce("no AppsUseLightTheme on this machine; " +
                    "taking Windows' own default of light");
                return true;
            }
            this.noteOnce("AppsUseLightTheme=" + value);
            return Number(value) !== 0;
        } catch (e) {
            this.noteOnce("could not read the app theme setting: " + e);
            return true;
        }
    };
    NeutrinoWebview.readWindowsTheme = function (SystemRef) {
        var nt = this;
        var hexOf = function (color) {
            return nt.toHex({
                red: SystemRef.Convert.ToInt32(color.R) / 255,
                green: SystemRef.Convert.ToInt32(color.G) / 255,
                blue: SystemRef.Convert.ToInt32(color.B) / 255
            });
        };
        try {
            var system = SystemRef.Drawing.SystemColors;
            var raw = {
                source: "windows",
                background: hexOf(system.Control),
                foreground: hexOf(system.ControlText),
                base: hexOf(system.Window),
                text: hexOf(system.WindowText),
                accent: hexOf(system.Highlight),
                accentText: hexOf(system.HighlightText),
                border: hexOf(system.ControlDark)
            };
            if (!this.windowsAppsUseLightTheme()) {
                var overrides = String(this.windowsDarkSurfaces).split(",");
                for (var i = 0; i < overrides.length; i++) {
                    var cut = overrides[i].indexOf(":");
                    raw[overrides[i].substring(0, cut)] = overrides[i].substring(cut + 1);
                }
            }
            return raw;
        } catch (e) {
            this.noteOnce("could not read the desktop palette: " + e);
            return null;
        }
    };
    NeutrinoWebview.makeWindowsColor = function (SystemRef, background) {
        var rgb = this.parseColor(background);
        if (!rgb) {
            return null;
        }
        try {
            return SystemRef.Drawing.Color.FromArgb(
                255,
                SystemRef.Convert.ToInt32(Math.round(rgb.red * 255)),
                SystemRef.Convert.ToInt32(Math.round(rgb.green * 255)),
                SystemRef.Convert.ToInt32(Math.round(rgb.blue * 255)));
        } catch (e) {
            this.note("could not read the background: " + e);
            return null;
        }
    };
    NeutrinoWebview.paintWindowsView = function (wv, color) {
        if (!color) {
            return false;
        }
        try {
            var prop = wv.GetType().GetProperty("DefaultBackgroundColor");
            if (!prop || !prop.CanWrite) {
                return false;
            }
            prop.SetValue(wv, color, null);
            return true;
        } catch (e) {
            return false;
        }
    };
    NeutrinoWebview.readWindowsFonts = function (SystemRef) {
        var raw = { source: "windows", unit: "pt" };
        var found = 0;
        try {
            var system = SystemRef.Drawing.SystemFonts;
            found += this.takeWindowsFont(SystemRef, raw, "ui",
                system.MessageBoxFont) ? 1 : 0;
            found += this.takeWindowsFont(SystemRef, raw, "titlebar",
                system.CaptionFont) ? 1 : 0;
            found += this.takeWindowsFont(SystemRef, raw, "small",
                system.SmallCaptionFont) ? 1 : 0;
        } catch (e) {
            this.noteOnce("could not reach System.Drawing.SystemFonts: " + e);
            return null;
        }
        if (!found) {
            return null;
        }
        return raw;
    };
    NeutrinoWebview.takeWindowsFont = function (SystemRef, raw, role, font) {
        if (!font) {
            this.noteOnce("this machine names no " + role + " font");
            return false;
        }
        var ok = false;
        try {
            raw[role + "Family"] = String(font.Name);
            raw[role + "Size"] = SystemRef.Convert.ToDouble(font.SizeInPoints);
            raw[role + "Weight"] = font.Bold ? 700 : 400;
            ok = true;
        } catch (e) {
            this.noteOnce("could not read the " + role + " font: " + e);
        }
        try {
            font.Dispose();
        } catch (_) {}
        return ok;
    };
    NeutrinoWebview.locateDocument = function (content) {
        var text = String(content || "");
        var lower = text.toLowerCase();
        var start = lower.indexOf("<!doctype html");
        if (start < 0) {
            return { error: "there is no <!doctype html> in it" };
        }
        var tag = text.indexOf("<script", start);
        if (tag < 0) {
            return { error: "nothing opens a script after the doctype" };
        }
        var second = lower.indexOf("<!doctype html", start + 1);
        if (second >= 0 && second < tag) {
            return { error: "the document names its doctype more than once" };
        }
        var open = text.indexOf(">", tag);
        if (open < 0) {
            return { error: "the script tag after the doctype is never closed" };
        }
        var end = text.lastIndexOf("//</script>");
        if (end <= open) {
            return { error: "nothing closes the page script" };
        }
        return { start: start, tag: tag, open: open, end: end };
    };
    NeutrinoWebview.splitOrThrow = function (content) {
        var at = this.locateDocument(content);
        if (at.error) {
            throw new Error(
                "neutrino: cannot tell this file's document from its script: " + at.error);
        }
        return at;
    };
    NeutrinoWebview.extractHtmlDocument = function (content) {
        var text = String(content || "");
        var at = this.splitOrThrow(text);
        return text.substring(at.start, at.tag) + "</body></html>";
    };
    NeutrinoWebview.extractPageScript = function (content) {
        var text = String(content || "");
        var at = this.splitOrThrow(text);
        return text.substring(at.open + 1, at.end);
    };
    NeutrinoWebview.getMacScriptPath = function (ObjCRef, dollar) {
        var fileManager = dollar.NSFileManager.defaultManager;
        var currentDir = String(fileManager.currentDirectoryPath);
        var envPathObj = dollar.NSProcessInfo.processInfo.environment.objectForKey("NEUTRINO_SCRIPT_PATH");
        if (envPathObj) {
            var envPath = String(envPathObj);
            if (envPath && fileManager.fileExistsAtPath(envPath)) {
                return envPath;
            }
        }
        var argv = [];
        try {
            argv = ObjCRef.deepUnwrap(dollar.NSProcessInfo.processInfo.arguments);
        } catch (_) {
            argv = [];
        }
        for (var i = argv.length - 1; i >= 0; i--) {
            var candidate = String(argv[i] || "");
            if (!candidate || /^-/.test(candidate)) {
                continue;
            }
            if (fileManager.fileExistsAtPath(candidate)) {
                return candidate;
            }
            var combined = currentDir + "/" + candidate;
            if (fileManager.fileExistsAtPath(combined)) {
                return combined;
            }
        }
        throw new Error("Could not resolve current script path on macOS.");
    };
    NeutrinoWebview.getLinuxScriptPath = function (importsRef) {
        var GLib = importsRef["gi"]["GLib"];
        var systemRef = importsRef["system"];
        var programPath = String(systemRef.programPath);
        if (!GLib.path_is_absolute(programPath)) {
            programPath = GLib.build_filenamev([GLib.get_current_dir(), programPath]);
        }
        return programPath;
    };
    NeutrinoWebview.run = function () {
        if (!this.runEngine) {
            throw new Error("Unsupported JS runtime for webview.js");
        }
        this.runEngine();
    };
    NeutrinoWebview.engineUnavailable = function (message) {
        var err = new Error(message);
        err.neutrinoEngineUnavailable = true;
        return err;
    };
    NeutrinoWebview.externalAllowed = function () {
        return true;
    };
    NeutrinoWebview.messageSeparator = String.fromCharCode(31);
    NeutrinoWebview.hasControlCharacters = function (value) {
        var text = String(value);
        for (var i = 0; i < text.length; i++) {
            var code = text.charCodeAt(i);
            if (code < 32 || code === 127) {
                return true;
            }
        }
        return false;
    };
    NeutrinoWebview.isCoordinate = function (value) {
        return /^-?[0-9]{1,6}$/.test(String(value));
    };
    NeutrinoWebview.isDimension = function (value) {
        return /^[0-9]{1,6}$/.test(String(value)) && parseInt(String(value), 10) > 0;
    };
    NeutrinoWebview.isExternalUrl = function (value) {
        var url = String(value == null ? "" : value);
        if (!url || url.length > 2048 || this.hasControlCharacters(url)) {
            return false;
        }
        return /^https?:\/\/[^\/?#]/i.test(url) || /^mailto:[^@\s]+@[^@\s]+$/i.test(url);
    };
    NeutrinoWebview.mayOpenExternal = function (value) {
        if (!this.isExternalUrl(value)) {
            return false;
        }
        return this.externalAllowed();
    };
    NeutrinoWebview.noteSink = null;
    NeutrinoWebview.note = function (message) {
        try {
            if (this.noteSink) { this.noteSink("neutrino: " + message); }
        } catch (_) {}
        try {
            if (this.noteOut) { this.noteOut("neutrino: " + message); }
        } catch (_) {}
    };
    NeutrinoWebview.trace = function () {
    };
    NeutrinoWebview.installWindowsTrace = function () {
    };
    NeutrinoWebview.isOwnDocument = function (url) {
        var u = this.viewIdentity(url);
        return u === "" || u === "about:blank";
    };
    NeutrinoWebview.trustedView = null;
    NeutrinoWebview.viewIdentity = function (uri) {
        var text = String(uri == null ? "" : uri);
        var fragment = text.indexOf("#");
        return fragment < 0 ? text : text.substring(0, fragment);
    };
    NeutrinoWebview.rememberTrustedView = function (uri) {
        if (this.trustedView !== null) {
            return;
        }
        var identity = this.viewIdentity(uri);
        if (identity === "") {
            this.note("the view did not say which document it committed");
            return;
        }
        this.trustedView = identity;
    };
    NeutrinoWebview.isTrustedView = function (uri) {
        if (this.trustedView === null) {
            return false;
        }
        return this.viewIdentity(uri) === this.trustedView;
    };
    NeutrinoWebview.hasCommittedDocument = function () {
        return this.trustedView !== null;
    };
    NeutrinoWebview.recordPrefix = "__NEUTRINO__";
    NeutrinoWebview.acceptDocumentTitle = function (showing, title) {
        if (this.trustedView === null) {
            return null;
        }
        if (!this.isTrustedView(showing)) {
            this.noteOnce("refused a window title from a document the view " +
                "was not given");
            return null;
        }
        var text = String(title == null ? "" : title);
        if (text === "") {
            return null;
        }
        if (text.indexOf(this.recordPrefix) === 0) {
            return null;
        }
        if (text === "about:blank" || text === this.viewIdentity(showing)) {
            return null;
        }
        if (text.length > 1024 || this.hasControlCharacters(text)) {
            this.noteOnce("refused a window title this launcher cannot carry");
            return null;
        }
        return text;
    };
    NeutrinoWebview.themeCssText = function (theme) {
        var values = this.themeColorList(theme);
        if (!values) {
            return null;
        }
        var names = String(this.systemColorNames).split(",");
        var parts = [];
        for (var i = 0; i < names.length; i++) {
            parts[parts.length] = "--neutrino-" + names[i] + ":" + values[i];
        }
        return ":root{" + parts.join(";") + "}";
    };
    NeutrinoWebview.fontsCssText = function (fonts) {
        var values = this.fontValueList(fonts);
        if (!values) {
            return null;
        }
        var names = this.fontCssNameList();
        var parts = [];
        for (var i = 0; i < names.length; i++) {
            parts[parts.length] = names[i] + ":" + values[i];
        }
        return ":root{" + parts.join(";") + "}";
    };
    NeutrinoWebview.styledDocument = function (html, css) {
        var text = String(html);
        if (!css) {
            return text;
        }
        var head = text.indexOf("</head>");
        if (head < 0) {
            this.noteOnce("this document has no <head>, so what the launcher " +
                "read is on window.neutrino and not in its stylesheet");
            return text;
        }
        var at = text.substring(0, head).indexOf("<style");
        if (at < 0) {
            at = head;
        }
        return text.substring(0, at) + "<style>" + css + "</style>" +
            text.substring(at);
    };
    NeutrinoWebview.dressedDocument = function (html, theme, fonts) {
        var css = String(this.themeCssText(theme) || "") +
            String(this.fontsCssText(fonts) || "");
        return this.styledDocument(html, css === "" ? null : css);
    };
    NeutrinoWebview.deferredPageScript = function (source) {
        return "(function(){var _r=function(){\n" + String(source) + "\n};" +
            "if(document.readyState===\"loading\"){" +
            "document.addEventListener(\"DOMContentLoaded\",function(){_r();});" +
            "}else{_r();}})();";
    };
    NeutrinoWebview.titledDocument = function (html, title) {
        var text = String(html);
        var name = String(title == null ? "" : title);
        if (name === "" || /<title[\s>]/i.test(text)) {
            return text;
        }
        var head = text.indexOf("</head>");
        if (head < 0) {
            this.noteOnce("this document has no <head>, so the window keeps " +
                "its own name whatever the page calls itself");
            return text;
        }
        var escaped = name.replace(/&/g, "&amp;")
            .replace(/</g, "&lt;")
            .replace(/>/g, "&gt;");
        return text.substring(0, head) + "<title>" + escaped + "</title>" +
            text.substring(head);
    };
    NeutrinoWebview.isTrustedOrigin = function (scheme, host) {
        var s = String(scheme == null ? "" : scheme);
        var h = String(host == null ? "" : host);
        return h === "" && (s === "" || s === "file");
    };
    NeutrinoWebview.isTrustedMacSender = function (ObjCRef, message, webView) {
        try {
            var current = webView.URL;
            var currentScheme = current
                ? String(ObjCRef.unwrap(current.scheme) || "") : "";
            var currentUrl = current
                ? String(ObjCRef.unwrap(current.absoluteString) || "") : "";
            if (!this.isTrustedOrigin(currentScheme, "")) {
                this.note("refused a message from a document at " +
                    currentScheme + ":");
                return false;
            }
            if (!this.isTrustedView(currentUrl)) {
                this.note("refused a message from " +
                    (currentUrl === "" ? "a view showing nothing" : currentUrl));
                return false;
            }
        } catch (e) {
            this.note("refused a message: could not read what the view is " +
                "showing: " + e);
            return false;
        }
        var frame = null;
        try {
            frame = message.frameInfo;
            if (!frame.isMainFrame) {
                this.note("refused a message from a subframe");
                return false;
            }
        } catch (e) {
            this.note("refused a message with no frame: " + e);
            return false;
        }
        try {
            var origin = frame.securityOrigin;
            var scheme = String(ObjCRef.unwrap(origin.protocol) || "");
            var host = String(ObjCRef.unwrap(origin.host) || "");
            if (this.isTrustedOrigin(scheme, host)) {
                return true;
            }
            this.note("refused a message from " + scheme + "://" + host);
            return false;
        } catch (e) {
            this.note("refused a message whose sender's origin could not " +
                "be read: " + e);
            return false;
        }
    };
    NeutrinoWebview.parseMessage = function (raw) {
        var text = String(raw == null ? "" : raw);
        if (text.length > 4096) {
            return null;
        }
        var sep = this.messageSeparator;
        var cut = text.indexOf(sep);
        var action = (cut < 0) ? text : text.substring(0, cut);
        var rest = (cut < 0) ? null : text.substring(cut + 1);
        if (action === "close") {
            return (rest === null) ? { action: "close" } : null;
        }
        if (action === "openExternal") {
            if (rest === null || !this.isExternalUrl(rest)) {
                return null;
            }
            if (!this.mayOpenExternal(rest)) {
                this.note("refused openExternal: this build is offline");
                return null;
            }
            return { action: "openExternal", url: rest };
        }
        if (action === "resize" || action === "move") {
            if (rest === null) {
                return null;
            }
            var parts = rest.split(sep);
            if (parts.length !== 2) {
                return null;
            }
            if (action === "resize") {
                if (!this.isDimension(parts[0]) || !this.isDimension(parts[1])) {
                    return null;
                }
                return {
                    action: "resize",
                    width: parseInt(parts[0], 10),
                    height: parseInt(parts[1], 10)
                };
            }
            if (!this.isCoordinate(parts[0]) || !this.isCoordinate(parts[1])) {
                return null;
            }
            return { action: "move", x: parseInt(parts[0], 10), y: parseInt(parts[1], 10) };
        }
        if (action === "resizeBy" || action === "moveBy") {
            if (rest === null) {
                return null;
            }
            var deltas = rest.split(sep);
            if (deltas.length !== 2) {
                return null;
            }
            if (!this.isCoordinate(deltas[0]) || !this.isCoordinate(deltas[1])) {
                return null;
            }
            if (action === "resizeBy") {
                return {
                    action: "resizeBy",
                    width: parseInt(deltas[0], 10),
                    height: parseInt(deltas[1], 10)
                };
            }
            return { action: "moveBy", x: parseInt(deltas[0], 10), y: parseInt(deltas[1], 10) };
        }
        return null;
    };
    NeutrinoWebview.buildPreloadScript = function (transport, name, themeLiteral, fontsLiteral) {
        return '(function(){' +
            'var S=String.fromCharCode(31);' +
            'var _send=function(m){try{(' + transport + ')(m);}catch(_){}};' +
            'var _n=function(v){return String(v===undefined||v===null?"":v);};' +
            'var _K=["' + this.themeKeyList().join('","') + '"];' +
            'var _P=["' + String(this.systemColorNames).split(",").join('","') + '"];' +
            'var _css=function(t){' +
            'var e=document.documentElement;' +
            'if(!e||!t||!t.colors){return;}' +
            'for(var i=0;i<_K.length;i++){' +
            'try{e.style.setProperty("--neutrino-"+_P[i],t.colors[_K[i]]);}catch(_){}' +
            '}' +
            '};' +
            'var _FR=["' + this.fontRoleList().join('","') + '"];' +
            'var _FP=["' + String(this.fontCssPrefixes).split(",").join('","') + '"];' +
            'var _fontcss=function(f){' +
            'var e=document.documentElement;' +
            'if(!e||!f){return;}' +
            'for(var i=0;i<_FR.length;i++){' +
            'var r=f[_FR[i]];if(!r){continue;}' +
            'var v=[r.stack,r.size,r.weight];' +
            'for(var j=0;j<_FP.length;j++){' +
            'try{e.style.setProperty("--neutrino-"+_FP[j]+"-"+_FR[i],v[j]);}catch(_){}' +
            '}}' +
            '};' +
            'var _act=function(action,data){' +
            'var d=data||{};' +
            'if(action==="resize")_send("resize"+S+_n(d.width)+S+_n(d.height));' +
            'else if(action==="resizeBy")_send("resizeBy"+S+_n(d.width)+S+_n(d.height));' +
            'else if(action==="move")_send("move"+S+_n(d.x)+S+_n(d.y));' +
            'else if(action==="moveBy")_send("moveBy"+S+_n(d.x)+S+_n(d.y));' +
            'else if(action==="openExternal")_send("openExternal"+S+_n(d.url));' +
            'else if(action==="close")_send("close");' +
            '};' +
            'window.neutrino={' +
            'transport:"' + String(name || "unknown") + '",' +
            'theme:' + (themeLiteral || "null") + ',' +
            'fonts:' + (fontsLiteral || "null") + ',' +
            '_theme:function(t){' +
            'window.neutrino.theme=t;' +
            '_css(t);' +
            'try{window.dispatchEvent(new CustomEvent("neutrino:themechange",{detail:t}));}catch(_){}' +
            '},' +
            '_fonts:function(f){' +
            'window.neutrino.fonts=f;' +
            '_fontcss(f);' +
            'try{window.dispatchEvent(new CustomEvent("neutrino:fontchange",{detail:f}));}catch(_){}' +
            '}' +
            '};' +
            'var _def=function(n,f){try{window[n]=f;}catch(_){}};' +
            '_def("resizeTo",function(w,h){_act("resize",{width:w,height:h});});' +
            '_def("resizeBy",function(w,h){_act("resizeBy",{width:w,height:h});});' +
            '_def("moveTo",function(x,y){_act("move",{x:x,y:y});});' +
            '_def("moveBy",function(x,y){_act("moveBy",{x:x,y:y});});' +
            '_def("close",function(){_act("close");});' +
            'var _open=window.open;' +
            'var _eng=function(u,t){try{return _open.call(window,u,t);}catch(_){return null;}};' +
            '_def("open",function(url,target){' +
            'var u=(url==null)?"":String(url);' +
            'if(u===""){return null;}' +
            'var t=String(target==null?"":target).toLowerCase();' +
            'if(t==="_self"||t==="_parent"||t==="_top"){return _eng(url,target);}' +
            'if(!/^(https?|mailto):/i.test(u)){return _eng(url,target);}' +
            '_act("openExternal",{url:u});' +
            'return null;' +
            '});' +
            '})();';
    };
    NeutrinoWebview.routeMessage = function (actions, raw) {
        var msg = this.parseMessage(raw);
        if (msg && actions[msg.action]) {
            actions[msg.action](msg);
        }
    };
    NeutrinoWebview.boot = function (driver, config) {
        driver.init();
        if (driver.readTheme) {
            this.theme = this.normalizeTheme(driver.readTheme());
            if (!this.theme) {
                this.note("could not read the desktop palette; using " +
                    this.resolveBackground(null));
            }
        }
        if (driver.readFonts) {
            this.fonts = this.normalizeFonts(driver.readFonts());
            if (!this.fonts) {
                this.note("could not read the desktop fonts; the page falls " +
                    "back to the engine's own");
            }
        }
        if (driver.forceScheme) {
            try {
                driver.forceScheme(this.theme);
            } catch (e) {
                this.note("could not force the colour scheme: " + e);
            }
        }
        var scriptPath = driver.getScriptPath();
        var source = driver.readFile(scriptPath);
        var html = this.dressedDocument(
            this.titledDocument(
                this.extractHtmlDocument(source),
                config.title),
            this.theme, this.fonts);
        var pageScript = this.extractPageScript(source);
        var win = driver.createWindow(config);
        if (driver.onWebMessage) {
            var self = this;
            var driverRef = driver;
            var winRef = win;
            var actions = {};
            if (driverRef.resize) actions.resize = function (m) { try { driverRef.resize(winRef, m.width, m.height); } catch (_) {} };
            if (driverRef.move) actions.move = function (m) { try { driverRef.move(winRef, m.x, m.y); } catch (_) {} };
            if (driverRef.getBounds && driverRef.resize) actions.resizeBy = function (m) {
                try {
                    var b = driverRef.getBounds(winRef);
                    if (!b) { return; }
                    var w = b.width + m.width;
                    var h = b.height + m.height;
                    driverRef.resize(winRef, w < 1 ? 1 : w, h < 1 ? 1 : h);
                } catch (_) {}
            };
            if (driverRef.getBounds && driverRef.move) actions.moveBy = function (m) {
                try {
                    var p = driverRef.getBounds(winRef);
                    if (!p) { return; }
                    driverRef.move(winRef, p.x + m.x, p.y + m.y);
                } catch (_) {}
            };
            if (typeof driverRef["close"] === "function") actions["close"] = function (m) { try { driverRef["close"](winRef); } catch (_) {} };
            if (driverRef.openExternal) actions.openExternal = function (m) { try { driverRef.openExternal(m.url); } catch (_) {} };
            driver.onWebMessage(function (json) {
                self.routeMessage(actions, json);
            });
        }
        if (driver.webMessageTransport) {
            driver.injectPreload(null, this.buildPreloadScript(
                driver.webMessageTransport, driver.transportName,
                this.themeLiteral(this.theme),
                this.fontsLiteral(this.fonts)));
        }
        if (driver.injectPageScript) {
            driver.injectPageScript(pageScript);
        }
        var wv = driver.createWebView();
        driver.loadHTML(wv, html, scriptPath);
        driver.attachWebView(win, wv);
        driver.showWindow(win);
        driver.runEventLoop(win, wv);
    };
    NeutrinoWebview.runMacOS = function () {
        try {
            this.boot(this.createMacDriver(), this.config);
        } catch (e) {
            this.note("could not start: " + e);
            throw e;
        }
    };
    NeutrinoWebview.runGjs = function () {
        try {
            this.boot(this.createGjsDriver(), this.config);
        } catch (e) {
            if (!e || !e.neutrinoEngineUnavailable) {
                throw e;
            }
            this.note("this interpreter cannot reach GTK and WebKit2: " +
                (e.message ? e.message : e));
            eval("imports")["system"]["exit"](69);
        }
    };
    NeutrinoWebview.webview2LibDir = function () {
        return null;
    };
    NeutrinoWebview.scriptPathOverride = function () {
        return null;
    };
    NeutrinoWebview.hasWebView2Assemblies = function (SystemRef, libDir) {
        if (!libDir) {
            return false;
        }
        return SystemRef.IO.File.Exists(SystemRef.IO.Path.Combine(libDir, "Microsoft.Web.WebView2.Core.dll")) &&
            SystemRef.IO.File.Exists(SystemRef.IO.Path.Combine(libDir, "Microsoft.Web.WebView2.WinForms.dll"));
    };
    NeutrinoWebview.windowsLayoutCache = null;
    NeutrinoWebview.windowsLayout = function (SystemRef) {
        if (this.windowsLayoutCache) {
            return this.windowsLayoutCache;
        }
        var exe = SystemRef.Windows.Forms.Application.ExecutablePath;
        var name = SystemRef.IO.Path.GetFileNameWithoutExtension(exe);
        var exeDir = SystemRef.IO.Path.GetDirectoryName(exe);
        if (exeDir == null || String(exeDir) === "" ||
                name == null || String(name) === "") {
            throw new Error("neutrino: this program is not where a neutrino " +
                "launcher puts it (" + String(exe) + ")");
        }
        var beside = this.windowsScriptIn(SystemRef, exeDir, name);
        if (beside != null) {
            this.windowsLayoutCache = {
                script: beside,
                appFolder: SystemRef.IO.Path.Combine(exeDir, name)
            };
            return this.windowsLayoutCache;
        }
        var parent = SystemRef.IO.Path.GetDirectoryName(exeDir);
        if (parent != null && String(parent) !== "") {
            var above = this.windowsScriptIn(SystemRef, parent, name);
            if (above != null) {
                var leaf = String(SystemRef.IO.Path.GetFileName(exeDir));
                this.windowsLayoutCache = {
                    script: above,
                    appFolder: leaf === name + ".build"
                        ? SystemRef.IO.Path.Combine(parent, name)
                        : exeDir
                };
                return this.windowsLayoutCache;
            }
        }
        throw new Error("neutrino: could not find the document this program " +
            "was compiled from; looked for " + name + ".cmd and " + name +
            ".bat beside it in " + exeDir + " and one level above");
    };
    NeutrinoWebview.windowsScriptIn = function (SystemRef, dir, name) {
        var asCmd = SystemRef.IO.Path.Combine(dir, name + ".cmd");
        if (SystemRef.IO.File.Exists(asCmd)) {
            return asCmd;
        }
        var asBat = SystemRef.IO.Path.Combine(dir, name + ".bat");
        if (SystemRef.IO.File.Exists(asBat)) {
            return asBat;
        }
        return null;
    };
    NeutrinoWebview.findWebView2LibDir = function (SystemRef, appFolder) {
        var envLibDir = this.webview2LibDir(SystemRef);
        if (this.hasWebView2Assemblies(SystemRef, envLibDir)) {
            return envLibDir;
        }
        var directNet462 = SystemRef.IO.Path.Combine(appFolder, "Microsoft.Web.WebView2", "lib", "net462");
        if (this.hasWebView2Assemblies(SystemRef, directNet462)) {
            return directNet462;
        }
        var directNet45 = SystemRef.IO.Path.Combine(appFolder, "Microsoft.Web.WebView2", "lib", "net45");
        if (this.hasWebView2Assemblies(SystemRef, directNet45)) {
            return directNet45;
        }
        if (SystemRef.IO.Directory.Exists(appFolder)) {
            var packageDirs = SystemRef.IO.Directory.GetDirectories(appFolder, "Microsoft.Web.WebView2*");
            for (var i = 0; i < packageDirs.Length; i++) {
                var candidateNet462 = SystemRef.IO.Path.Combine(packageDirs[i], "lib", "net462");
                if (this.hasWebView2Assemblies(SystemRef, candidateNet462)) {
                    return candidateNet462;
                }
                var candidateNet45 = SystemRef.IO.Path.Combine(packageDirs[i], "lib", "net45");
                if (this.hasWebView2Assemblies(SystemRef, candidateNet45)) {
                    return candidateNet45;
                }
            }
        }
        return null;
    };
    NeutrinoWebview.prependLoaderPaths = function (SystemRef, webView2LibDir) {
        if (!webView2LibDir) {
            return;
        }
        var packageRoot = this.webView2PackageRootOf(SystemRef, webView2LibDir);
        var loaderPaths = "";
        var x86Loader = SystemRef.IO.Path.Combine(packageRoot, "runtimes", "win-x86", "native", "WebView2Loader.dll");
        if (SystemRef.IO.File.Exists(x86Loader)) {
            loaderPaths = SystemRef.IO.Path.GetDirectoryName(x86Loader) + ";" + loaderPaths;
        }
        var x64Loader = SystemRef.IO.Path.Combine(packageRoot, "runtimes", "win-x64", "native", "WebView2Loader.dll");
        if (SystemRef.IO.File.Exists(x64Loader)) {
            loaderPaths = SystemRef.IO.Path.GetDirectoryName(x64Loader) + ";" + loaderPaths;
        }
        var arm64Loader = SystemRef.IO.Path.Combine(packageRoot, "runtimes", "win-arm64", "native", "WebView2Loader.dll");
        if (SystemRef.IO.File.Exists(arm64Loader)) {
            loaderPaths = SystemRef.IO.Path.GetDirectoryName(arm64Loader) + ";" + loaderPaths;
        }
        if (loaderPaths) {
            var currentPath = SystemRef.Environment.GetEnvironmentVariable("PATH");
            if (!currentPath) {
                currentPath = "";
            }
            SystemRef.Environment.SetEnvironmentVariable("PATH", loaderPaths + currentPath);
        }
    };
    NeutrinoWebview.webView2BrowserArguments =
        "--no-first-run --disable-background-networking " +
        "--disable-component-update --disable-sync --no-pings";
    NeutrinoWebview.webView2PinnedVersion = "1.0.4129.50";
    NeutrinoWebview.webView2PinnedSha256 = "d3934f482d484b89fb4825df720c710664e1143a1e90f7b3a60794ef33f473d2";
    NeutrinoWebview.webView2Members = [
        { path: "lib/net462/Microsoft.Web.WebView2.Core.dll",
          sha256: "958efdb7f13a6d1f3079756c96956cc96cf713ae46fa085c8b1e7f44316a4f7e" },
        { path: "lib/net462/Microsoft.Web.WebView2.WinForms.dll",
          sha256: "a7b8be525030f19d9e88c6e684bca053dc7a3b080c31c3d9428f7438e7b6768f" },
        { path: "lib/net462/Microsoft.Web.WebView2.Wpf.dll",
          sha256: "217874fcb11722cf41a11c6d0483eab3f9d9c310d63486068f194614a7778a56" },
        { path: "runtimes/win-arm64/native/WebView2Loader.dll",
          sha256: "b0bfa03347a00169903c4ef0c27579dd9e85236a6dcd637a941d20b86eeec8fc" },
        { path: "runtimes/win-x64/native/WebView2Loader.dll",
          sha256: "a9a09232c25805323d4cfb3fc8f545a190a9c8a99c93262ea99d0b88df99ec90" },
        { path: "runtimes/win-x86/native/WebView2Loader.dll",
          sha256: "cbcd9a820b23aec9d68a95fb8cfd8c7d48e5bac1129faaf87aecabf4409a2ee2" }
    ];
    NeutrinoWebview.webView2PackageUrl = function () {
        var v = this.webView2PinnedVersion;
        return "https://api.nuget.org/v3-flatcontainer/microsoft.web.webview2/" +
            v + "/microsoft.web.webview2." + v + ".nupkg";
    };
    NeutrinoWebview.sha256Hex = function (SystemRef, path) {
        var hasher = SystemRef.Security.Cryptography.SHA256.Create();
        var stream = SystemRef.IO.File.OpenRead(path);
        var digest = null;
        try {
            digest = hasher.ComputeHash(stream);
        } finally {
            stream.Close();
        }
        return String(SystemRef.BitConverter.ToString(digest)).replace(/-/g, "").toLowerCase();
    };
    NeutrinoWebview.firstBadWebView2Member = function (SystemRef, packageRoot) {
        for (var i = 0; i < this.webView2Members.length; i++) {
            var member = this.webView2Members[i];
            var full = SystemRef.IO.Path.Combine(packageRoot, member.path.replace(/\//g, "\\"));
            try {
                if (!SystemRef.IO.File.Exists(full)) {
                    return member.path;
                }
                if (this.sha256Hex(SystemRef, full) !== member.sha256) {
                    return member.path;
                }
            } catch (_) {
                return member.path;
            }
        }
        return null;
    };
    NeutrinoWebview.extractWebView2Members = function (SystemRef, archivePath, destinationPath) {
        var archive = null;
        try {
            archive = SystemRef.IO.Compression.ZipFile.OpenRead(String(archivePath));
            for (var i = 0; i < this.webView2Members.length; i++) {
                var name = this.webView2Members[i].path;
                var entry = archive.GetEntry(name);
                if (entry == null) {
                    throw new Error("WebView2 package is missing " + name + ".");
                }
                var out = SystemRef.IO.Path.Combine(
                    String(destinationPath),
                    name.replace(/\//g, "\\")
                );
                var dir = SystemRef.IO.Path.GetDirectoryName(out);
                if (!SystemRef.IO.Directory.Exists(dir)) {
                    SystemRef.IO.Directory.CreateDirectory(dir);
                }
                SystemRef.IO.Compression.ZipFileExtensions.ExtractToFile(entry, out, true);
            }
        } finally {
            if (archive) {
                archive.Dispose();
            }
        }
    };
    NeutrinoWebview.deleteTree = function (SystemRef, dir) {
        var files = SystemRef.IO.Directory.GetFiles(dir);
        for (var i = 0; i < files.Length; i++) {
            SystemRef.IO.File.Delete(files[i]);
        }
        var subs = SystemRef.IO.Directory.GetDirectories(dir);
        for (var j = 0; j < subs.Length; j++) {
            var attrs = SystemRef.Convert.ToInt32(SystemRef.IO.File.GetAttributes(subs[j]));
            if ((attrs & 1024) !== 0) {
                SystemRef.IO.Directory.Delete(subs[j], false);
            } else {
                this.deleteTree(SystemRef, subs[j]);
            }
        }
        SystemRef.IO.Directory.Delete(dir, false);
    };
    NeutrinoWebview.windowsErrorSeconds = 20;
    NeutrinoWebview.showWindowsError = function (SystemRef, title, message) {
        var written = this.recordWindowsError(SystemRef, title, message);
        var shown = String(message);
        if (written) {
            shown = shown + "\n\n" + written;
        }
        this.showBoundedError(SystemRef, title, shown);
    };
    NeutrinoWebview.windowsErrorFolder = function (SystemRef) {
        try {
            return this.windowsLayout(SystemRef).appFolder;
        } catch (_) {
        }
        var exe = SystemRef.Windows.Forms.Application.ExecutablePath;
        var exeDir = SystemRef.IO.Path.GetDirectoryName(exe);
        var name = SystemRef.IO.Path.GetFileNameWithoutExtension(exe);
        var beside = SystemRef.IO.Path.Combine(exeDir, name);
        if (SystemRef.IO.Directory.Exists(beside)) {
            return beside;
        }
        return exeDir;
    };
    NeutrinoWebview.recordWindowsError = function (SystemRef, title, message) {
        var written = "";
        try {
            var path = SystemRef.IO.Path.Combine(
                this.windowsErrorFolder(SystemRef),
                "neutrino-error.log"
            );
            SystemRef.IO.File.WriteAllText(
                path,
                String(title) + "\r\n" +
                SystemRef.DateTime.UtcNow.ToString("u") + "\r\n\r\n" +
                String(message) + "\r\n"
            );
            written = path;
        } catch (_) {
        }
        try {
            SystemRef.Console.Error.WriteLine(String(title) + ": " + String(message));
            if (written) {
                SystemRef.Console.Error.WriteLine("neutrino: written to " + written);
            }
            SystemRef.Console.Error.Flush();
        } catch (_) {
        }
        return written;
    };
    NeutrinoWebview.showBoundedError = function (SystemRef, title, message) {
        try {
            var form = new SystemRef.Windows.Forms.Form();
            form.Text = String(title);
            form.FormBorderStyle = SystemRef.Windows.Forms.FormBorderStyle.FixedDialog;
            form.StartPosition = SystemRef.Windows.Forms.FormStartPosition.CenterScreen;
            form.ClientSize = new SystemRef.Drawing.Size(440, 148);
            form.MinimizeBox = false;
            form.MaximizeBox = false;
            form.TopMost = true;
            var label = new SystemRef.Windows.Forms.Label();
            label.AutoSize = false;
            label.TextAlign = SystemRef.Drawing.ContentAlignment.TopLeft;
            label.SetBounds(16, 12, 408, 120);
            label.Text = String(message);
            form.Controls.Add(label);
            form.Show();
            form.Refresh();
            SystemRef.Windows.Forms.Application.DoEvents();
            var start = SystemRef.Environment.TickCount;
            var budget = this.windowsErrorSeconds * 1000;
            while (form.Visible && (SystemRef.Environment.TickCount - start) < budget) {
                SystemRef.Windows.Forms.Application.DoEvents();
                SystemRef.Threading.Thread.Sleep(16);
            }
            form.Close();
        } catch (_) {
        }
    };
    NeutrinoWebview.downloadWebView2WithProgress = function (SystemRef, appFolder) {
        var packageRoot = SystemRef.IO.Path.Combine(appFolder, "Microsoft.Web.WebView2");
        var tempPackagePath = SystemRef.IO.Path.Combine(
            SystemRef.IO.Path.GetTempPath(),
            "Microsoft.Web.WebView2." + SystemRef.Guid.NewGuid().ToString("N") + ".zip"
        );
        var packageUrl = this.webView2PackageUrl();
        var progressForm = new SystemRef.Windows.Forms.Form();
        progressForm.Text = "Downloading WebView2 Runtime";
        progressForm.FormBorderStyle = SystemRef.Windows.Forms.FormBorderStyle.FixedDialog;
        progressForm.StartPosition = SystemRef.Windows.Forms.FormStartPosition.CenterScreen;
        progressForm.ClientSize = new SystemRef.Drawing.Size(440, 92);
        progressForm.ControlBox = false;
        progressForm.TopMost = true;
        var progressLabel = new SystemRef.Windows.Forms.Label();
        progressLabel.AutoSize = false;
        progressLabel.TextAlign = SystemRef.Drawing.ContentAlignment.MiddleLeft;
        progressLabel.SetBounds(16, 12, 408, 20);
        progressLabel.Text = "Starting download...";
        var progressBar = new SystemRef.Windows.Forms.ProgressBar();
        progressBar.SetBounds(16, 40, 408, 22);
        progressBar.Minimum = 0;
        progressBar.Maximum = 100;
        progressBar.Style = SystemRef.Windows.Forms.ProgressBarStyle.Continuous;
        progressForm.Controls.Add(progressLabel);
        progressForm.Controls.Add(progressBar);
        progressForm.Show();
        progressForm.Refresh();
        SystemRef.Windows.Forms.Application.DoEvents();
        var response = null;
        var responseStream = null;
        var fileStream = null;
        try {
            if (SystemRef.IO.Directory.Exists(packageRoot)) {
                this.deleteTree(SystemRef, packageRoot);
            }
            try {
                var tls12 = 3072;
                var tls13 = 12288;
                SystemRef.Net.ServicePointManager.SecurityProtocol = tls12 | tls13;
            } catch (_) {
                try {
                    SystemRef.Net.ServicePointManager.SecurityProtocol = 3072;
                } catch (_) {
                }
            }
            var request = SystemRef.Net.WebRequest.Create(packageUrl);
            response = request.GetResponse();
            responseStream = response.GetResponseStream();
            var totalBytes = response.ContentLength;
            if (totalBytes <= 0) {
                progressBar.Style = SystemRef.Windows.Forms.ProgressBarStyle.Marquee;
                progressLabel.Text = "Downloading package...";
            }
            fileStream = new SystemRef.IO.FileStream(tempPackagePath, SystemRef.IO.FileMode.Create, SystemRef.IO.FileAccess.Write, SystemRef.IO.FileShare.None);
            var buffer = System.Array.CreateInstance(System.Byte, 32768);
            var downloadedBytes = 0;
            var lastPercentage = -1;
            var bytesRead = 0;
            while ((bytesRead = responseStream.Read(buffer, 0, buffer.Length)) > 0) {
                fileStream.Write(buffer, 0, bytesRead);
                downloadedBytes += bytesRead;
                if (totalBytes > 0) {
                    var percentage = System.Math.Min(100, System.Convert.ToInt32((downloadedBytes * 100.0) / totalBytes));
                    if (percentage !== lastPercentage) {
                        progressBar.Value = percentage;
                        progressLabel.Text = "Downloading package... " + percentage + "%";
                        lastPercentage = percentage;
                    }
                }
                SystemRef.Windows.Forms.Application.DoEvents();
            }
            fileStream.Close();
            fileStream = null;
            responseStream.Close();
            responseStream = null;
            response.Close();
            response = null;
            if (totalBytes > 0) {
                progressBar.Value = 100;
            }
            progressLabel.Text = "Verifying package...";
            SystemRef.Windows.Forms.Application.DoEvents();
            var digest = this.sha256Hex(SystemRef, tempPackagePath);
            if (digest !== this.webView2PinnedSha256) {
                throw new Error("WebView2 package does not match its pin.\n\nexpected " +
                    this.webView2PinnedSha256 + "\ngot      " + digest);
            }
            progressLabel.Text = "Extracting package...";
            SystemRef.Windows.Forms.Application.DoEvents();
            this.extractWebView2Members(SystemRef, tempPackagePath, packageRoot);
        } catch (exDownload) {
            var message = "Download/extract failed.";
            try {
                if (exDownload && exDownload.message) {
                    message = message + "\n\n" + String(exDownload.message);
                }
            } catch (_) {
            }
            try {
                message = message + "\n\n" + String(exDownload);
            } catch (_) {
            }
            throw new Error(message);
        } finally {
            if (fileStream) {
                fileStream.Close();
            }
            if (responseStream) {
                responseStream.Close();
            }
            if (response) {
                response.Close();
            }
            if (SystemRef.IO.File.Exists(tempPackagePath)) {
                SystemRef.IO.File.Delete(tempPackagePath);
            }
            progressForm.Close();
            progressForm.Dispose();
        }
    };
    NeutrinoWebview.webView2PackageRootOf = function (SystemRef, libDir) {
        return SystemRef.IO.Path.GetFullPath(SystemRef.IO.Path.Combine(libDir, "..", ".."));
    };
    NeutrinoWebview.ensureWebView2Package = function (SystemRef, appFolder) {
        var existingLibDir = this.findWebView2LibDir(SystemRef, appFolder);
        if (existingLibDir &&
            !this.firstBadWebView2Member(SystemRef, this.webView2PackageRootOf(SystemRef, existingLibDir))) {
            return existingLibDir;
        }
        if (!SystemRef.IO.Directory.Exists(appFolder)) {
            SystemRef.IO.Directory.CreateDirectory(appFolder);
        }
        this.downloadWebView2WithProgress(SystemRef, appFolder);
        var libDir = this.findWebView2LibDir(SystemRef, appFolder);
        if (!libDir) {
            throw new Error("WebView2 package download completed but required assemblies were not found.");
        }
        var bad = this.firstBadWebView2Member(SystemRef, this.webView2PackageRootOf(SystemRef, libDir));
        if (bad) {
            throw new Error("WebView2 package member does not match its pin: " + bad);
        }
        return libDir;
    };
    NeutrinoWebview.hardenWebView2 = function (coreWv2) {
        var settings = null;
        try {
            settings = coreWv2.GetType().GetProperty("Settings").GetValue(coreWv2, null);
        } catch (_) {
            return;
        }
        if (!settings) {
            return;
        }
        var off = [
            "AreDevToolsEnabled",
            "AreDefaultContextMenusEnabled",
            "AreHostObjectsAllowed",
            "IsStatusBarEnabled",
            "IsSwipeNavigationEnabled",
            "AreBrowserAcceleratorKeysEnabled",
            "IsGeneralAutofillEnabled",
            "IsPasswordAutosaveEnabled",
            "IsPinchZoomEnabled"
        ];
        var closed = 0;
        for (var i = 0; i < off.length; i++) {
            try {
                var prop = settings.GetType().GetProperty(off[i]);
                if (prop && prop.CanWrite) {
                    prop.SetValue(settings, false, null);
                    closed++;
                }
            } catch (_) {}
        }
        this.trace("package: closed " + closed + " of " + off.length + " settings");
        if (closed < off.length) {
            this.note("this WebView2 package closed " + closed + " of " +
                off.length + " settings");
        }
    };
    NeutrinoWebview.wireWebView2Messages = function (SystemRef, coreWv2) {
        try {
            var evt = coreWv2.GetType().GetEvent("WebMessageReceived");
            if (!evt) {
                return false;
            }
            var sink = new NeutrinoWebMessageSink();
            var handler = SystemRef.Delegate.CreateDelegate(
                evt.EventHandlerType, sink, sink.GetType().GetMethod("Handle")
            );
            evt.AddEventHandler(coreWv2, handler);
            return true;
        } catch (_) {
            return false;
        }
    };
    NeutrinoWebview.wireWebView2Navigation = function (SystemRef, coreWv2) {
        var wanted = [
            ["NavigationStarting", "navstart"],
            ["NewWindowRequested", "newwindow"],
            ["ContentLoading", "commit"]
        ];
        var wired = 0;
        for (var i = 0; i < wanted.length; i++) {
            try {
                var evt = coreWv2.GetType().GetEvent(wanted[i][0]);
                if (!evt) {
                    continue;
                }
                var sink = new NeutrinoNavSink();
                sink.kind = wanted[i][1];
                evt.AddEventHandler(coreWv2, SystemRef.Delegate.CreateDelegate(
                    evt.EventHandlerType, sink, sink.GetType().GetMethod("Handle")
                ));
                wired++;
            } catch (_) {
            }
        }
        if (wired < wanted.length) {
            this.note("navigation guard wired " + wired + " of " +
                wanted.length + " events; a navigation may not be refused here");
        }
        return wired === wanted.length;
    };
    NeutrinoWebview.drainNavRefusals = function (driver) {
        while (NeutrinoNavSink.refusals.Count > 0) {
            var text = String(NeutrinoNavSink.refusals[0]);
            NeutrinoNavSink.refusals.RemoveAt(0);
            this.note(text);
        }
        while (NeutrinoNavSink.externals.Count > 0) {
            var url = String(NeutrinoNavSink.externals[0]);
            NeutrinoNavSink.externals.RemoveAt(0);
            if (driver && driver.openExternal && this.mayOpenExternal(url)) {
                try {
                    driver.openExternal(url);
                } catch (e) {
                    this.noteOnce("could not open the refused window's url: " + e);
                }
            }
        }
    };
    NeutrinoWebview.readWebView2Source = function (coreWv2, sourceProp) {
        if (!sourceProp) {
            return null;
        }
        try {
            return String(sourceProp.GetValue(coreWv2, null) || "");
        } catch (_) {
            return null;
        }
    };
    NeutrinoWebview.readWebView2Message = function (args) {
        var source = "";
        try {
            source = String(args.GetType().GetProperty("Source").GetValue(args, null) || "");
        } catch (_) {
            return null;
        }
        if (!this.isOwnDocument(source)) {
            return null;
        }
        try {
            return String(args.GetType().GetMethod("TryGetWebMessageAsString").Invoke(args, null));
        } catch (_) {
            return null;
        }
    };
    NeutrinoWebview.webView2Interfaces = {
        environment: {
            name: "ICoreWebView2Environment",
            iid: "b96d755e-0319-4e92-a296-23436f46a1fc",
            calls: {
                CreateCoreWebView2Controller: [0, "v", "pp"],
                get_BrowserVersionString:     [2, "p", ""]
            }
        },
        controller: {
            name: "ICoreWebView2Controller",
            iid: "4d00c0d1-9434-4eb6-8078-8697a560334f",
            calls: {
                put_IsVisible:     [1,  "v", "i"],
                put_Bounds:        [3,  "v", "r"],
                MoveFocus:         [9,  "v", "i"],
                NotifyParentWindowPositionChanged: [20, "v", ""],
                Close:             [21, "v", ""],
                get_CoreWebView2:  [22, "p", ""]
            }
        },
        controller2: {
            name: "ICoreWebView2Controller2",
            iid: "c979903e-d4ca-4228-92eb-47ee3fa96eab",
            calls: {
                put_DefaultBackgroundColor: [24, "v", "c"]
            }
        },
        webview: {
            name: "ICoreWebView2",
            iid: "76eceacb-0462-4d94-ac83-423a6793775e",
            calls: {
                get_Settings:                        [0,  "p", ""],
                get_Source:                          [1,  "p", ""],
                NavigateToString:                    [3,  "v", "p"],
                add_NavigationStarting:              [4,  "l", "p"],
                add_ContentLoading:                  [6,  "l", "p"],
                AddScriptToExecuteOnDocumentCreated: [24, "v", "pp"],
                ExecuteScript:                       [26, "v", "pp"],
                add_WebMessageReceived:              [31, "l", "p"],
                add_NewWindowRequested:              [41, "l", "p"],
                get_DocumentTitle:                   [45, "p", ""]
            }
        },
        settings: {
            name: "ICoreWebView2Settings",
            iid: "e562e4f0-d7fa-43ac-8d71-c05150499f00",
            calls: {
                put_IsStatusBarEnabled:           [7,  "v", "i"],
                put_AreDevToolsEnabled:           [9,  "v", "i"],
                put_AreDefaultContextMenusEnabled: [11, "v", "i"],
                put_AreHostObjectsAllowed:        [13, "v", "i"]
            }
        },
        settings3: {
            name: "ICoreWebView2Settings3",
            iid: "fdb5ab74-af33-4854-84f0-0a631deb5eba",
            calls: {
                put_AreBrowserAcceleratorKeysEnabled: [21, "v", "i"]
            }
        },
        settings4: {
            name: "ICoreWebView2Settings4",
            iid: "cb56846c-4168-4d53-b04f-03b6d6796ff2",
            calls: {
                put_IsPasswordAutosaveEnabled: [23, "v", "i"],
                put_IsGeneralAutofillEnabled:  [25, "v", "i"]
            }
        },
        settings5: {
            name: "ICoreWebView2Settings5",
            iid: "183e7052-1d03-43a0-ab99-98e043b66b39",
            calls: {
                put_IsPinchZoomEnabled: [27, "v", "i"]
            }
        },
        settings6: {
            name: "ICoreWebView2Settings6",
            iid: "11cb3acd-9bc8-43b8-83bf-f40753714f87",
            calls: {
                put_IsSwipeNavigationEnabled: [29, "v", "i"]
            }
        },
        messageArgs: {
            name: "ICoreWebView2WebMessageReceivedEventArgs",
            iid: "0f99a40c-e962-4207-9e92-e3d542eff849",
            calls: {
                get_Source:               [0, "p", ""],
                TryGetWebMessageAsString: [2, "p", ""]
            }
        },
        navigationArgs: {
            name: "ICoreWebView2NavigationStartingEventArgs",
            iid: "5b495469-e119-438a-9b18-7604f25f2e49",
            calls: {
                get_Uri:    [0, "p", ""],
                put_Cancel: [5, "v", "i"]
            }
        },
        newWindowArgs: {
            name: "ICoreWebView2NewWindowRequestedEventArgs",
            iid: "34acb11c-fc37-4418-9132-f9c21d1eafb9",
            calls: {
                get_Uri:     [0, "p", ""],
                put_Handled: [3, "v", "i"]
            }
        }
    };
    NeutrinoWebview.webView2SettingsInterfaces =
        ["settings", "settings3", "settings4", "settings5", "settings6"];
    NeutrinoWebview.webView2Handlers = {
        environmentCompleted: {
            name: "ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler",
            iid: "4e8a3389-c9d8-4bd2-b6b5-124fee6cc14d",
            args: "ip",
            target: "OnEnvironment"
        },
        controllerCompleted: {
            name: "ICoreWebView2CreateCoreWebView2ControllerCompletedHandler",
            iid: "6c4819f3-c9b7-4260-8127-c9f5bde7f68c",
            args: "ip",
            target: "OnController"
        },
        webMessageReceived: {
            name: "ICoreWebView2WebMessageReceivedEventHandler",
            iid: "57213f19-00e6-49fa-8e07-898ea01ecbd2",
            args: "pp",
            target: "OnWebMessage"
        },
        navigationStarting: {
            name: "ICoreWebView2NavigationStartingEventHandler",
            iid: "9adbe429-f36d-432b-9ddc-f8881fbd76e3",
            args: "pp",
            target: "OnNavigationStarting"
        },
        contentLoading: {
            name: "ICoreWebView2ContentLoadingEventHandler",
            iid: "364471e7-f2be-4910-bdba-d72077d51c4b",
            args: "pp",
            target: "OnContentLoading"
        },
        newWindowRequested: {
            name: "ICoreWebView2NewWindowRequestedEventHandler",
            iid: "d4c185fe-c81c-4989-97af-2d3fa7ab5651",
            args: "pp",
            target: "OnNewWindowRequested"
        },
        scriptAdded: {
            name: "ICoreWebView2AddScriptToExecuteOnDocumentCreatedCompletedHandler",
            iid: "b99369f3-9b11-47b5-bc6f-8e7895fcea17",
            args: "ip",
            target: "OnScriptAdded"
        },
        scriptRan: {
            name: "ICoreWebView2ExecuteScriptCompletedHandler",
            iid: "49511172-cc67-4bca-9923-137112f4c4cc",
            args: "ip",
            target: "OnScriptRan"
        }
    };
    NeutrinoWebview.evergreenRuntimeVersion = function (SystemRef) {
        var registry = SystemRef.Type.GetType("Microsoft.Win32.Registry");
        if (!registry) {
            return null;
        }
        var roots = [
            ["LocalMachine", "SOFTWARE\\WOW6432Node\\Microsoft\\EdgeUpdate\\Clients"],
            ["LocalMachine", "SOFTWARE\\Microsoft\\EdgeUpdate\\Clients"],
            ["CurrentUser", "SOFTWARE\\Microsoft\\EdgeUpdate\\Clients"]
        ];
        for (var i = 0; i < roots.length; i++) {
            try {
                var hiveField = registry.GetField(roots[i][0]);
                if (!hiveField) {
                    continue;
                }
                var hive = hiveField.GetValue(null);
                if (!hive) {
                    continue;
                }
                var clients = hive.OpenSubKey(roots[i][1]);
                if (!clients) {
                    continue;
                }
                var names = clients.GetSubKeyNames();
                for (var j = 0; j < names.Length; j++) {
                    var client = clients.OpenSubKey(names[j]);
                    if (!client) {
                        continue;
                    }
                    var label = String(client.GetValue("name") || "");
                    var version = String(client.GetValue("pv") || "");
                    if (label.indexOf("WebView2") < 0) {
                        continue;
                    }
                    if (/Beta|Dev|Canary/.test(label)) {
                        continue;
                    }
                    if (version === "" || version === "0.0.0.0") {
                        continue;
                    }
                    return version;
                }
            } catch (_) {
            }
        }
        return null;
    };
    NeutrinoWebview.evergreenRuntimeDir = function (SystemRef, version) {
        var roots = [
            SystemRef.Environment.GetEnvironmentVariable("ProgramFiles(x86)"),
            SystemRef.Environment.GetEnvironmentVariable("ProgramFiles"),
            SystemRef.Environment.GetEnvironmentVariable("LOCALAPPDATA")
        ];
        var fallback = null;
        for (var i = 0; i < roots.length; i++) {
            if (!roots[i]) {
                continue;
            }
            var base = SystemRef.IO.Path.Combine(String(roots[i]),
                "Microsoft\\EdgeWebView\\Application");
            if (!SystemRef.IO.Directory.Exists(base)) {
                continue;
            }
            if (version) {
                var wanted = SystemRef.IO.Path.Combine(base, String(version));
                if (SystemRef.IO.Directory.Exists(wanted)) {
                    return wanted;
                }
            }
            var dirs = SystemRef.IO.Directory.GetDirectories(base);
            for (var j = 0; j < dirs.Length; j++) {
                var leaf = String(SystemRef.IO.Path.GetFileName(dirs[j]));
                if (/^[0-9]+(\.[0-9]+)+$/.test(leaf) && !fallback) {
                    fallback = dirs[j];
                }
            }
        }
        return fallback;
    };
    NeutrinoWebview.evergreenEntryDll = function (SystemRef, runtimeDir) {
        if (!runtimeDir) {
            return null;
        }
        var arch = SystemRef.Environment.Is64BitProcess ? "x64" : "x86";
        var dll = SystemRef.IO.Path.Combine(runtimeDir,
            SystemRef.IO.Path.Combine("EBWebView", SystemRef.IO.Path.Combine(arch,
                "EmbeddedBrowserWebView.dll")));
        return SystemRef.IO.File.Exists(dll) ? dll : null;
    };
    NeutrinoWebview.evergreenEntryExport = "CreateWebViewEnvironmentWithOptionsInternal";
    NeutrinoWebview.buildEvergreenTypes = function () {
        var built = {};
        for (var key in this.webView2Interfaces) {
            if (!this.webView2Interfaces.hasOwnProperty(key)) {
                continue;
            }
            var spec = this.webView2Interfaces[key];
            NeutrinoEvergreen.DefineInterface("Neutrino_" + key, spec.iid);
            for (var name in spec.calls) {
                if (!spec.calls.hasOwnProperty(name)) {
                    continue;
                }
                var call = spec.calls[name];
                NeutrinoEvergreen.DefineSlot(call[0], name, call[1], call[2]);
            }
            built[key] = NeutrinoEvergreen.EndInterface();
        }
        NeutrinoEvergreen.messageArgsType = built.messageArgs;
        NeutrinoEvergreen.navigationArgsType = built.navigationArgs;
        NeutrinoEvergreen.newWindowArgsType = built.newWindowArgs;
        return built;
    };
    NeutrinoWebview.pumpUntil = function (SystemRef, ready, milliseconds) {
        var eager = 100;
        var left = milliseconds;
        while (left > 0) {
            if (ready()) {
                return true;
            }
            SystemRef.Windows.Forms.Application.DoEvents();
            if (eager > 0) {
                eager--;
                SystemRef.Threading.Thread.Sleep(0);
            } else {
                SystemRef.Threading.Thread.Sleep(16);
                left -= 16;
            }
        }
        return ready();
    };
    NeutrinoWebview.evergreenPlan = function (SystemRef) {
        try {
            return this.evergreenPlanUnguarded(SystemRef);
        } catch (e) {
            this.trace("evergreen: could not look for a runtime: " + e);
            return null;
        }
    };
    NeutrinoWebview.evergreenPlanUnguarded = function (SystemRef) {
        var version = this.evergreenRuntimeVersion(SystemRef);
        var runtimeDir = this.evergreenRuntimeDir(SystemRef, version);
        if (!runtimeDir) {
            this.trace("evergreen: no installed runtime found");
            return null;
        }
        var dll = this.evergreenEntryDll(SystemRef, runtimeDir);
        if (!dll) {
            this.trace("evergreen: no entry library under " + runtimeDir);
            return null;
        }
        this.trace("evergreen: runtime " + (version ? version : "unversioned") +
            " at " + runtimeDir);
        return { version: version, runtimeDir: runtimeDir, dll: dll };
    };
    NeutrinoWebview.startEvergreen = function (SystemRef, userDataDir, plan) {
        if (!plan) {
            plan = this.evergreenPlan(SystemRef);
        }
        if (!plan) {
            return null;
        }
        var dll = plan.dll;
        this.trace("evergreen: emitting types");
        if (!NeutrinoEvergreen.Begin()) {
            this.trace("evergreen: the emitter would not start");
            return null;
        }
        var types = this.buildEvergreenTypes();
        this.trace("evergreen: types emitted");
        if (!NeutrinoEvergreen.DefineEntry(
                SystemRef.IO.Path.GetFileName(dll), this.evergreenEntryExport)) {
            return null;
        }
        if (!NeutrinoEvergreen.LoadModule(dll)) {
            this.trace("evergreen: " + dll + " would not load");
            return null;
        }
        this.trace("evergreen: entry bound");
        var handler = NeutrinoEvergreen.MakeSink("NeutrinoEnvSink",
            this.webView2Handlers.environmentCompleted.iid,
            this.webView2Handlers.environmentCompleted.args,
            this.webView2Handlers.environmentCompleted.target);
        var hr = NeutrinoEvergreen.CreateEnvironment(userDataDir, handler);
        if (hr !== 0) {
            this.trace("evergreen: the entry point answered " + hr);
            return null;
        }
        var self = this;
        this.pumpUntil(SystemRef, function () {
            return NeutrinoEvergreen.environmentDone;
        }, 60000);
        if (!NeutrinoEvergreen.environmentDone) {
            this.trace("evergreen: the runtime never called back");
            return null;
        }
        if (NeutrinoEvergreen.environmentHr !== 0 ||
                String(NeutrinoEvergreen.environmentPtr) === "0") {
            this.trace("evergreen: the environment failed with " +
                NeutrinoEvergreen.environmentHr);
            return null;
        }
        var environment = NeutrinoEvergreen.Wrap(
            NeutrinoEvergreen.environmentPtr, types.environment);
        var reported = NeutrinoEvergreen.TakeString(
            types.environment.GetMethod("get_BrowserVersionString").Invoke(environment, null));
        if (!reported) {
            this.trace("evergreen: the environment would not name a version");
            return null;
        }
        this.trace("evergreen: environment up, runtime says " + reported);
        return { types: types, environment: environment, version: String(reported) };
    };
    NeutrinoWebview.managedView = function (SystemRef, wv) {
        var self = this;
        var core = null;
        var titleProp = null;
        var sourceProp = null;
        return {
            name: "package",
            ready: function () {
                if (core) {
                    return true;
                }
                if (!wv) {
                    return false;
                }
                var prop = wv.GetType().GetProperty("CoreWebView2");
                if (!prop) {
                    return false;
                }
                core = prop.GetValue(wv, null);
                if (core) {
                    titleProp = core.GetType().GetProperty("DocumentTitle");
                    sourceProp = core.GetType().GetProperty("Source");
                }
                return !!core;
            },
            harden: function () {
                self.hardenWebView2(core);
            },
            wireMessages: function () {
                return self.wireWebView2Messages(SystemRef, core);
            },
            wireNavigation: function () {
                return self.wireWebView2Navigation(SystemRef, core);
            },
            addScripts: function (sources) {
                var add = core.GetType().GetMethod("AddScriptToExecuteOnDocumentCreatedAsync");
                if (!add) {
                    return false;
                }
                for (var i = 0; i < sources.length; i++) {
                    var task = add.Invoke(core, [sources[i]]);
                    if (task) {
                        while (!task.IsCompleted) {
                            SystemRef.Windows.Forms.Application.DoEvents();
                            SystemRef.Threading.Thread.Sleep(10);
                        }
                    }
                }
                return true;
            },
            navigateToString: function (html) {
                var nav = core.GetType().GetMethod("NavigateToString");
                if (!nav) {
                    return false;
                }
                nav.Invoke(core, [html]);
                return true;
            },
            takeMessages: function () {
                var out = [];
                while (NeutrinoWebMessageSink.queue.Count > 0) {
                    var queued = NeutrinoWebMessageSink.queue[0];
                    NeutrinoWebMessageSink.queue.RemoveAt(0);
                    var text = self.readWebView2Message(queued);
                    if (text !== null) {
                        out.push(text);
                    }
                }
                return out;
            },
            documentTitle: function () {
                if (!titleProp) {
                    return "";
                }
                return String(titleProp.GetValue(core, null) || "");
            },
            source: function () {
                return self.readWebView2Source(core, sourceProp);
            },
            evaluate: function (js) {
                var run = core.GetType().GetMethod("ExecuteScriptAsync");
                if (!run) {
                    return false;
                }
                run.Invoke(core, [js]);
                return true;
            },
            paint: function (color) {
                return self.paintWindowsView(wv, color);
            },
            syncBounds: function () {},
            close: function () {}
        };
    };
    NeutrinoWebview.evergreenAskForController = function (SystemRef, session, win) {
        if (session.controllerAsked) {
            return;
        }
        session.controllerAsked = true;
        var handler = NeutrinoEvergreen.MakeSink("NeutrinoCtlSink",
            this.webView2Handlers.controllerCompleted.iid,
            this.webView2Handlers.controllerCompleted.args,
            this.webView2Handlers.controllerCompleted.target);
        session.types.environment.GetMethod("CreateCoreWebView2Controller")
            .Invoke(session.environment, [win.Handle, handler]);
        this.trace("evergreen: controller asked for");
    };
    NeutrinoWebview.evergreenView = function (SystemRef, session, win) {
        var self = this;
        var types = session.types;
        var lastWidth = -1;
        var lastHeight = -1;
        var lastX = -100000;
        var lastY = -100000;
        var focused = false;
        this.evergreenAskForController(SystemRef, session, win);
        this.pumpUntil(SystemRef, function () {
            return NeutrinoEvergreen.controllerDone;
        }, 60000);
        if (!NeutrinoEvergreen.controllerDone || NeutrinoEvergreen.controllerHr !== 0) {
            this.trace("evergreen: no controller (" + NeutrinoEvergreen.controllerHr + ")");
            return null;
        }
        this.trace("evergreen  controller up");
        var controller = NeutrinoEvergreen.Wrap(NeutrinoEvergreen.controllerPtr, types.controller);
        if (!controller) {
            return null;
        }
        var core = NeutrinoEvergreen.Wrap(
            types.controller.GetMethod("get_CoreWebView2").Invoke(controller, null),
            types.webview);
        if (!core) {
            this.trace("evergreen: the controller has no webview");
            return null;
        }
        var controller2 = null;
        try {
            controller2 = NeutrinoEvergreen.Wrap(NeutrinoEvergreen.controllerPtr, types.controller2);
        } catch (e) {
            this.noteOnce("this WebView2 runtime cannot be told what to paint " +
                "before the document arrives: " + e);
        }
        var scriptAddedSink = NeutrinoEvergreen.MakeSink("NeutrinoAddSink",
            this.webView2Handlers.scriptAdded.iid,
            this.webView2Handlers.scriptAdded.args,
            this.webView2Handlers.scriptAdded.target);
        var scriptRanSink = NeutrinoEvergreen.MakeSink("NeutrinoRanSink",
            this.webView2Handlers.scriptRan.iid,
            this.webView2Handlers.scriptRan.args,
            this.webView2Handlers.scriptRan.target);
        var withString = function (text, run) {
            var held = NeutrinoEvergreen.GiveString(String(text));
            try {
                return run(held);
            } finally {
                NeutrinoEvergreen.DropString(held);
            }
        };
        var subscribe = function (slot, which) {
            try {
                var sink = NeutrinoEvergreen.MakeSink("NeutrinoSink_" + which,
                    self.webView2Handlers[which].iid,
                    self.webView2Handlers[which].args,
                    self.webView2Handlers[which].target);
                types.webview.GetMethod(slot).Invoke(core, [sink]);
                return true;
            } catch (e) {
                self.note("could not subscribe to " + slot + ": " + e);
                return false;
            }
        };
        return {
            name: "evergreen",
            ready: function () {
                return true;
            },
            harden: function () {
                var handed = types.webview.GetMethod("get_Settings").Invoke(core, null);
                var baseSettings = NeutrinoEvergreen.Wrap(handed, types.settings);
                if (!baseSettings) {
                    self.note("the view would not hand over its settings");
                    return;
                }
                var no = SystemRef.Convert.ToInt32(0);
                var names = self.webView2SettingsInterfaces;
                var closed = 0;
                var wanted = 0;
                for (var i = 0; i < names.length; i++) {
                    var spec = self.webView2Interfaces[names[i]];
                    var revision = null;
                    try {
                        revision = NeutrinoEvergreen.Wrap(handed, types[names[i]]);
                    } catch (e) {
                        revision = null;
                    }
                    for (var name in spec.calls) {
                        if (!spec.calls.hasOwnProperty(name)) {
                            continue;
                        }
                        wanted++;
                        if (!revision) {
                            continue;
                        }
                        try {
                            types[names[i]].GetMethod(name).Invoke(revision, [no]);
                            closed++;
                        } catch (_) {}
                    }
                }
                self.trace("evergreen: closed " + closed + " of " + wanted + " settings");
                if (closed < wanted) {
                    self.note("this WebView2 runtime closed " + closed + " of " +
                        wanted + " settings; the rest are on interface revisions " +
                        "it does not offer");
                }
            },
            wireMessages: function () {
                return subscribe("add_WebMessageReceived", "webMessageReceived");
            },
            wireNavigation: function () {
                var wired = 0;
                if (subscribe("add_NavigationStarting", "navigationStarting")) { wired++; }
                if (subscribe("add_ContentLoading", "contentLoading")) { wired++; }
                if (subscribe("add_NewWindowRequested", "newWindowRequested")) { wired++; }
                if (wired < 3) {
                    self.note("navigation guard wired " + wired + " of 3 events; " +
                        "a navigation may not be refused here");
                }
                return wired === 3;
            },
            addScripts: function (sources) {
                var method = types.webview.GetMethod("AddScriptToExecuteOnDocumentCreated");
                for (var i = 0; i < sources.length; i++) {
                    withString(sources[i], function (held) {
                        method.Invoke(core, [held, scriptAddedSink]);
                    });
                }
                return true;
            },
            navigateToString: function (html) {
                var method = types.webview.GetMethod("NavigateToString");
                withString(html, function (held) {
                    method.Invoke(core, [held]);
                });
                return true;
            },
            takeMessages: function () {
                var out = [];
                while (NeutrinoEvergreen.messages.Count > 0) {
                    var from = String(NeutrinoEvergreen.messageSources[0]);
                    var text = String(NeutrinoEvergreen.messages[0]);
                    NeutrinoEvergreen.messageSources.RemoveAt(0);
                    NeutrinoEvergreen.messages.RemoveAt(0);
                    if (self.isOwnDocument(from)) {
                        out.push(text);
                    }
                }
                return out;
            },
            documentTitle: function () {
                try {
                    return String(NeutrinoEvergreen.TakeString(
                        types.webview.GetMethod("get_DocumentTitle").Invoke(core, null)));
                } catch (_) {
                    return "";
                }
            },
            source: function () {
                try {
                    return String(NeutrinoEvergreen.TakeString(
                        types.webview.GetMethod("get_Source").Invoke(core, null)));
                } catch (_) {
                    return null;
                }
            },
            evaluate: function (js) {
                var method = types.webview.GetMethod("ExecuteScript");
                withString(js, function (held) {
                    method.Invoke(core, [held, scriptRanSink]);
                });
                return true;
            },
            paint: function (color) {
                if (!controller2 || !color) {
                    return false;
                }
                try {
                    types.controller2.GetMethod("put_DefaultBackgroundColor").Invoke(
                        controller2,
                        [NeutrinoEvergreen.MakeColour(color.R, color.G, color.B)]);
                    return true;
                } catch (e) {
                    self.noteOnce("could not paint the view: " + e);
                    return false;
                }
            },
            syncBounds: function () {
                var width = SystemRef.Convert.ToInt32(win.ClientSize.Width);
                var height = SystemRef.Convert.ToInt32(win.ClientSize.Height);
                if (width !== lastWidth || height !== lastHeight) {
                    lastWidth = width;
                    lastHeight = height;
                    try {
                        types.controller.GetMethod("put_Bounds")
                            .Invoke(controller, [NeutrinoEvergreen.MakeRect(width, height)]);
                        types.controller.GetMethod("put_IsVisible")
                            .Invoke(controller, [SystemRef.Convert.ToInt32(1)]);
                    } catch (e) {
                        self.noteOnce("could not size the view: " + e);
                    }
                }
                var x = SystemRef.Convert.ToInt32(win.Location.X);
                var y = SystemRef.Convert.ToInt32(win.Location.Y);
                if (x !== lastX || y !== lastY) {
                    lastX = x;
                    lastY = y;
                    try {
                        types.controller.GetMethod("NotifyParentWindowPositionChanged")
                            .Invoke(controller, null);
                    } catch (e) {
                        self.noteOnce("could not tell the view its window moved: " + e);
                    }
                }
                if (!focused) {
                    focused = true;
                    try {
                        types.controller.GetMethod("MoveFocus")
                            .Invoke(controller, [SystemRef.Convert.ToInt32(0)]);
                    } catch (e) {
                        self.noteOnce("could not give the view the keyboard: " + e);
                    }
                }
            },
            close: function () {
                try {
                    types.controller.GetMethod("Close").Invoke(controller, null);
                } catch (_) {}
            }
        };
    };
    NeutrinoWebview.createWindowsDriver = function () {
        var SystemRef = eval("System");
        var appFolder = null, userDataDir = null;
        var self = this;
        var messageCallback = null;
        var lastDocTitle = "";
        var pendingPreload = null;
        var pendingPageScript = null;
        var pendingDocument = null;
        var settingsApplied = false;
        var webMessagesWired = false;
        var view = null;
        var evergreen = null;
        var evergreenPlan = null;
        var viewClosed = false;
        var preparePackage = function () {
            var folder = self.windowsLayout(SystemRef).appFolder;
            var webView2LibDir = self.ensureWebView2Package(SystemRef, folder);
            if (!webView2LibDir) {
                return false;
            }
            SystemRef.Environment.SetEnvironmentVariable("NEUTRINO_WEBVIEW2_LIB_DIR", webView2LibDir);
            self.prependLoaderPaths(SystemRef, webView2LibDir);
            SystemRef.Reflection.Assembly.LoadFrom(SystemRef.IO.Path.Combine(webView2LibDir, "Microsoft.Web.WebView2.Core.dll"));
            var winFormsAssembly = SystemRef.Reflection.Assembly.LoadFrom(SystemRef.IO.Path.Combine(webView2LibDir, "Microsoft.Web.WebView2.WinForms.dll"));
            var winFormsType = winFormsAssembly.GetType("Microsoft.Web.WebView2.WinForms.WebView2");
            if (!winFormsType) {
                throw new Error("Could not load Microsoft.Web.WebView2.WinForms.WebView2 type.");
            }
            self.windowsWebViewPackage = { assembly: winFormsAssembly, type: winFormsType };
            self.trace("init: assemblies loaded from " + webView2LibDir);
            return true;
        };
        var closeView = function () {
            if (viewClosed || !view) {
                return;
            }
            viewClosed = true;
            try {
                view.close();
            } catch (_) {}
        };
        return {
            webMessageTransport: "function(m){document.title='__NEUTRINO__'+encodeURIComponent(m);}",
            transportName: "title",
            init: function () {
                SystemRef.Windows.Forms.Application.EnableVisualStyles();
                SystemRef.Windows.Forms.Application.SetCompatibleTextRenderingDefault(false);
                appFolder = self.windowsLayout(SystemRef).appFolder;
                if (!SystemRef.IO.Directory.Exists(appFolder)) {
                    SystemRef.IO.Directory.CreateDirectory(appFolder);
                }
                userDataDir = SystemRef.IO.Path.Combine(appFolder, "data");
                self.installWindowsTrace(SystemRef, appFolder);
                self.trace("init: app folder " + appFolder);
                SystemRef.Environment.SetEnvironmentVariable(
                    "WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS",
                    self.webView2BrowserArguments);
                self.trace("init: browser arguments set");
                evergreenPlan = self.evergreenPlan(SystemRef);
                if (evergreenPlan) {
                    self.trace("init: evergreen runtime " +
                        (evergreenPlan.version ? evergreenPlan.version : "unversioned") +
                        " available, nothing to download");
                    return;
                }
                if (!preparePackage()) {
                    SystemRef.Environment.Exit(1);
                    return;
                }
            },
            readFile: function (path) {
                return SystemRef.IO.File.ReadAllText(path);
            },
            getScriptPath: function () {
                var fromEnv = self.scriptPathOverride(SystemRef);
                if (fromEnv) {
                    return fromEnv;
                }
                return self.windowsLayout(SystemRef).script;
            },
            readTheme: function () {
                return self.readWindowsTheme(SystemRef);
            },
            readFonts: function () {
                return self.readWindowsFonts(SystemRef);
            },
            repaint: function (win, wv, background) {
                var color = self.makeWindowsColor(SystemRef, background);
                if (!color) {
                    return;
                }
                try {
                    win.BackColor = color;
                } catch (e) {
                    self.note("could not repaint the window: " + e);
                }
                if (view) {
                    view.paint(color);
                } else if (wv) {
                    self.paintWindowsView(wv, color);
                }
            },
            evaluate: function (wv, js) {
                if (!view || !NeutrinoNavSink.armed) {
                    return;
                }
                view.evaluate(js);
            },
            createWindow: function (config) {
                self.trace("window  building the frame");
                var win = new SystemRef.Windows.Forms.Form();
                win.Text = config.title;
                if (self.undecorated()) {
                    win.FormBorderStyle = SystemRef.Windows.Forms.FormBorderStyle.None;
                }
                win.ClientSize = new SystemRef.Drawing.Size(config.width, config.height);
                win.StartPosition = SystemRef.Windows.Forms.FormStartPosition.CenterScreen;
                var winColor = self.makeWindowsColor(SystemRef, self.resolveBackground(self.theme));
                if (winColor) {
                    try { win.BackColor = winColor; } catch (e) { self.note("could not paint the window: " + e); }
                }
                self.trace("window  frame built");
                return win;
            },
            createWebView: function (forcePackage) {
                if (evergreenPlan && !forcePackage) {
                    return { evergreen: true };
                }
                var pkg = self.windowsWebViewPackage;
                if (!pkg) {
                    throw new Error("neutrino: the WebView2 package was not prepared");
                }
                var wv = SystemRef.Activator.CreateInstance(pkg.type);
                self.paintWindowsView(wv,
                    self.makeWindowsColor(SystemRef, self.resolveBackground(self.theme)));
                if (userDataDir) {
                    try {
                        var cpType = pkg.assembly.GetType("Microsoft.Web.WebView2.WinForms.CoreWebView2CreationProperties");
                        if (cpType) {
                            var cp = SystemRef.Activator.CreateInstance(cpType);
                            try {
                                cp.UserDataFolder = userDataDir;
                                SystemRef.IO.Directory.CreateDirectory(cp.UserDataFolder);
                                wv.CreationProperties = cp;
                            } catch (_) {}
                        }
                    } catch (_) {}
                }
                wv.Dock = SystemRef.Windows.Forms.DockStyle.Fill;
                return wv;
            },
            attachWebView: function (win, wv) {
                if (evergreenPlan) {
                    evergreen = self.startEvergreen(SystemRef, userDataDir, evergreenPlan);
                    if (evergreen) {
                        self.evergreenAskForController(SystemRef, evergreen, win);
                    }
                }
                win.Show();
                SystemRef.Windows.Forms.Application.DoEvents();
                self.trace("attach  window on screen");
                if (evergreenPlan) {
                    if (!evergreen) {
                        evergreenPlan = null;
                        self.trace("attach: the runtime would not start; " +
                            "falling back to the package");
                        if (!preparePackage()) {
                            throw new Error("neutrino: the installed WebView2 " +
                                "runtime would not start and the package could " +
                                "not be fetched");
                        }
                        var made = this.createWebView(true);
                        made.Source = new SystemRef.Uri("about:blank");
                        view = self.managedView(SystemRef, made);
                        win.Controls.Add(made);
                        return;
                    }
                }
                if (evergreen) {
                    view = self.evergreenView(SystemRef, evergreen, win);
                    if (!view) {
                        throw new Error("neutrino: the installed WebView2 runtime " +
                            "started but would not give this window a view");
                    }
                    view.paint(self.makeWindowsColor(SystemRef,
                        self.resolveBackground(self.theme)));
                    view.syncBounds();
                    return;
                }
                view = self.managedView(SystemRef, wv);
                win.Controls.Add(wv);
            },
            loadHTML: function (wv, html) {
                pendingDocument = html;
                wv.Source = new SystemRef.Uri("about:blank");
            },
            setTitle: function (win, title) {
                win.Text = title;
                self.trace("title -> " + title);
            },
            resize: function (win, w, h) {
                win.ClientSize = new SystemRef.Drawing.Size(parseInt(w), parseInt(h));
            },
            move: function (win, x, y) {
                win.Location = new SystemRef.Drawing.Point(parseInt(x), parseInt(y));
            },
            getBounds: function (win) {
                return {
                    width: SystemRef.Convert.ToInt32(win.ClientSize.Width),
                    height: SystemRef.Convert.ToInt32(win.ClientSize.Height),
                    x: SystemRef.Convert.ToInt32(win.Location.X),
                    y: SystemRef.Convert.ToInt32(win.Location.Y)
                };
            },
            openExternal: function (url) {
                if (!self.mayOpenExternal(url)) {
                    return;
                }
                var info = new SystemRef.Diagnostics.ProcessStartInfo(String(url));
                info.UseShellExecute = true;
                SystemRef.Diagnostics.Process.Start(info);
            },
            showWindow: function () {},
            "close": function (win) {
                closeView();
                win.Close();
            },
            onWebMessage: function (cb) {
                messageCallback = cb;
            },
            injectPreload: function (wv, js) {
                pendingPreload = js;
            },
            injectPageScript: function (js) {
                pendingPageScript = js;
            },
            runEventLoop: function (win, wv) {
                win.Show();
                self.trace("loop: window shown");
                var driver = this;
                var coreReady = false;
                var preloadInjected = false;
                var trustedRemembered = false;
                var spins = 0;
                var beats = 0;
                var loopExNoted = false;
                while (win.Visible) {
                    SystemRef.Windows.Forms.Application.DoEvents();
                    try {
                        if (!coreReady) {
                            coreReady = view.ready();
                        }
                        if (coreReady && !settingsApplied) {
                            settingsApplied = true;
                            self.trace("loop: " + view.name + " view ready after " +
                                spins + " turns");
                            view.harden();
                            webMessagesWired = view.wireMessages();
                            view.wireNavigation();
                            pendingPreload = self.buildPreloadScript(
                                webMessagesWired
                                    ? "function(m){window.chrome.webview.postMessage(m);}"
                                    : "function(m){document.title='__NEUTRINO__'+encodeURIComponent(m);}",
                                webMessagesWired ? "webmessage" : "title",
                                self.themeLiteral(self.theme),
                            self.fontsLiteral(self.fonts)
                            );
                        }
                        if (coreReady && pendingPreload && !preloadInjected) {
                            preloadInjected = true;
                            view.addScripts(pendingPageScript
                                ? [pendingPreload,
                                   self.deferredPageScript(pendingPageScript)]
                                : [pendingPreload]);
                            if (pendingDocument) {
                                NeutrinoNavSink.navIssued = true;
                                self.trace("loop: navigating to the app document");
                                view.navigateToString(pendingDocument);
                            } else {
                                self.note("the view was given no document; the window will stay blank");
                            }
                        }
                        if (coreReady) {
                            self.drainNavRefusals(driver);
                            view.syncBounds();
                        }
                        if (spins % 60 === 0) {
                            self.applyTheme(driver, win, wv, driver.readTheme());
                        }
                        if (spins % 60 === 30) {
                            self.applyFonts(driver, wv, driver.readFonts());
                        }
                        if (coreReady && messageCallback && webMessagesWired) {
                            var arrived = view.takeMessages();
                            for (var mi = 0; mi < arrived.length; mi++) {
                                messageCallback(arrived[mi]);
                            }
                        }
                        if (coreReady && !viewClosed) {
                            if (!trustedRemembered && NeutrinoNavSink.armed) {
                                var committed = view.source();
                                if (committed !== null && committed !== "") {
                                    trustedRemembered = true;
                                    self.rememberTrustedView(committed);
                                }
                            }
                            if (trustedRemembered) {
                                var docTitle = view.documentTitle();
                                if (docTitle !== lastDocTitle) {
                                    var showing = view.source();
                                    if (docTitle.indexOf(self.recordPrefix) === 0) {
                                        lastDocTitle = docTitle;
                                        var mine = (showing !== null) && self.isOwnDocument(showing);
                                        if (!messageCallback || webMessagesWired) {
                                            self.trace("a record arrived in the title and the channel is " +
                                                (webMessagesWired ? "webmessage" : "unwired"));
                                        } else if (mine) {
                                            try {
                                                messageCallback(decodeURIComponent(
                                                    docTitle.substring(self.recordPrefix.length)));
                                            } catch (_) {}
                                        } else {
                                            self.note("refused a record in the title of " +
                                                (showing === null ? "a view that did not say" : showing));
                                        }
                                    } else {
                                        var name = self.acceptDocumentTitle(
                                            showing === null ? "" : showing, docTitle);
                                        if (name !== null) {
                                            lastDocTitle = docTitle;
                                            driver.setTitle(win, name);
                                        }
                                    }
                                }
                            }
                        }
                    } catch (loopEx) {
                        if (!loopExNoted) {
                            loopExNoted = true;
                            try {
                                self.trace("loop: raised " + String(loopEx));
                            } catch (_) {}
                        }
                    }
                    SystemRef.Threading.Thread.Sleep(16);
                    spins++;
                    if (!coreReady && beats < 40 && spins % 300 === 0) {
                        beats++;
                        try {
                            self.trace("loop: still no view after " +
                                spins + " turns");
                        } catch (_) {}
                    }
                }
                closeView();
            },
            handleError: function (ex) {
                var message = "";
                try {
                    if (ex && ex.message) { message = String(ex.message); }
                } catch (_) {}
                var detail;
                if (message.indexOf("neutrino:") === 0) {
                    detail = message;
                } else {
                    detail = "Failed to initialize WebView2 package/download.";
                    if (message) { detail = detail + "\n\n" + message; }
                }
                self.showWindowsError(SystemRef, "neutrino", detail);
                SystemRef.Environment.Exit(1);
            }
        };
    };
    NeutrinoWebview.runWindows = function () {
        var driver = this.createWindowsDriver();
        try {
            this.boot(driver, this.config);
        } catch (ex) {
            driver.handleError(ex);
        }
    };

    /*@cc_on
        @if (@_jscript_version >= 7)
            import System;
            import System.IO;
            import System.Collections;
            import System.Drawing;
            import System.Windows.Forms;
            import System.Reflection;
            import System.Reflection.Emit;
            import System.Runtime.InteropServices;
            class NeutrinoWebMessageSink {
                static var queue : ArrayList = new ArrayList();
                function Handle(sender : Object, args : Object) : void {
                    NeutrinoWebMessageSink.queue.Add(args);
                }
            }
            class NeutrinoNavSink {
                static var navIssued : boolean = false;
                static var ownDocument : String = "";
                static var armed : boolean = false;
                static var refusals : ArrayList = new ArrayList();
                static var externals : ArrayList = new ArrayList();
                var kind : String;
                function Handle(sender : Object, args : Object) : void {
                    if (this.kind == "commit") {
                        if (NeutrinoNavSink.ownDocument != "") {
                            NeutrinoNavSink.armed = true;
                        }
                        return;
                    }
                    var going : String = NeutrinoNavSink.uriOf(args);
                    if (this.kind == "navstart" && !NeutrinoNavSink.armed) {
                        if (NeutrinoNavSink.navIssued && NeutrinoNavSink.ownDocument == "") {
                            NeutrinoNavSink.ownDocument = NeutrinoNavSink.identity(going);
                        }
                        return;
                    }
                    if (!NeutrinoNavSink.armed) {
                        return;
                    }
                    if (this.kind == "newwindow") {
                        if (NeutrinoNavSink.setTrue(args, "Handled")) {
                            NeutrinoNavSink.refusals.Add("refused a new window for " +
                                NeutrinoNavSink.shorten(going));
                            NeutrinoNavSink.externals.Add(going);
                        } else {
                            NeutrinoNavSink.refusals.Add("could not refuse a new window for " +
                                NeutrinoNavSink.shorten(going));
                        }
                        return;
                    }
                    if (NeutrinoNavSink.isOwn(going)) {
                        return;
                    }
                    if (NeutrinoNavSink.setTrue(args, "Cancel")) {
                        NeutrinoNavSink.refusals.Add("refused navigation to " +
                            NeutrinoNavSink.shorten(going));
                    } else {
                        NeutrinoNavSink.refusals.Add("could not refuse navigation to " +
                            NeutrinoNavSink.shorten(going));
                    }
                }
                static function identity(uri : String) : String {
                    var cut : int = uri.IndexOf("#");
                    return cut < 0 ? uri : uri.Substring(0, cut);
                }
                static function isOwn(uri : String) : boolean {
                    var id : String = NeutrinoNavSink.identity(uri);
                    if (id == "" || id == "about:blank") {
                        return true;
                    }
                    return NeutrinoNavSink.ownDocument != "" &&
                        id == NeutrinoNavSink.ownDocument;
                }
                static function uriOf(args : Object) : String {
                    try {
                        var p : PropertyInfo = args.GetType().GetProperty("Uri");
                        if (p == null) {
                            return "";
                        }
                        var v : Object = p.GetValue(args, null);
                        return v == null ? "" : String(v);
                    } catch (e) {
                        return "";
                    }
                }
                static function shorten(uri : String) : String {
                    return uri.Length > 120 ? uri.Substring(0, 120) + "..." : uri;
                }
                static function setTrue(args : Object, name : String) : boolean {
                    try {
                        var p : PropertyInfo = args.GetType().GetProperty(name);
                        if (p == null || !p.CanWrite) {
                            return false;
                        }
                        p.SetValue(args, true, null);
                        return true;
                    } catch (e) {
                        return false;
                    }
                }
            }
            class NeutrinoEvergreen {
                static var module : ModuleBuilder = null;
                static var selfType : Type = null;
                static var rectType : Type = null;
                static var colourType : Type = null;
                static var entryType : Type = null;
                static var sinks : ArrayList = new ArrayList();
                static var messageArgsType : Type = null;
                static var navigationArgsType : Type = null;
                static var newWindowArgsType : Type = null;
                static var environmentPtr : IntPtr = IntPtr.Zero;
                static var environmentHr : int = 0;
                static var environmentDone : boolean = false;
                static var controllerPtr : IntPtr = IntPtr.Zero;
                static var controllerHr : int = 0;
                static var controllerDone : boolean = false;
                static var messages : ArrayList = new ArrayList();
                static var messageSources : ArrayList = new ArrayList();
                static var faults : ArrayList = new ArrayList();
                static var building : TypeBuilder = null;
                static var slotNames : ArrayList = new ArrayList();
                static var slotReturns : ArrayList = new ArrayList();
                static var slotArgs : ArrayList = new ArrayList();
                static function Self() : Type {
                    if (NeutrinoEvergreen.selfType == null) {
                        NeutrinoEvergreen.selfType = (new NeutrinoEvergreen()).GetType();
                    }
                    return NeutrinoEvergreen.selfType;
                }
                static function TypeFor(code : String) : Type {
                    if (code == "i") {
                        return Type.GetType("System.Int32");
                    }
                    if (code == "l") {
                        return Type.GetType("System.Int64");
                    }
                    if (code == "r") {
                        return NeutrinoEvergreen.rectType;
                    }
                    if (code == "c") {
                        return NeutrinoEvergreen.colourType;
                    }
                    return Type.GetType("System.IntPtr");
                }
                static function TypesFor(codes : String) : Type[] {
                    var types : Type[] = new Type[codes.Length];
                    for (var i : int = 0; i < codes.Length; i++) {
                        types[i] = NeutrinoEvergreen.TypeFor(codes.Substring(i, 1));
                    }
                    return types;
                }
                static function ReturnFor(code : String) : Type {
                    if (code == "v") {
                        return null;
                    }
                    return NeutrinoEvergreen.TypeFor(code);
                }
                static function Begin() : boolean {
                    if (NeutrinoEvergreen.module != null) {
                        return true;
                    }
                    try {
                        var name : AssemblyName = new AssemblyName("NeutrinoEvergreen");
                        var built : AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(
                            name, AssemblyBuilderAccess.Run);
                        NeutrinoEvergreen.module = built.DefineDynamicModule("m");
                        var rect : TypeBuilder = NeutrinoEvergreen.module.DefineType("RECT",
                            TypeAttributes.Public | TypeAttributes.SequentialLayout | TypeAttributes.Sealed,
                            Type.GetType("System.ValueType"));
                        var intType : Type = Type.GetType("System.Int32");
                        rect.DefineField("left", intType, FieldAttributes.Public);
                        rect.DefineField("top", intType, FieldAttributes.Public);
                        rect.DefineField("right", intType, FieldAttributes.Public);
                        rect.DefineField("bottom", intType, FieldAttributes.Public);
                        NeutrinoEvergreen.rectType = rect.CreateType();
                        var byteType : Type = Type.GetType("System.Byte");
                        var colour : TypeBuilder = NeutrinoEvergreen.module.DefineType("COLOR",
                            TypeAttributes.Public | TypeAttributes.SequentialLayout | TypeAttributes.Sealed,
                            Type.GetType("System.ValueType"));
                        colour.DefineField("A", byteType, FieldAttributes.Public);
                        colour.DefineField("R", byteType, FieldAttributes.Public);
                        colour.DefineField("G", byteType, FieldAttributes.Public);
                        colour.DefineField("B", byteType, FieldAttributes.Public);
                        NeutrinoEvergreen.colourType = colour.CreateType();
                        return true;
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("could not start the emitter: " + e);
                        return false;
                    }
                }
                static function NewInterface(name : String, iid : String) : TypeBuilder {
                    var tb : TypeBuilder = NeutrinoEvergreen.module.DefineType(name,
                        TypeAttributes.Public | TypeAttributes.Interface |
                        TypeAttributes.Abstract | TypeAttributes.Import);
                    var guidType : Type = Type.GetType("System.Runtime.InteropServices.GuidAttribute");
                    var guidArgs : Type[] = [Type.GetType("System.String")];
                    var guidValue : Object[] = [iid];
                    tb.SetCustomAttribute(new CustomAttributeBuilder(
                        guidType.GetConstructor(guidArgs), guidValue));
                    var itfType : Type = Type.GetType("System.Runtime.InteropServices.InterfaceTypeAttribute");
                    var itfArgs : Type[] = [Type.GetType("System.Runtime.InteropServices.ComInterfaceType")];
                    var itfValue : Object[] = [ComInterfaceType.InterfaceIsIUnknown];
                    tb.SetCustomAttribute(new CustomAttributeBuilder(
                        itfType.GetConstructor(itfArgs), itfValue));
                    return tb;
                }
                static function AbstractAttributes() : MethodAttributes {
                    return MethodAttributes.Public | MethodAttributes.Virtual |
                        MethodAttributes.Abstract | MethodAttributes.HideBySig |
                        MethodAttributes.NewSlot;
                }
                static function DefineInterface(name : String, iid : String) : void {
                    NeutrinoEvergreen.building = NeutrinoEvergreen.NewInterface(name, iid);
                    NeutrinoEvergreen.slotNames.Clear();
                    NeutrinoEvergreen.slotReturns.Clear();
                    NeutrinoEvergreen.slotArgs.Clear();
                }
                static function DefineSlot(index : int, name : String, ret : String, args : String) : void {
                    while (NeutrinoEvergreen.slotNames.Count <= index) {
                        NeutrinoEvergreen.slotNames.Add(null);
                        NeutrinoEvergreen.slotReturns.Add(null);
                        NeutrinoEvergreen.slotArgs.Add(null);
                    }
                    NeutrinoEvergreen.slotNames[index] = name;
                    NeutrinoEvergreen.slotReturns[index] = ret;
                    NeutrinoEvergreen.slotArgs[index] = args;
                }
                static function EndInterface() : Type {
                    var placeholder : Type[] = [
                        Type.GetType("System.IntPtr"), Type.GetType("System.IntPtr"),
                        Type.GetType("System.IntPtr"), Type.GetType("System.IntPtr")
                    ];
                    for (var i : int = 0; i < NeutrinoEvergreen.slotNames.Count; i++) {
                        if (NeutrinoEvergreen.slotNames[i] == null) {
                            NeutrinoEvergreen.building.DefineMethod("slot" + i,
                                NeutrinoEvergreen.AbstractAttributes(), null, placeholder);
                        } else {
                            NeutrinoEvergreen.building.DefineMethod(
                                String(NeutrinoEvergreen.slotNames[i]),
                                NeutrinoEvergreen.AbstractAttributes(),
                                NeutrinoEvergreen.ReturnFor(String(NeutrinoEvergreen.slotReturns[i])),
                                NeutrinoEvergreen.TypesFor(String(NeutrinoEvergreen.slotArgs[i])));
                        }
                    }
                    var made : Type = NeutrinoEvergreen.building.CreateType();
                    NeutrinoEvergreen.building = null;
                    return made;
                }
                static function MakeSink(name : String, iid : String, args : String,
                                         target : String) : IntPtr {
                    var argTypes : Type[] = NeutrinoEvergreen.TypesFor(args);
                    var itb : TypeBuilder = NeutrinoEvergreen.NewInterface(name, iid);
                    itb.DefineMethod("Invoke", NeutrinoEvergreen.AbstractAttributes(), null, argTypes);
                    var itype : Type = itb.CreateType();
                    var ctb : TypeBuilder = NeutrinoEvergreen.module.DefineType(name + "Impl",
                        TypeAttributes.Public);
                    ctb.AddInterfaceImplementation(itype);
                    var impl : MethodBuilder = ctb.DefineMethod("Invoke",
                        MethodAttributes.Public | MethodAttributes.Virtual |
                        MethodAttributes.HideBySig | MethodAttributes.NewSlot |
                        MethodAttributes.Final,
                        null, argTypes);
                    var il : ILGenerator = impl.GetILGenerator();
                    il.Emit(OpCodes.Ldarg_1);
                    il.Emit(OpCodes.Ldarg_2);
                    il.Emit(OpCodes.Call, NeutrinoEvergreen.Self().GetMethod(target));
                    il.Emit(OpCodes.Ret);
                    ctb.DefineMethodOverride(impl, itype.GetMethod("Invoke"));
                    var instance : Object = Activator.CreateInstance(ctb.CreateType());
                    NeutrinoEvergreen.sinks.Add(instance);
                    return Marshal.GetComInterfaceForObject(instance, itype);
                }
                static function DefineEntry(dll : String, entry : String) : boolean {
                    try {
                        var tb : TypeBuilder = NeutrinoEvergreen.module.DefineType("NeutrinoNative",
                            TypeAttributes.Public);
                        var pinvoke : MethodAttributes = MethodAttributes.Public |
                            MethodAttributes.Static | MethodAttributes.PinvokeImpl;
                        var ptr : Type = Type.GetType("System.IntPtr");
                        var loadArgs : Type[] = [ptr];
                        var load : MethodBuilder = tb.DefinePInvokeMethod("LoadLibraryW",
                            "kernel32.dll", "LoadLibraryW", pinvoke, CallingConventions.Standard,
                            ptr, loadArgs, CallingConvention.Winapi, CharSet.Auto);
                        load.SetImplementationFlags(
                            load.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig);
                        var createArgs : Type[] = [
                            Type.GetType("System.Boolean"), ptr, ptr, ptr, ptr
                        ];
                        var create : MethodBuilder = tb.DefinePInvokeMethod("CreateEnvironment",
                            dll, entry, pinvoke, CallingConventions.Standard,
                            Type.GetType("System.Int32"), createArgs,
                            CallingConvention.Winapi, CharSet.Auto);
                        create.SetImplementationFlags(
                            create.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig);
                        NeutrinoEvergreen.entryType = tb.CreateType();
                        return true;
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("could not build the entry point: " + e);
                        return false;
                    }
                }
                static function LoadModule(path : String) : boolean {
                    var held : IntPtr = Marshal.StringToCoTaskMemUni(path);
                    try {
                        var args : Object[] = [held];
                        var got : Object = NeutrinoEvergreen.entryType.GetMethod("LoadLibraryW")
                            .Invoke(null, args);
                        return String(got) != "0";
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("could not load " + path + ": " + e);
                        return false;
                    } finally {
                        Marshal.FreeCoTaskMem(held);
                    }
                }
                static function CreateEnvironment(userDataFolder : String, handler : IntPtr) : int {
                    var folder : IntPtr = Marshal.StringToCoTaskMemUni(userDataFolder);
                    try {
                        var args : Object[] = [true, IntPtr.Zero, folder, IntPtr.Zero, handler];
                        var hr : int = NeutrinoEvergreen.entryType.GetMethod("CreateEnvironment")
                            .Invoke(null, args);
                        return hr;
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("the entry point would not be called: " + e);
                        return -1;
                    } finally {
                        Marshal.FreeCoTaskMem(folder);
                    }
                }
                static function TakeString(p : IntPtr) : String {
                    if (p == IntPtr.Zero) {
                        return "";
                    }
                    var s : String = Marshal.PtrToStringUni(p);
                    Marshal.FreeCoTaskMem(p);
                    return s;
                }
                static function GiveString(s : String) : IntPtr {
                    return Marshal.StringToCoTaskMemUni(s);
                }
                static function DropString(p : IntPtr) : void {
                    if (p != IntPtr.Zero) {
                        Marshal.FreeCoTaskMem(p);
                    }
                }
                static function Wrap(p : IntPtr, itf : Type) : Object {
                    if (p == IntPtr.Zero || itf == null) {
                        return null;
                    }
                    return Marshal.GetTypedObjectForIUnknown(p, itf);
                }
                static function MakeColour(r : int, g : int, b : int) : Object {
                    var made : Object = Activator.CreateInstance(NeutrinoEvergreen.colourType);
                    NeutrinoEvergreen.colourType.GetField("A").SetValue(made, Convert.ToByte(255));
                    NeutrinoEvergreen.colourType.GetField("R").SetValue(made, Convert.ToByte(r));
                    NeutrinoEvergreen.colourType.GetField("G").SetValue(made, Convert.ToByte(g));
                    NeutrinoEvergreen.colourType.GetField("B").SetValue(made, Convert.ToByte(b));
                    return made;
                }
                static function MakeRect(w : int, h : int) : Object {
                    var rect : Object = Activator.CreateInstance(NeutrinoEvergreen.rectType);
                    var zero : int = 0;
                    NeutrinoEvergreen.rectType.GetField("left").SetValue(rect, zero);
                    NeutrinoEvergreen.rectType.GetField("top").SetValue(rect, zero);
                    NeutrinoEvergreen.rectType.GetField("right").SetValue(rect, w);
                    NeutrinoEvergreen.rectType.GetField("bottom").SetValue(rect, h);
                    return rect;
                }
                static function OnEnvironment(hr : int, env : IntPtr) : void {
                    NeutrinoEvergreen.environmentHr = hr;
                    if (env != IntPtr.Zero) {
                        Marshal.AddRef(env);
                    }
                    NeutrinoEvergreen.environmentPtr = env;
                    NeutrinoEvergreen.environmentDone = true;
                }
                static function OnController(hr : int, controller : IntPtr) : void {
                    NeutrinoEvergreen.controllerHr = hr;
                    if (controller != IntPtr.Zero) {
                        Marshal.AddRef(controller);
                    }
                    NeutrinoEvergreen.controllerPtr = controller;
                    NeutrinoEvergreen.controllerDone = true;
                }
                static function OnWebMessage(sender : IntPtr, args : IntPtr) : void {
                    try {
                        var o : Object = NeutrinoEvergreen.Wrap(args, NeutrinoEvergreen.messageArgsType);
                        if (o == null) {
                            return;
                        }
                        var from : String = NeutrinoEvergreen.TakeString(
                            NeutrinoEvergreen.messageArgsType.GetMethod("get_Source").Invoke(o, null));
                        var text : String = NeutrinoEvergreen.TakeString(
                            NeutrinoEvergreen.messageArgsType
                                .GetMethod("TryGetWebMessageAsString").Invoke(o, null));
                        NeutrinoEvergreen.messageSources.Add(from);
                        NeutrinoEvergreen.messages.Add(text);
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("a page message could not be read: " + e);
                    }
                }
                static var scriptAddedHr : int = 0;
                static function OnScriptAdded(hr : int, id : IntPtr) : void {
                    NeutrinoEvergreen.scriptAddedHr = hr;
                }
                static var scriptRanHr : int = 0;
                static function OnScriptRan(hr : int, result : IntPtr) : void {
                    NeutrinoEvergreen.scriptRanHr = hr;
                }
                static function OnNavigationStarting(sender : IntPtr, args : IntPtr) : void {
                    try {
                        var o : Object = NeutrinoEvergreen.Wrap(args, NeutrinoEvergreen.navigationArgsType);
                        if (o == null) {
                            return;
                        }
                        var going : String = NeutrinoEvergreen.TakeString(
                            NeutrinoEvergreen.navigationArgsType.GetMethod("get_Uri").Invoke(o, null));
                        if (!NeutrinoNavSink.armed) {
                            if (NeutrinoNavSink.navIssued && NeutrinoNavSink.ownDocument == "") {
                                NeutrinoNavSink.ownDocument = NeutrinoNavSink.identity(going);
                            }
                            return;
                        }
                        if (NeutrinoNavSink.isOwn(going)) {
                            return;
                        }
                        var one : int = 1;
                        var yes : Object[] = [one];
                        NeutrinoEvergreen.navigationArgsType.GetMethod("put_Cancel").Invoke(o, yes);
                        NeutrinoNavSink.refusals.Add("refused navigation to " +
                            NeutrinoNavSink.shorten(going));
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("a navigation could not be judged: " + e);
                    }
                }
                static function OnContentLoading(sender : IntPtr, args : IntPtr) : void {
                    if (NeutrinoNavSink.ownDocument != "") {
                        NeutrinoNavSink.armed = true;
                    }
                }
                static function OnNewWindowRequested(sender : IntPtr, args : IntPtr) : void {
                    try {
                        var o : Object = NeutrinoEvergreen.Wrap(args, NeutrinoEvergreen.newWindowArgsType);
                        if (o == null) {
                            return;
                        }
                        var going : String = NeutrinoEvergreen.TakeString(
                            NeutrinoEvergreen.newWindowArgsType.GetMethod("get_Uri").Invoke(o, null));
                        if (!NeutrinoNavSink.armed) {
                            return;
                        }
                        var one : int = 1;
                        var yes : Object[] = [one];
                        NeutrinoEvergreen.newWindowArgsType.GetMethod("put_Handled").Invoke(o, yes);
                        NeutrinoNavSink.refusals.Add("refused a new window for " +
                            NeutrinoNavSink.shorten(going));
                        NeutrinoNavSink.externals.Add(going);
                    } catch (e) {
                        NeutrinoEvergreen.faults.Add("a new window could not be refused: " + e);
                    }
                }
            }
            NeutrinoWebview.runEngine = function () {
                this.runWindows();
            };
        @else @*/

    NeutrinoWebview.runEngine = function () {
        if (typeof ObjC !== "undefined" && typeof $ !== "undefined") {
            this.runMacOS();
            return;
        }
        if (typeof imports !== "undefined" && imports.gi) {
            this.runGjs();
            return;
        }
        if (typeof NeutrinoQml !== "undefined") {
            return;
        }
        if (typeof NeutrinoPy !== "undefined") {
            return;
        }
        if (typeof window !== "undefined" && typeof document !== "undefined") {
            this.runWeb();
            return;
        }
        throw new Error("Unsupported JS runtime for webview.js");
    };
    NeutrinoWebview.noteOut = function (line) {
        if (typeof printerr !== "undefined") {
            printerr(line);
            return;
        }
        if (typeof console === "undefined") {
            return;
        }
        if (console.warn) {
            console.warn(line);
            return;
        }
        if (console.log) {
            console.log(line);
        }
    };
    NeutrinoWebview.gtkColorNames = "theme_bg_color,theme_fg_color,theme_base_color,theme_text_color," +
        "theme_selected_bg_color,theme_selected_fg_color,borders";
    NeutrinoWebview.readGtkTheme = function (styleContext) {
        if (!styleContext) {
            return null;
        }
        var keys = this.themeKeyList();
        var names = String(this.gtkColorNames).split(",");
        var hex = [];
        var alpha = [];
        for (var i = 0; i < names.length; i++) {
            var found = null;
            try {
                found = styleContext.lookup_color(names[i]);
            } catch (e) {
                this.noteOnce("could not look up " + names[i] + ": " + e);
                return null;
            }
            if (!found || !found[0] || !found[1]) {
                this.noteOnce("this theme defines no " + names[i]);
                return null;
            }
            hex[i] = this.toHex(found[1]);
            alpha[i] = found[1].alpha;
        }
        var raw = { source: "gtk" };
        for (var j = 0; j < keys.length; j++) {
            raw[keys[j]] = this.flattenColor(hex[j], alpha[j], hex[0]);
        }
        return raw;
    };
    NeutrinoWebview.gtkPreferDark = function (theme) {
        return !!theme && theme.scheme === "dark";
    };
    NeutrinoWebview.paintGtkWindow = function (Gtk, Gdk, win, background) {
        var rgb = this.parseColor(background);
        if (!rgb || !Gtk || !Gdk) {
            return false;
        }
        var css = "window, .background { background-color: rgb(" +
            Math.round(rgb.red * 255) + "," +
            Math.round(rgb.green * 255) + "," +
            Math.round(rgb.blue * 255) + "); }";
        try {
            var provider = new Gtk.CssProvider();
            provider.load_from_data(css);
            win.get_style_context().add_provider(
                provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION);
            return true;
        } catch (e) {
            this.note("could not paint the window: " + e);
            return false;
        }
    };
    NeutrinoWebview.paintWebKitView = function (Gdk, wv, background) {
        var rgb = this.parseColor(background);
        if (!rgb || !Gdk) {
            return false;
        }
        try {
            var rgba = new Gdk.RGBA();
            rgba.red = rgb.red;
            rgba.green = rgb.green;
            rgba.blue = rgb.blue;
            rgba.alpha = 1;
            wv.set_background_color(rgba);
            return true;
        } catch (e) {
            this.note("could not paint the view: " + e);
            return false;
        }
    };
    NeutrinoWebview.gtkFontSchemas = "org.gnome.desktop.interface," +
        "org.cinnamon.desktop.interface,org.mate.interface";
    NeutrinoWebview.gtkFontWmSchemas = "org.gnome.desktop.wm.preferences," +
        "org.cinnamon.desktop.wm.preferences";
    NeutrinoWebview.gtkFontKeys = "font-name,document-font-name,monospace-font-name";
    NeutrinoWebview.gtkFontWmKeys = "titlebar-font,titlebar-uses-system-font";
    NeutrinoWebview.gtkFontAliases = "Sans:sans-serif,Serif:serif,Monospace:monospace," +
        "System-ui:system-ui";
    NeutrinoWebview.gtkFontSchemaChoice = function (uiString, names) {
        var list = String(this.gtkFontSchemas).split(",");
        var i;
        for (i = 0; i < list.length; i++) {
            if (names[list[i]] !== undefined && names[list[i]] === uiString) {
                return list[i];
            }
        }
        for (i = 0; i < list.length; i++) {
            if (names[list[i]] !== undefined) {
                this.noteOnce("no GSettings schema agrees with GtkSettings' " +
                    "font-name; the other roles come from " + list[i]);
                return list[i];
            }
        }
        return "";
    };
    NeutrinoWebview.gtkChooseFontStrings = function (gathered) {
        var chosen = gathered.interface || {};
        var out = {
            ui: String(gathered.gtkFontName || ""),
            document: String(chosen["document-font-name"] || ""),
            monospace: String(chosen["monospace-font-name"] || ""),
            titlebar: ""
        };
        if (!gathered.titlebarSystem) {
            out.titlebar = String(gathered.titlebar || "");
        }
        return out;
    };
    NeutrinoWebview.gtkRoleFields = function (role, parsed) {
        var out = { family: "", generic: "", size: "", weight: "" };
        if (!parsed) {
            return out;
        }
        var family = String(parsed.family || "");
        var alias = this.fontLookup(this.gtkFontAliases, family);
        if (alias !== "") {
            out.generic = alias;
        } else if (family !== "") {
            out.family = family;
        }
        if (parsed.size > 0) {
            out.size = parsed.size;
        }
        if (parsed.weight > 0) {
            out.weight = parsed.weight;
        }
        return out;
    };
    NeutrinoWebview.gtkFontsFromParsed = function (parsedByRole) {
        var roles = this.fontRoleList();
        var raw = { source: "gtk", unit: "pt" };
        for (var i = 0; i < roles.length; i++) {
            var got = parsedByRole[roles[i]];
            if (!got) {
                continue;
            }
            raw[roles[i] + "Family"] = got.family;
            raw[roles[i] + "Generic"] = got.generic;
            raw[roles[i] + "Size"] = got.size;
            raw[roles[i] + "Weight"] = got.weight;
        }
        return raw;
    };
    NeutrinoWebview.macColorNames = "windowBackgroundColor," +
        "labelColor|controlTextColor," +
        "textBackgroundColor," +
        "textColor," +
        "controlAccentColor|selectedContentBackgroundColor|alternateSelectedControlColor," +
        "alternateSelectedControlTextColor|selectedMenuItemTextColor," +
        "separatorColor|gridColor";
    NeutrinoWebview.readMacColor = function (dollar, names) {
        var list = String(names).split("|");
        for (var i = 0; i < list.length; i++) {
            try {
                var color = dollar.NSColor[list[i]];
                if (!color) {
                    continue;
                }
                var srgb = color.colorUsingColorSpace(dollar.NSColorSpace.sRGBColorSpace);
                if (!srgb) {
                    continue;
                }
                return {
                    hex: this.toHex({
                        red: srgb.redComponent,
                        green: srgb.greenComponent,
                        blue: srgb.blueComponent
                    }),
                    alpha: srgb.alphaComponent
                };
            } catch (_) {}
        }
        return null;
    };
    NeutrinoWebview.readMacTheme = function (ObjCRef, dollar) {
        try {
            var style = dollar.NSUserDefaults.standardUserDefaults
                .stringForKey("AppleInterfaceStyle");
            var wantsDark = String(ObjCRef.unwrap(style) || "").indexOf("Dark") === 0;
            var appearance = dollar.NSAppearance.appearanceNamed(
                wantsDark ? "NSAppearanceNameDarkAqua" : "NSAppearanceNameAqua");
            if (appearance) {
                dollar.NSAppearance.setCurrentAppearance(appearance);
            }
        } catch (e) {
            this.noteOnce("could not set the drawing appearance: " + e);
        }
        var keys = this.themeKeyList();
        var names = String(this.macColorNames).split(",");
        var found = [];
        for (var i = 0; i < names.length; i++) {
            found[i] = this.readMacColor(dollar, names[i]);
            if (!found[i]) {
                this.noteOnce("no NSColor answered to " + names[i]);
                return null;
            }
        }
        var raw = { source: "macos" };
        for (var j = 0; j < keys.length; j++) {
            raw[keys[j]] = this.flattenColor(found[j].hex, found[j].alpha, found[0].hex);
        }
        return raw;
    };
    NeutrinoWebview.paintMacWindow = function (win, background) {
        var dollar = $;
        var rgb = this.parseColor(background);
        if (!rgb) {
            return false;
        }
        try {
            win.backgroundColor = dollar.NSColor.colorWithSRGBRedGreenBlueAlpha(
                rgb.red, rgb.green, rgb.blue, 1.0);
            return true;
        } catch (e) {
            this.note("could not paint the window: " + e);
            return false;
        }
    };
    NeutrinoWebview.paintMacView = function (wv, background) {
        var dollar = $;
        var rgb = this.parseColor(background);
        if (!rgb) {
            return false;
        }
        var painted = false;
        try {
            wv.underPageBackgroundColor = dollar.NSColor.colorWithSRGBRedGreenBlueAlpha(
                rgb.red, rgb.green, rgb.blue, 1.0);
            painted = true;
        } catch (_) {}
        try {
            wv.setValueForKey(false, "drawsBackground");
            painted = true;
        } catch (_) {}
        if (!painted) {
            this.note("could not paint the view; it will show its own background");
        }
        return painted;
    };
    NeutrinoWebview.macFontRoles = "ui:systemFontOfSize,document:userFontOfSize," +
        "monospace:userFixedPitchFontOfSize,titlebar:titleBarFontOfSize," +
        "small:smallSystemFont";
    NeutrinoWebview.macWeightSteps = "0:100,1:100,2:200,3:300,4:350,5:400," +
        "6:500,7:600,8:600,9:700,10:800,11:800,12:900,13:900,14:900,15:900";
    NeutrinoWebview.macFontUnit = "px";
    NeutrinoWebview.readMacFont = function (ObjCRef, dollar, font) {
        if (!font) {
            return null;
        }
        var out = { family: "", generic: "", size: "", weight: "" };
        try {
            var family = String(ObjCRef.unwrap(font.familyName) || "");
            if (family.charAt(0) === ".") {
                out.generic = "system-ui";
            } else if (family !== "") {
                out.family = family;
            }
            out.size = font.pointSize;
        } catch (e) {
            return null;
        }
        try {
            var step = dollar.NSFontManager.sharedFontManager.weightOfFont(font);
            var mapped = this.fontLookup(this.macWeightSteps, String(Math.round(step)));
            if (mapped !== "") {
                out.weight = mapped;
            }
        } catch (_) {}
        return out;
    };
    NeutrinoWebview.readMacFonts = function (ObjCRef, dollar) {
        var pairs = String(this.macFontRoles).split(",");
        var raw = { source: "macos", unit: this.macFontUnit };
        var found = 0;
        for (var i = 0; i < pairs.length; i++) {
            var cut = pairs[i].indexOf(":");
            var role = pairs[i].substring(0, cut);
            var name = pairs[i].substring(cut + 1);
            var font = null;
            try {
                font = (name === "smallSystemFont")
                    ? dollar.NSFont.systemFontOfSize(dollar.NSFont.smallSystemFontSize)
                    : dollar.NSFont[name](0);
            } catch (e) {
                this.noteOnce("no NSFont answered to " + name + ": " + e);
                continue;
            }
            var got = this.readMacFont(ObjCRef, dollar, font);
            if (!got) {
                this.noteOnce("could not read the " + role + " font");
                continue;
            }
            found++;
            raw[role + "Family"] = got.family;
            raw[role + "Generic"] = got.generic;
            raw[role + "Size"] = got.size;
            raw[role + "Weight"] = got.weight;
        }
        if (!found) {
            return null;
        }
        return raw;
    };
    NeutrinoWebview.writeMacStatus = function () {
    };
    NeutrinoWebview.createGjsDriver = function () {
        var importsRef = imports;
        var Gtk, WebKit2, GLib, ByteArray, Gdk, Gio, Pango;
        var fontSettings = [];
        var self = this;
        var messageCallback = null;
        var pendingPreload = null;
        var pendingPageScript = null;
        var documentLoaded = false;
        var windowRef = null;
        var wv = null;
        var openSettings = function (id) {
            try {
                var source = Gio.SettingsSchemaSource.get_default();
                if (!source || !source.lookup(id, true)) {
                    return null;
                }
                var settings = new Gio.Settings({ schema_id: id });
                fontSettings[fontSettings.length] = settings;
                return settings;
            } catch (e) {
                self.noteOnce("could not open " + id + ": " + e);
                return null;
            }
        };
        var settingString = function (settings, key) {
            if (!settings || settings.list_keys().indexOf(key) < 0) {
                return "";
            }
            return String(settings.get_string(key) || "");
        };
        var settingBool = function (settings, key) {
            if (!settings || settings.list_keys().indexOf(key) < 0) {
                return false;
            }
            return !!settings.get_boolean(key);
        };
        var gatherFontStrings = function () {
            var gathered = { gtkFontName: "", names: {}, interface: {},
                             titlebar: "", titlebarSystem: false };
            var settings = Gtk.Settings.get_default();
            if (settings) {
                gathered.gtkFontName = String(settings.gtk_font_name || "");
            }
            var ids = String(self.gtkFontSchemas).split(",");
            var opened = {};
            var i;
            for (i = 0; i < ids.length; i++) {
                opened[ids[i]] = openSettings(ids[i]);
                var name = settingString(opened[ids[i]], "font-name");
                if (name !== "") {
                    gathered.names[ids[i]] = name;
                }
            }
            var chose = self.gtkFontSchemaChoice(gathered.gtkFontName, gathered.names);
            if (chose !== "" && opened[chose]) {
                var keys = String(self.gtkFontKeys).split(",");
                for (i = 0; i < keys.length; i++) {
                    gathered.interface[keys[i]] = settingString(opened[chose], keys[i]);
                }
            }
            var wmIds = String(self.gtkFontWmSchemas).split(",");
            for (i = 0; i < wmIds.length; i++) {
                var wm = openSettings(wmIds[i]);
                if (!wm) {
                    continue;
                }
                var titlebar = settingString(wm, "titlebar-font");
                if (titlebar === "") {
                    continue;
                }
                gathered.titlebar = titlebar;
                gathered.titlebarSystem = settingBool(wm, "titlebar-uses-system-font");
                break;
            }
            return gathered;
        };
        var parseFontStrings = function (chosen) {
            var roles = self.fontRoleList();
            var out = {};
            for (var i = 0; i < roles.length; i++) {
                var text = chosen[roles[i]];
                if (!text) {
                    continue;
                }
                var desc = Pango.FontDescription.from_string(String(text));
                var size = desc.get_size() / Pango.SCALE;
                if (desc.get_size_is_absolute()) {
                    size = size * 72 / 96;
                }
                out[roles[i]] = self.gtkRoleFields(roles[i], {
                    family: desc.get_family(),
                    size: size,
                    weight: desc.get_weight()
                });
            }
            return out;
        };
        return {
            webMessageTransport: "window.webkit.messageHandlers.neutrino.postMessage",
            transportName: "scriptmessage",
            init: function () {
                try {
                    importsRef["gi"]["versions"]["Gtk"] = "3.0";
                    importsRef["gi"]["versions"]["WebKit2"] = self.resolveLinuxWebKitVersion();
                    Gtk = importsRef["gi"]["Gtk"];
                    WebKit2 = importsRef["gi"]["WebKit2"];
                    GLib = importsRef["gi"]["GLib"];
                    ByteArray = importsRef["byteArray"];
                    importsRef["gi"]["versions"]["Gdk"] = "3.0";
                    Gdk = importsRef["gi"]["Gdk"];
                    importsRef["gi"]["versions"]["Pango"] = "1.0";
                    Gio = importsRef["gi"]["Gio"];
                    Pango = importsRef["gi"]["Pango"];
                } catch (e) {
                    if (e && e.neutrinoEngineUnavailable) {
                        throw e;
                    }
                    throw self.engineUnavailable(String(e && e.message ? e.message : e));
                }
                Gtk.init(null);
                if (GLib.getenv("NEUTRINO_WEBKIT_SANDBOX") === "1") {
                    try {
                        WebKit2.WebContext.get_default().set_sandbox_enabled(true);
                    } catch (e) {
                        self.note("webkit sandbox unavailable: " + e);
                    }
                } else {
                    self.note("webkit sandbox off: this system refused a user namespace");
                }
            },
            readFile: function (path) {
                var result = GLib.file_get_contents(path);
                if (!result[0]) {
                    throw new Error("Could not read local document: " + path);
                }
                return ByteArray.toString(result[1]);
            },
            getScriptPath: function () {
                return self.getLinuxScriptPath(importsRef);
            },
            readTheme: function () {
                try {
                    return self.readGtkTheme(new Gtk.Box().get_style_context());
                } catch (e) {
                    self.noteOnce("could not read the desktop theme: " + e);
                    return null;
                }
            },
            readFonts: function () {
                try {
                    var gathered = gatherFontStrings();
                    var chosen = self.gtkChooseFontStrings(gathered);
                    return self.gtkFontsFromParsed(parseFontStrings(chosen));
                } catch (e) {
                    self.noteOnce("could not read the desktop fonts: " + e);
                    return null;
                }
            },
            createWindow: function (config) {
                var win = new Gtk.Window({
                    title: config.title,
                    default_width: config.width,
                    default_height: config.height,
                    decorated: !self.undecorated()
                });
                windowRef = win;
                win.set_position(Gtk.WindowPosition.CENTER);
                win.connect("destroy", function () { Gtk.main_quit(); });
                self.paintGtkWindow(Gtk, Gdk, win, self.resolveBackground(self.theme));
                return win;
            },
            forceScheme: function (theme) {
                if (!self.gtkPreferDark(theme)) {
                    return;
                }
                var settings = Gtk.Settings.get_default();
                if (!settings) {
                    return;
                }
                if (settings.gtk_application_prefer_dark_theme) {
                    return;
                }
                settings.gtk_application_prefer_dark_theme = true;
            },
            repaint: function (win, wv, background) {
                self.paintGtkWindow(Gtk, Gdk, win, background);
                if (wv) {
                    self.paintWebKitView(Gdk, wv, background);
                }
            },
            evaluate: function (wv, js) {
                if (!documentLoaded) {
                    return;
                }
                wv.run_javascript(js, null, null);
            },
            createWebView: function () {
                var ucm = new WebKit2.UserContentManager();
                var titleWatcher = function (view) {
                    if (!windowRef) {
                        return;
                    }
                    var showing = "";
                    try {
                        showing = String(view.get_uri());
                    } catch (_) {
                        showing = "";
                    }
                    var name = self.acceptDocumentTitle(showing, view.get_title());
                    if (name !== null) {
                        windowRef.set_title(name);
                    }
                };
                if (messageCallback) {
                    ucm.register_script_message_handler("neutrino");
                    ucm.connect("script-message-received::neutrino", function (_, result) {
                        var showing = "";
                        try {
                            showing = String(wv.get_uri());
                        } catch (_) {
                            showing = "";
                        }
                        if (!self.isTrustedView(showing)) {
                            self.note("refused a message from " + showing);
                            return;
                        }
                        messageCallback(result.get_js_value().to_string());
                    });
                }
                var inject = function (source, when) {
                    if (!source) {
                        return;
                    }
                    try {
                        ucm.add_script(WebKit2.UserScript["new"](
                            source,
                            WebKit2.UserContentInjectedFrames.TOP_FRAME,
                            when,
                            null,
                            null
                        ));
                    } catch (e) {
                        self.note("could not inject: " + e);
                    }
                };
                inject(pendingPreload, WebKit2.UserScriptInjectionTime.START);
                inject(pendingPageScript, WebKit2.UserScriptInjectionTime.END);
                wv = new WebKit2.WebView({ user_content_manager: ucm });
                self.paintWebKitView(Gdk, wv, self.resolveBackground(self.theme));
                wv.connect("load-changed", function (_, loadEvent) {
                    if (loadEvent === WebKit2.LoadEvent.COMMITTED) {
                        documentLoaded = true;
                        try {
                            self.rememberTrustedView(wv.get_uri());
                        } catch (_) {}
                    }
                });
                var settings = wv.get_settings();
                try {
                    settings.set_enable_developer_extras(false);
                    settings.set_allow_file_access_from_file_urls(false);
                    settings.set_allow_universal_access_from_file_urls(false);
                    settings.set_javascript_can_access_clipboard(false);
                    settings.set_enable_write_console_messages_to_stdout(false);
                } catch (_) {}
                var driverRef = this;
                wv.connect("decide-policy", function (_, decision, decisionType) {
                    var types = WebKit2.PolicyDecisionType;
                    if (decisionType !== types.NAVIGATION_ACTION &&
                        decisionType !== types.NEW_WINDOW_ACTION) {
                        return false;
                    }
                    var uri = "";
                    try {
                        uri = String(decision.get_navigation_action().get_request().get_uri());
                    } catch (_) {
                        uri = "";
                    }
                    if (!documentLoaded || self.isOwnDocument(uri)) {
                        return false;
                    }
                    decision.ignore();
                    self.note("refused navigation to " + uri);
                    driverRef.openExternal(uri);
                    return true;
                });
                wv.connect("notify::title", titleWatcher);
                return wv;
            },
            resize: function (win, w, h) {
                win.resize(w, h);
            },
            move: function (win, x, y) {
                win.move(x, y);
            },
            getBounds: function (win) {
                var size = win.get_size();
                var pos = win.get_position();
                return { width: size[0], height: size[1], x: pos[0], y: pos[1] };
            },
            openExternal: function (url) {
                if (!self.mayOpenExternal(url)) {
                    return;
                }
                try {
                    var Gio = importsRef["gi"]["Gio"];
                    Gio.AppInfo.launch_default_for_uri(String(url), null);
                } catch (_) {
                    try {
                        GLib.spawn_async(
                            null,
                            ["xdg-open", String(url)],
                            null,
                            GLib.SpawnFlags.SEARCH_PATH,
                            null
                        );
                    } catch (_) {}
                }
            },
            "close": function (win) {
                win.destroy();
            },
            onWebMessage: function (cb) {
                messageCallback = cb;
            },
            injectPreload: function (_, js) {
                pendingPreload = js;
            },
            injectPageScript: function (js) {
                pendingPageScript = js;
            },
            attachWebView: function (win, wv) {
                win.add(wv);
            },
            loadHTML: function (wv, html) {
                wv.load_html(html, null);
            },
            showWindow: function (win) {
                win.show_all();
            },
            runEventLoop: function (win, wv) {
                var driver = this;
                try {
                    win.connect("style-updated", function () {
                        self.applyTheme(driver, win, wv, driver.readTheme());
                        self.applyFonts(driver, wv, driver.readFonts());
                    });
                } catch (e) {
                    self.note("no theme watcher on this lane: " + e);
                }
                try {
                    var settings = Gtk.Settings.get_default();
                    if (settings) {
                        settings.connect("notify::gtk-theme-name", function () {
                            self.applyTheme(driver, win, wv, driver.readTheme());
                        });
                        settings.connect("notify::gtk-font-name", function () {
                            self.applyFonts(driver, wv, driver.readFonts());
                        });
                    }
                } catch (eT) {
                    self.note("no settings watcher on this lane: " + eT);
                }
                try {
                    var watched = String(self.gtkFontKeys).split(",")
                        .concat(String(self.gtkFontWmKeys).split(","));
                    for (var fi = 0; fi < fontSettings.length; fi++) {
                        for (var ki = 0; ki < watched.length; ki++) {
                            if (watched[ki] === "font-name" ||
                                fontSettings[fi].list_keys().indexOf(watched[ki]) < 0) {
                                continue;
                            }
                            fontSettings[fi].connect("changed::" + watched[ki], function () {
                                self.applyFonts(driver, wv, driver.readFonts());
                            });
                        }
                    }
                } catch (eF) {
                    self.note("no font-settings watcher on this lane: " + eF);
                }
                Gtk.main();
            }
        };
    };
    NeutrinoWebview.resolveLinuxWebKitVersion = function () {
        var GIRepository = imports.gi.GIRepository;
        var Repository = GIRepository["Repository"];
        var repository = Repository["dup_default"]
            ? Repository["dup_default"]()
            : Repository["get_default"]();
        var versions = repository.enumerate_versions("WebKit2");
        if (versions.indexOf("4.1") !== -1) {
            return "4.1";
        }
        if (versions.indexOf("4.0") !== -1) {
            return "4.0";
        }
        throw this.engineUnavailable("WebKit2 introspection typelibs not found");
    };
    NeutrinoWebview.confineMac = function (ObjCRef, dollar) {
        var profile = "";
        try {
            profile = String(ObjCRef.unwrap(
                dollar.NSProcessInfo.processInfo.environment
                    .objectForKey("NEUTRINO_MACOS_PROFILE")) || "");
        } catch (_) {}
        if (!profile) {
            return false;
        }
        if (this.macWritesConfined(ObjCRef, dollar)) {
            return true;
        }
        var rc = -1;
        try {
            ObjCRef.bindFunction("sandbox_init_with_parameters",
                ["int", ["string", "long long", "pointer", "pointer"]]);
            rc = dollar.sandbox_init_with_parameters(profile, 0, dollar(), dollar());
        } catch (e) {
            this.note("could not reach sandbox_init_with_parameters: " + e);
            return false;
        }
        if (rc === 0) {
            return true;
        }
        this.note("seatbelt refused this process's own profile; running unconfined");
        return false;
    };
    NeutrinoWebview.macWritesConfined = function (ObjCRef, dollar) {
        try {
            var home = String(ObjCRef.unwrap(
                dollar.NSProcessInfo.processInfo.environment.objectForKey("HOME")));
            if (!home) {
                return false;
            }
            return !dollar.NSFileManager.defaultManager.isWritableFileAtPath(home);
        } catch (_) {
            return false;
        }
    };
    NeutrinoWebview.createMacDriver = function () {
        var ObjCRef = ObjC;
        var dollar = $;
        var app;
        var self = this;
        var messageCallback = null;
        var webViewRef = null;
        var windowDelegateRef = null;
        var scriptHandlerRef = null;
        var navDelegateRef = null;
        var macKeyableWindow = false;
        var pendingPreload = null;
        var pendingPageScript = null;
        var documentLoaded = false;
        var driverRef = null;
        var windowRef = null;
        var observerRef = null;
        var tickerRef = null;
        var statusTicks = 0;
        var lastDocumentTitle = "";
        var currentUrl = function () {
            try {
                var u = webViewRef ? webViewRef.URL : null;
                return u ? String(ObjCRef.unwrap(u.absoluteString) || "") : "";
            } catch (_) {
                return "";
            }
        };
        return {
            webMessageTransport: "window.webkit.messageHandlers.neutrino.postMessage",
            transportName: "wkscriptmessage",
            init: function () {
                ObjCRef["import"]("Cocoa");
                ObjCRef["import"]("WebKit");
                app = dollar.NSApplication.sharedApplication;
                try {
                    if (!dollar.NSApp.setActivationPolicy(0)) {
                        self.note("this process could not register as an " +
                            "application; its window will not appear");
                    }
                } catch (e) {
                    self.note("could not set the activation policy: " + e);
                }
                try { app.finishLaunching; } catch (e) {
                    self.note("could not finish launching: " + e);
                }
                self.confineMac(ObjCRef, dollar);
                ObjCRef.registerSubclass({
                    name: "NeutrinoWindowDelegate",
                    superclass: "NSObject",
                    methods: {
                        "windowWillClose:": {
                            types: ["void", ["id"]],
                            implementation: function () {
                                try { app.terminate(null); } catch (_) {}
                            }
                        }
                    }
                });
                macKeyableWindow = false;
                try {
                    ObjCRef.registerSubclass({
                        name: "NeutrinoKeyableWindow",
                        superclass: "NSWindow",
                        methods: {
                            "canBecomeKeyWindow": {
                                types: ["bool", []],
                                implementation: function () { return true; }
                            },
                            "canBecomeMainWindow": {
                                types: ["bool", []],
                                implementation: function () { return true; }
                            }
                        }
                    });
                    macKeyableWindow = true;
                } catch (e) {
                    self.note("no keyable window subclass: " + e);
                }
                try {
                    ObjCRef.registerSubclass({
                        name: "NeutrinoAppearanceObserver",
                        superclass: "NSObject",
                        methods: {
                            "desktopThemeChanged:": {
                                types: ["void", ["id"]],
                                implementation: function () {
                                    if (!driverRef) {
                                        return;
                                    }
                                    self.applyTheme(driverRef, windowRef, webViewRef,
                                        driverRef.readTheme());
                                }
                            }
                        }
                    });
                } catch (e) {
                    self.note("no theme watcher on this lane: " + e);
                }
                try {
                    ObjCRef.registerSubclass({
                        name: "NeutrinoStatusTicker",
                        superclass: "NSObject",
                        methods: {
                            "tick:": {
                                types: ["void", ["id"]],
                                implementation: function () {
                                    if (driverRef) {
                                        driverRef.statusTick();
                                    }
                                }
                            }
                        }
                    });
                } catch (e) {
                    self.note("no clock on this lane: " + e);
                }
                try {
                ObjCRef.registerSubclass({
                    name: "NeutrinoNavDelegate",
                    superclass: "NSObject",
                    methods: {
                        "webView:didStartProvisionalNavigation:": {
                            types: ["void", ["id", "id"]],
                            implementation: function () {
                                var going = currentUrl();
                                if (!documentLoaded || self.isTrustedView(going)) {
                                    return;
                                }
                                try {
                                    webViewRef.stopLoading;
                                    self.note("refused navigation to " + going);
                                } catch (e) {
                                    self.note("could not refuse navigation to " +
                                        going + ": " + e);
                                }
                            }
                        },
                        "webView:didCommitNavigation:": {
                            types: ["void", ["id", "id"]],
                            implementation: function () {
                                self.rememberTrustedView(currentUrl());
                                documentLoaded = true;
                            }
                        }
                    }
                });
                } catch (e) {
                    self.note("no navigation guard, and no message will be " +
                        "trusted: " + e);
                }
                try {
                ObjCRef.registerSubclass({
                    name: "NeutrinoScriptHandler",
                    superclass: "NSObject",
                    methods: {
                        "userContentController:didReceiveScriptMessage:": {
                            types: ["void", ["id", "id"]],
                            implementation: function (_, message) {
                                try {
                                    if (!messageCallback) {
                                        return;
                                    }
                                    self.trace("message handler fired");
                                    if (!self.isTrustedMacSender(ObjCRef, message, webViewRef)) {
                                        return;
                                    }
                                    messageCallback(ObjCRef.unwrap(message.body));
                                } catch (_) {}
                            }
                        }
                    }
                });
                } catch (e) {
                    self.note("could not register the message handler: " + e);
                }
            },
            readFile: function (path) {
                var data = dollar.NSData.dataWithContentsOfFile(path);
                if (!data) {
                    throw new Error("Could not read local document: " + path);
                }
                var nsStr = dollar.NSString.alloc.initWithDataEncoding(data, dollar.NSUTF8StringEncoding);
                if (!nsStr) {
                    throw new Error("Could not decode local document as UTF-8: " + path);
                }
                return ObjCRef.unwrap(nsStr);
            },
            getScriptPath: function () {
                return self.getMacScriptPath(ObjCRef, dollar);
            },
            readTheme: function () {
                return self.readMacTheme(ObjCRef, dollar);
            },
            readFonts: function () {
                return self.readMacFonts(ObjCRef, dollar);
            },
            repaint: function (win, wv, background) {
                self.paintMacWindow(win, background);
                if (wv) {
                    self.paintMacView(wv, background);
                }
            },
            evaluate: function (wv, js) {
                if (!documentLoaded) {
                    return;
                }
                wv.evaluateJavaScriptCompletionHandler(js, function () {});
            },
            createWindow: function (config) {
                var frame = dollar.NSMakeRect(0, 0, config.width, config.height);
                var mask = self.undecorated()
                    ? dollar.NSBorderlessWindowMask
                    : (dollar.NSTitledWindowMask | dollar.NSClosableWindowMask |
                       dollar.NSResizableWindowMask);
                var windowClass = (self.undecorated() && macKeyableWindow)
                    ? dollar.NeutrinoKeyableWindow
                    : dollar.NSWindow;
                var win = windowClass.alloc.initWithContentRectStyleMaskBackingDefer(
                    frame,
                    mask,
                    dollar.NSBackingStoreBuffered,
                    false
                );
                win.title = config.title;
                self.paintMacWindow(win, self.resolveBackground(self.theme));
                windowRef = win;
                try { win.center; } catch (_) {}
                windowDelegateRef = dollar.NeutrinoWindowDelegate.alloc.init;
                win["delegate"] = windowDelegateRef;
                this.writeStatus(win);
                return win;
            },
            createWebView: function () {
                var frame = dollar.NSMakeRect(0, 0, 100, 100);
                var wkConfig = dollar.WKWebViewConfiguration.alloc.init;
                try {
                    var ucc = dollar.WKUserContentController.alloc.init;
                    if (messageCallback) {
                        scriptHandlerRef = dollar.NeutrinoScriptHandler.alloc.init;
                        ucc.addScriptMessageHandlerName(scriptHandlerRef, "neutrino");
                    }
                    if (pendingPreload) {
                        ucc.addUserScript(
                            dollar.WKUserScript.alloc
                                .initWithSourceInjectionTimeForMainFrameOnly(
                                    pendingPreload, 0, true
                                )
                        );
                    }
                    if (pendingPageScript) {
                        ucc.addUserScript(
                            dollar.WKUserScript.alloc
                                .initWithSourceInjectionTimeForMainFrameOnly(
                                    pendingPageScript, 1, true
                                )
                        );
                    }
                    wkConfig.userContentController = ucc;
                    self.trace("message channel wired, preload " +
                        (pendingPreload ? "injected" : "MISSING"));
                } catch (e) {
                    self.note("no message channel: " + e);
                }
                var wv = dollar.WKWebView.alloc.initWithFrameConfiguration(frame, wkConfig);
                try { wv.allowsLinkPreview = false; } catch (_) {}
                self.paintMacView(wv, self.resolveBackground(self.theme));
                webViewRef = wv;
                try {
                    navDelegateRef = dollar.NeutrinoNavDelegate.alloc.init;
                    wv["navigationDelegate"] = navDelegateRef;
                } catch (e) {
                    self.note("no navigation guard on this view: " + e);
                }
                return wv;
            },
            injectPreload: function (_, js) {
                pendingPreload = js;
            },
            injectPageScript: function (js) {
                pendingPageScript = js;
            },
            primaryScreenHeight: function () {
                var screens = dollar.NSScreen.screens;
                if (screens && screens.count > 0) {
                    return screens.objectAtIndex(0).frame.size.height;
                }
                return dollar.NSScreen.mainScreen.frame.size.height;
            },
            toMacY: function (y, winHeight) {
                return this.primaryScreenHeight() - y - winHeight;
            },
            toTopLeftY: function (macY, winHeight) {
                return this.primaryScreenHeight() - macY - winHeight;
            },
            windowTitle: function (win) {
                try {
                    return String(ObjCRef.unwrap(win.title) || "");
                } catch (_) {
                    return "";
                }
            },
            writeStatus: function (win) {
                self.writeMacStatus(this, dollar, win);
            },
            statusTick: function () {
                if (!windowRef) {
                    return;
                }
                try {
                    if (webViewRef && self.hasCommittedDocument()) {
                        var raw = "";
                        try {
                            raw = String(ObjCRef.unwrap(webViewRef.title) || "");
                        } catch (_) {
                            raw = "";
                        }
                        if (raw !== lastDocumentTitle) {
                            var name = self.acceptDocumentTitle(currentUrl(), raw);
                            if (name !== null) {
                                lastDocumentTitle = raw;
                                this.setTitle(windowRef, name);
                            }
                        }
                    }
                } catch (_) {}
                try { this.writeStatus(windowRef); } catch (_) {}
            },
            setTitle: function (win, title) {
                win.title = title;
                this.writeStatus(win);
            },
            resize: function (win, w, h) {
                var frame = win.frame;
                var target = this.frameSizeForContent(win, w, h);
                var top = this.toTopLeftY(frame.origin.y, frame.size.height);
                win.setFrameDisplay(
                    dollar.NSMakeRect(frame.origin.x,
                        this.toMacY(top, target.height),
                        target.width, target.height), true);
                this.writeStatus(win);
            },
            frameSizeForContent: function (win, w, h) {
                try {
                    var r = win.frameRectForContentRect(dollar.NSMakeRect(0, 0, w, h));
                    var fw = Math.round(r.size.width);
                    var fh = Math.round(r.size.height);
                    if (fw > 0 && fh > 0) {
                        return { width: fw, height: fh };
                    }
                } catch (e) {
                    self.noteOnce("frameRectForContentRect did not answer: " + e);
                }
                try {
                    var frame = win.frame;
                    var cv = win.contentView.frame;
                    var dw = Math.round(frame.size.width - cv.size.width);
                    var dh = Math.round(frame.size.height - cv.size.height);
                    if (dw >= 0 && dh >= 0) {
                        return { width: w + dw, height: h + dh };
                    }
                } catch (e2) {
                    self.noteOnce("could not measure the window chrome: " + e2);
                }
                self.noteOnce("sizing the frame instead of the content; " +
                    "the chrome is unmeasurable on this window");
                return { width: w, height: h };
            },
            contentSize: function (win) {
                try {
                    var cv = win.contentView.frame;
                    return {
                        width: Math.round(cv.size.width),
                        height: Math.round(cv.size.height)
                    };
                } catch (_) {
                    var f = win.frame;
                    return {
                        width: Math.round(f.size.width),
                        height: Math.round(f.size.height)
                    };
                }
            },
            move: function (win, x, y) {
                var frame = win.frame;
                var macY = this.toMacY(y, frame.size.height);
                win.setFrameDisplay(dollar.NSMakeRect(x, macY, frame.size.width, frame.size.height), true);
                this.writeStatus(win);
            },
            getBounds: function (win) {
                var f = win.frame;
                var size = this.contentSize(win);
                return {
                    width: size.width,
                    height: size.height,
                    x: Math.round(f.origin.x),
                    y: Math.round(this.toTopLeftY(f.origin.y, f.size.height))
                };
            },
            "close": function (win) {
                win.performClose(null);
            },
            openExternal: function (url) {
                if (!self.mayOpenExternal(url)) {
                    return;
                }
                dollar.NSWorkspace.sharedWorkspace.openURL(
                    dollar.NSURL.URLWithString(String(url))
                );
            },
            onWebMessage: function (cb) {
                messageCallback = cb;
            },
            attachWebView: function (win, wv) {
                win.contentView = wv;
            },
            loadHTML: function (wv, html, basePath) {
                var baseUrl = dollar.NSURL.fileURLWithPath(basePath).URLByDeletingLastPathComponent;
                self.trace("loading " + html.length + " bytes with base " + basePath);
                wv.loadHTMLStringBaseURL(html, baseUrl);
            },
            showWindow: function (win) {
                win.makeKeyAndOrderFront(null);
                try { app.activateIgnoringOtherApps(true); } catch (_) {}
            },
            runEventLoop: function () {
                driverRef = this;
                try {
                    observerRef = dollar.NeutrinoAppearanceObserver.alloc.init;
                    dollar.NSDistributedNotificationCenter.defaultCenter
                        .addObserverSelectorNameObject(
                            observerRef, "desktopThemeChanged:",
                            "AppleInterfaceThemeChangedNotification", dollar());
                    dollar.NSNotificationCenter.defaultCenter
                        .addObserverSelectorNameObject(
                            observerRef, "desktopThemeChanged:",
                            "NSSystemColorsDidChangeNotification", dollar());
                } catch (e) {
                    self.note("no theme watcher on this lane: " + e);
                }
                try {
                    tickerRef = dollar.NeutrinoStatusTicker.alloc.init;
                    dollar.NSTimer
                        .scheduledTimerWithTimeIntervalTargetSelectorUserInfoRepeats(
                            0.2, tickerRef, "tick:", null, true);
                } catch (e) {
                    self.note("could not start the clock on this lane: " + e);
                }
                app.run();
            }
        };
    };
    NeutrinoWebview.runWeb = function () {
// SPDX-FileCopyrightText: 2026 Alexandre Gomes Gaigalas <alganet@gmail.com>
// SPDX-License-Identifier: ISC
var win = window;
var doc = document;
function el(id) {
    return doc.getElementById(id);
}
function fill(id, text) {
    var node = el(id);
    if (node) { node.textContent = text; }
}
function engineName() {
    var ua = "";
    try { ua = String(win.navigator.userAgent || ""); } catch (_) { return "unknown"; }
    if (ua.indexOf("Edg") !== -1) { return "WebView2"; }
    if (ua.indexOf("QtWebEngine") !== -1) { return "QtWebEngine"; }
    if (ua.indexOf("Chrome") !== -1) { return "Chromium"; }
    if (ua.indexOf("AppleWebKit") !== -1) { return "WebKit"; }
    return "unknown";
}
function showSize() {
    fill("size", win.innerWidth + " x " + win.innerHeight);
}
function showTheme() {
    var theme = win.neutrino.theme;
    if (!theme) {
        fill("scheme", "no palette on this lane");
        return;
    }
    fill("scheme", theme.scheme + " · " + theme.source + " · " +
        theme.colors.accent);
}
function on(id, handler) {
    var node = el(id);
    if (node) { node.onclick = handler; }
}
var renamed = 0;
function wire() {
    on("bigger", function () { win.resizeBy(60, 40); });
    on("smaller", function () { win.resizeBy(-60, -40); });
    on("nudge", function () { win.moveBy(24, 24); });
    on("corner", function () { win.moveTo(0, 0); });
    on("rename", function () {
        renamed = renamed + 1;
        doc.title = "renamed " + renamed + " time" + (renamed === 1 ? "" : "s");
        fill("hint", "the window is called " + doc.title +
            ", because document.title names it");
    });
    on("source", function () {
        win.open("https://github.com/alganet/neutrino");
        fill("hint", "window.open sent that url to your browser, " +
            "which is a different program");
    });
    on("close", function () { win.close(); });
}
fill("engine", engineName());
fill("transport", win.neutrino.transport);
showSize();
showTheme();
wire();
win.addEventListener("resize", showSize);
win.addEventListener("neutrino:themechange", showTheme);
    };

        /*@end @*/

    NeutrinoWebview.run();
//</script></body></html>
