This commit is contained in:
UnlegitDqrk
2026-02-10 19:24:38 +01:00
parent 93fff3ffb3
commit 86800cb166
9 changed files with 480 additions and 193 deletions

View File

@@ -0,0 +1,134 @@
package org.openautonomousconnection.luascript.runtime;
import javafx.concurrent.Worker;
import javafx.scene.web.WebEngine;
import org.luaj.vm2.Globals;
import org.openautonomousconnection.luascript.fx.FxDomHost;
import org.openautonomousconnection.luascript.fx.FxEventHost;
import org.openautonomousconnection.luascript.fx.FxUiHost;
import org.openautonomousconnection.luascript.fx.FxWebViewResourceHost;
import org.openautonomousconnection.luascript.hosts.HostServices;
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
import org.openautonomousconnection.luascript.utils.LuaGlobalsFactory;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* JavaFX WebView integration entry point for LuaScript (no JavaScript).
*
* <p>Hard rule: every HTML script tag is treated as Lua.</p>
*/
public final class FxLuaScriptEngine implements AutoCloseable {
private final WebEngine engine;
private final LuaExecutionPolicy policy;
private final AtomicBoolean bootstrapped = new AtomicBoolean(false);
private LuaRuntime runtime;
/**
* Creates an integration engine with default UI execution policy.
*
* @param engine web engine
*/
public FxLuaScriptEngine(WebEngine engine) {
this(engine, LuaExecutionPolicy.uiDefault());
}
/**
* Creates an integration engine with a custom execution policy.
*
* @param engine web engine
* @param policy execution policy
*/
public FxLuaScriptEngine(WebEngine engine, LuaExecutionPolicy policy) {
this.engine = Objects.requireNonNull(engine, "engine");
this.policy = Objects.requireNonNull(policy, "policy");
}
/**
* Installs a load hook that bootstraps Lua when a page finished loading.
*/
public void install() {
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
bootstrapped.set(false);
bootstrap();
} else if (newState == Worker.State.CANCELLED || newState == Worker.State.FAILED) {
bootstrapped.set(false);
closeRuntimeQuietly();
}
});
}
/**
* Bootstraps Lua for the currently loaded document.
*/
public void bootstrap() {
if (!bootstrapped.compareAndSet(false, true)) return;
closeRuntimeQuietly();
// DOM host must exist before event/UI tables, and must ensure stable ids.
FxDomHost dom = new FxDomHost(engine);
dom.ensureAllElementsHaveId();
// Create per-page globals; harden sandbox in production.
Globals globals = LuaGlobalsFactory.create(
new LuaGlobalsFactory.Options()
.enableDebug(false)
.sandbox(true)
);
// Create runtime first (router lives inside it).
HostServices.StdoutConsole console = new HostServices.StdoutConsole("[lua] ");
FxUiHost uiHost = new FxUiHost(engine, dom);
FxWebViewResourceHost resourceHost = new FxWebViewResourceHost(engine);
// runtime depends on services; events depends on runtime router.
// We'll create eventHost after runtime, then build HostServices with it.
LuaRuntime rt = new LuaRuntime(globals, new HostServices.Default(uiHost, dom, null, resourceHost, console), policy);
FxEventHost eventHost = new FxEventHost(dom, rt.eventRouter());
// Rebuild services including eventHost and reinstall tables.
HostServices services = new HostServices.Default(uiHost, dom, eventHost, resourceHost, console);
// Replace runtime with correct services (clean and deterministic).
rt.close();
rt = new LuaRuntime(globals, services, policy);
rt.installStdTables(true);
rt.bootstrapFromDom();
this.runtime = rt;
}
/**
* Returns active runtime or null if not bootstrapped.
*
* @return runtime or null
*/
public LuaRuntime runtimeOrNull() {
return runtime;
}
@Override
public void close() {
closeRuntimeQuietly();
}
private void closeRuntimeQuietly() {
LuaRuntime rt = this.runtime;
this.runtime = null;
if (rt != null) {
try {
rt.close();
} catch (Exception ignored) {
// Best-effort shutdown.
}
}
}
}

View File

@@ -12,8 +12,10 @@ import java.util.Objects;
/**
* Bootstrap that:
* 1) Executes ALL scripts found in DOM as Lua: &lt;script&gt; ... or &lt;script src="..."&gt;
* 1) Executes ALL scripts found in DOM as Lua: <script> ... or <script src="...">
* 2) Scans DOM for on:* handlers and binds them automatically.
*
* <p>Hard client rule: JavaScript is not executed. Every HTML script tag is treated as Lua.</p>
*/
public final class LuaScriptBootstrap {
@@ -36,11 +38,11 @@ public final class LuaScriptBootstrap {
EventHost eventHost = services.events().orElseThrow(() -> new IllegalStateException("EventHost not provided"));
ResourceHost resources = services.resources().orElseThrow(() -> new IllegalStateException("ResourceHost not provided"));
executeAllScripts(globals, dom, resources);
executeAllScriptsAsLua(globals, dom, resources);
new LuaDomBinder(dom, eventHost, dispatcher).bindAll();
}
private static void executeAllScripts(Globals globals, DomHost dom, ResourceHost resources) {
private static void executeAllScriptsAsLua(Globals globals, DomHost dom, ResourceHost resources) {
for (String elementId : dom.getAllElementIds()) {
String tag = safeLower(dom.getTagName(elementId));
if (!"script".equals(tag)) continue;
@@ -70,4 +72,4 @@ public final class LuaScriptBootstrap {
private static String safeLower(String s) {
return s == null ? "" : s.trim().toLowerCase();
}
}
}