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

@@ -6,7 +6,7 @@
<groupId>org.openautonomousconnection</groupId> <groupId>org.openautonomousconnection</groupId>
<artifactId>LuaScript</artifactId> <artifactId>LuaScript</artifactId>
<version>1.0.0-BETA.1.4</version> <version>1.0.0-BETA.1.5</version>
<organization> <organization>
<name>Open Autonomous Connection</name> <name>Open Autonomous Connection</name>

View File

@@ -1,30 +1,41 @@
package org.openautonomousconnection.luascript.hosts; package org.openautonomousconnection.luascript.hosts;
/** /**
* Host capability for console logging. * Abstraction for script console output.
*/ */
public interface ConsoleHost { public interface ConsoleHost {
/** /**
* Info log.
*
* @param message message * @param message message
*/ */
void info(String message); void info(String message);
/** /**
* Standard log.
*
* @param message message * @param message message
*/ */
void log(String message); void log(String message);
/** /**
* Warning log.
*
* @param message message * @param message message
*/ */
void warn(String message); void warn(String message);
/** /**
* Error-like stacktrace print.
*
* @param message message * @param message message
*/ */
void error(String message); void error(String message);
/** /**
* Exception-like print.
*
* @param message message * @param message message
*/ */
void exception(String message); void exception(String message);

View File

@@ -4,79 +4,79 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* Host capability that exposes a DOM-like API. * Abstraction over DOM access for scripting.
* *
* <p>Element identity is the (stable) element id.</p> * <p>All element references are by stable element {@code id}.</p>
*/ */
public interface DomHost { public interface DomHost {
/** /**
* Returns all element ids known to the renderer (must be stable). * Returns a list of all element ids in the document.
* *
* @return list of element ids * @return list of ids (never null)
*/ */
List<String> getAllElementIds(); List<String> getAllElementIds();
/** /**
* Returns all attributes for the given element id. * Returns all attributes of the element.
* *
* @param elementId element id * @param elementId element id
* @return attributes map (attributeName -> attributeValue) * @return attributes map (never null)
*/ */
Map<String, String> getAttributes(String elementId); Map<String, String> getAttributes(String elementId);
/** /**
* Returns the tag name of the element (lowercase recommended), e.g. "script", "button". * Returns the element tag name, lowercase if possible.
* *
* @param elementId element id * @param elementId element id
* @return tag name * @return tag name (never null)
*/ */
String getTagName(String elementId); String getTagName(String elementId);
/** /**
* Returns the text content of an element * Returns the element text content.
* *
* @param elementId element id * @param elementId element id
* @return text content (never null) * @return text (never null)
*/ */
String getTextContent(String elementId); String getTextContent(String elementId);
/** /**
* Sets the text content of an element. * Sets the element text content.
* *
* @param elementId element id * @param elementId element id
* @param text text * @param text new text
*/ */
void setTextContent(String elementId, String text); void setTextContent(String elementId, String text);
/** /**
* Gets a single attribute or null if missing. * Returns an attribute value or null if missing.
* *
* @param elementId element id * @param elementId element id
* @param name attribute name * @param name attribute name
* @return value or null * @return value or null if missing
*/ */
String getAttribute(String elementId, String name); String getAttribute(String elementId, String name);
/** /**
* Sets an attribute (creates it if missing). * Sets an attribute value (empty string allowed).
* *
* @param elementId element id * @param elementId element id
* @param name attribute name * @param name attribute name
* @param value attribute value * @param value attribute value
*/ */
void setAttribute(String elementId, String name, String value); void setAttribute(String elementId, String name, String value);
/** /**
* Removes an attribute. * Removes an attribute from the element.
* *
* @param elementId element id * @param elementId element id
* @param name attribute name * @param name attribute name
*/ */
void removeAttribute(String elementId, String name); void removeAttribute(String elementId, String name);
/** /**
* Returns parent id or null. * Returns the parent element id or null if none.
* *
* @param elementId element id * @param elementId element id
* @return parent id or null * @return parent id or null
@@ -84,48 +84,48 @@ public interface DomHost {
String getParentId(String elementId); String getParentId(String elementId);
/** /**
* Returns direct children ids. * Returns children element ids.
* *
* @param elementId element id * @param elementId element id
* @return children ids * @return list of children ids (never null)
*/ */
List<String> getChildrenIds(String elementId); List<String> getChildrenIds(String elementId);
/** /**
* Creates a new element (detached) and returns its id. * Creates an element and makes it addressable immediately.
* *
* @param tagName tag name * @param tagName tag name
* @param requestedId optional requested id, may be null/blank for auto id * @param requestedId requested id or null
* @return created element id * @return created element id
*/ */
String createElement(String tagName, String requestedId); String createElement(String tagName, String requestedId);
/** /**
* Removes an element from the DOM. * Removes an element from the document.
* *
* @param elementId element id * @param elementId element id
*/ */
void removeElement(String elementId); void removeElement(String elementId);
/** /**
* Moves/appends child under parent. * Appends a child element to a parent.
* *
* @param parentId parent id * @param parentId parent id
* @param childId child id * @param childId child id
*/ */
void appendChild(String parentId, String childId); void appendChild(String parentId, String childId);
/** /**
* Inserts child before an existing direct child. * Inserts {@code childId} before {@code beforeChildId} within {@code parentId}.
* *
* @param parentId parent id * @param parentId parent id
* @param childId child id * @param childId child id
* @param beforeChildId existing child id * @param beforeChildId existing child id
*/ */
void insertBefore(String parentId, String childId, String beforeChildId); void insertBefore(String parentId, String childId, String beforeChildId);
/** /**
* Checks if an element id exists. * Checks if an element with the given id exists.
* *
* @param id element id * @param id element id
* @return true if exists * @return true if exists
@@ -133,18 +133,18 @@ public interface DomHost {
boolean exists(String id); boolean exists(String id);
/** /**
* Returns element ids by tag. * Queries elements by tag name and returns their ids.
* *
* @param tagName tag * @param tagName tag name
* @return ids * @return list of ids (never null)
*/ */
List<String> queryByTag(String tagName); List<String> queryByTag(String tagName);
/** /**
* Returns element ids by class. * Queries elements by class token and returns their ids.
* *
* @param className class * @param className class name token
* @return ids * @return list of ids (never null)
*/ */
List<String> queryByClass(String className); List<String> queryByClass(String className);
} }

View File

@@ -1,37 +1,41 @@
package org.openautonomousconnection.luascript.hosts; package org.openautonomousconnection.luascript.hosts;
import java.util.Map;
/** /**
* Event subscription abstraction (implemented by the client UI layer). * Abstraction over DOM event subscription for scripting.
*
* <p>Implementations forward DOM events into a {@code LuaEventRouter}.</p>
*/ */
public interface EventHost { public interface EventHost {
/** /**
* Subscribes to an element event. * Adds a listener for an element's event.
* *
* @param elementId element id * @param elementId element id
* @param eventName event name (e.g. click) * @param eventName normalized event name
*/ */
void addListener(String elementId, String eventName); void addListener(String elementId, String eventName);
/** /**
* Unsubscribes from an element event. * Removes a listener for an element's event.
* *
* @param elementId element id * @param elementId element id
* @param eventName event name * @param eventName normalized event name
*/ */
void removeListener(String elementId, String eventName); void removeListener(String elementId, String eventName);
/** /**
* Subscribes to a global event (app/window scope). * Adds a global (document-level) listener.
* *
* @param eventName event name * @param eventName normalized event name
*/ */
void addGlobalListener(String eventName); void addGlobalListener(String eventName);
/** /**
* Unsubscribes from a global event. * Removes a global (document-level) listener.
* *
* @param eventName event name * @param eventName normalized event name
*/ */
void removeGlobalListener(String eventName); void removeGlobalListener(String eventName);
} }

View File

@@ -1,165 +1,145 @@
package org.openautonomousconnection.luascript.hosts; package org.openautonomousconnection.luascript.hosts;
import javafx.scene.web.WebEngine;
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.runtime.LuaRuntime;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
/** /**
* Service container holding optional host capabilities. * Container for host-side services exposed to Lua.
* *
* <p>This avoids one huge "bridge" interface.</p> * <p>All services are optional to allow different embedding scenarios.</p>
*/ */
public final class HostServices { public interface HostServices {
private final ConsoleHost console;
private final DomHost dom;
private final EventHost events;
private final ResourceHost resources;
private final UiHost ui;
private HostServices(Builder b) {
this.console = b.console;
this.ui = b.ui;
this.dom = b.dom;
this.events = b.events;
this.resources = b.resources;
}
/** /**
* Builds a JavaFX WebView preset of HostServices (DOM + events + resources + UI). * Returns an optional UI host.
* *
* <p>Important: This method creates the {@link EventHost} using the provided {@link LuaRuntime}'s * @return ui host
* {@link org.openautonomousconnection.luascript.runtime.LuaEventRouter}. Therefore, the runtime must already */
* be constructed before calling this method.</p> Optional<UiHost> ui();
/**
* Returns an optional DOM host.
* *
* <p>Note: You should call {@link FxDomHost#ensureAllElementsHaveId()} after the WebEngine finished loading.</p> * @return dom host
*/
Optional<DomHost> dom();
/**
* Returns an optional event host.
* *
* @param engine JavaFX WebEngine * @return event host
* @param runtime Lua runtime used for event routing
* @param console optional console host (may be null)
* @return HostServices preset for JavaFX WebView
*/ */
public static HostServices fxPreset(WebEngine engine, LuaRuntime runtime, ConsoleHost console) { Optional<EventHost> events();
Objects.requireNonNull(engine, "engine");
Objects.requireNonNull(runtime, "runtime");
FxDomHost dom = new FxDomHost(engine);
FxWebViewResourceHost resources = new FxWebViewResourceHost(engine);
FxUiHost ui = new FxUiHost(engine, dom);
FxEventHost events = new FxEventHost(dom, runtime.eventRouter());
Builder b = builder().dom(dom).events(events).resources(resources).ui(ui);
if (console != null) {
b.console(console);
}
return b.build();
}
/** /**
* @return builder * Returns an optional resource host.
*
* @return resource host
*/ */
public static Builder builder() { Optional<ResourceHost> resources();
return new Builder();
}
/** /**
* @return optional DomHost capability * Returns an optional console host.
*
* @return console host
*/ */
public Optional<DomHost> dom() { Optional<ConsoleHost> console();
return Optional.ofNullable(dom);
}
/** /**
* @return optional EventHost capability * Simple immutable implementation.
*/ */
public Optional<EventHost> events() { final class Default implements HostServices {
return Optional.ofNullable(events); private final UiHost ui;
} private final DomHost dom;
private final EventHost events;
/** private final ResourceHost resources;
* @return optional ResourceHost capability private final ConsoleHost console;
*/
public Optional<ResourceHost> resources() {
return Optional.ofNullable(resources);
}
/**
* @return optional console host
*/
public Optional<ConsoleHost> console() {
return Optional.ofNullable(console);
}
/**
* @return optional ui host
*/
public Optional<UiHost> ui() {
return Optional.ofNullable(ui);
}
/**
* Builder for HostServices.
*/
public static final class Builder {
private ConsoleHost console;
private DomHost dom;
private EventHost events;
private ResourceHost resources;
private UiHost ui;
public Builder console(ConsoleHost console) {
this.console = Objects.requireNonNull(console, "console");
return this;
}
public Builder ui(UiHost ui) {
this.ui = Objects.requireNonNull(ui, "ui");
return this;
}
/** /**
* Provides dom capability. * Creates a HostServices container.
* *
* @param ui ui host
* @param dom dom host * @param dom dom host
* @return this
*/
public Builder dom(DomHost dom) {
this.dom = Objects.requireNonNull(dom, "dom");
return this;
}
/**
* Provides event subscription capability.
*
* @param events event host * @param events event host
* @return this * @param resources resource host
* @param console console host
*/ */
public Builder events(EventHost events) { public Default(UiHost ui, DomHost dom, EventHost events, ResourceHost resources, ConsoleHost console) {
this.events = Objects.requireNonNull(events, "events"); this.ui = ui;
return this; this.dom = dom;
this.events = events;
this.resources = resources;
this.console = console;
} }
@Override
public Optional<UiHost> ui() {
return Optional.ofNullable(ui);
}
@Override
public Optional<DomHost> dom() {
return Optional.ofNullable(dom);
}
@Override
public Optional<EventHost> events() {
return Optional.ofNullable(events);
}
@Override
public Optional<ResourceHost> resources() {
return Optional.ofNullable(resources);
}
@Override
public Optional<ConsoleHost> console() {
return Optional.ofNullable(console);
}
}
/**
* Stdout-based console host.
*/
final class StdoutConsole implements ConsoleHost {
private final String prefix;
/** /**
* Provides resource loading capability. * Creates a new stdout console with a prefix.
* *
* @param resources resource host * @param prefix prefix (may be empty)
* @return this
*/ */
public Builder resources(ResourceHost resources) { public StdoutConsole(String prefix) {
this.resources = Objects.requireNonNull(resources, "resources"); this.prefix = Objects.requireNonNull(prefix, "prefix");
return this;
} }
public HostServices build() { @Override
return new HostServices(this); public void info(String message) {
System.out.println(prefix + "[info] " + safe(message));
}
@Override
public void log(String message) {
System.out.println(prefix + "[log] " + safe(message));
}
@Override
public void warn(String message) {
System.out.println(prefix + "[warn] " + safe(message));
}
@Override
public void error(String message) {
System.err.println(prefix + "[error] " + safe(message));
}
@Override
public void exception(String message) {
System.err.println(prefix + "[exception] " + safe(message));
}
private static String safe(String s) {
return s == null ? "" : s;
} }
} }
} }

View File

@@ -1,16 +1,16 @@
package org.openautonomousconnection.luascript.hosts; package org.openautonomousconnection.luascript.hosts;
/** /**
* Resource loading abstraction for LuaScript (e.g. script src). * Abstraction for loading external resources (e.g. {@code <script src="...">}).
*/ */
public interface ResourceHost { public interface ResourceHost {
/** /**
* Reads text from a script source (file/url/virtual path). * Reads a UTF-8 text resource.
* *
* @param src source identifier * @param src resource location (absolute or relative)
* @return text content * @return text content
* @throws Exception on load failures * @throws Exception on IO or resolution error
*/ */
String readText(String src) throws Exception; String readText(String src) throws Exception;
} }

View File

@@ -1,59 +1,215 @@
package org.openautonomousconnection.luascript.hosts; package org.openautonomousconnection.luascript.hosts;
/** /**
* Host capability for UI operations. * Abstraction for UI operations exposed to scripts.
*/ */
public interface UiHost { public interface UiHost {
/**
* Displays an alert-like message.
*
* @param message message
*/
void alert(String message); void alert(String message);
/**
* Displays a confirm-like prompt.
*
* @param message message
* @return true if accepted
*/
boolean confirm(String message); boolean confirm(String message);
/**
* Displays a prompt-like query.
*
* @param message message
* @param defaultValue default value
* @return user response or default
*/
String prompt(String message, String defaultValue); String prompt(String message, String defaultValue);
/**
* Sets element text.
*
* @param elementId element id
* @param text text
*/
void setText(String elementId, String text); void setText(String elementId, String text);
/**
* Gets element text.
*
* @param elementId element id
* @return text (never null)
*/
String getText(String elementId); String getText(String elementId);
/**
* Sets element HTML (best-effort for non-JS hosts).
*
* @param elementId element id
* @param html html
*/
void setHtml(String elementId, String html); void setHtml(String elementId, String html);
/**
* Gets element HTML (best-effort for non-JS hosts).
*
* @param elementId element id
* @return html (never null)
*/
String getHtml(String elementId); String getHtml(String elementId);
/**
* Sets a form-like value.
*
* @param elementId element id
* @param value value
*/
void setValue(String elementId, String value); void setValue(String elementId, String value);
/**
* Gets a form-like value.
*
* @param elementId element id
* @return value (never null)
*/
String getValue(String elementId); String getValue(String elementId);
/**
* Enables/disables an element.
*
* @param elementId element id
* @param enabled enabled
*/
void setEnabled(String elementId, boolean enabled); void setEnabled(String elementId, boolean enabled);
/**
* Shows/hides an element.
*
* @param elementId element id
* @param visible visible
*/
void setVisible(String elementId, boolean visible); void setVisible(String elementId, boolean visible);
/**
* Adds a class token.
*
* @param elementId element id
* @param className class token
*/
void addClass(String elementId, String className); void addClass(String elementId, String className);
/**
* Removes a class token.
*
* @param elementId element id
* @param className class token
*/
void removeClass(String elementId, String className); void removeClass(String elementId, String className);
/**
* Toggles a class token.
*
* @param elementId element id
* @param className class token
* @return true if class is present after toggle
*/
boolean toggleClass(String elementId, String className); boolean toggleClass(String elementId, String className);
/**
* Checks whether a class token is present.
*
* @param elementId element id
* @param className class token
* @return true if present
*/
boolean hasClass(String elementId, String className); boolean hasClass(String elementId, String className);
/**
* Sets a CSS property via style attribute (best-effort).
*
* @param elementId element id
* @param property css property
* @param value css value
*/
void setStyle(String elementId, String property, String value); void setStyle(String elementId, String property, String value);
/**
* Gets a CSS property via style attribute (best-effort).
*
* @param elementId element id
* @param property css property
* @return css value or empty string
*/
String getStyle(String elementId, String property); String getStyle(String elementId, String property);
/**
* Sets an attribute.
*
* @param elementId element id
* @param name attribute name
* @param value value
*/
void setAttribute(String elementId, String name, String value); void setAttribute(String elementId, String name, String value);
/**
* Gets an attribute value or null if missing.
*
* @param elementId element id
* @param name attribute name
* @return value or null
*/
String getAttribute(String elementId, String name); String getAttribute(String elementId, String name);
/**
* Removes an attribute.
*
* @param elementId element id
* @param name attribute name
*/
void removeAttribute(String elementId, String name); void removeAttribute(String elementId, String name);
/**
* Focuses an element (best-effort).
*
* @param elementId element id
*/
void focus(String elementId); void focus(String elementId);
/**
* Blurs an element (best-effort).
*
* @param elementId element id
*/
void blur(String elementId); void blur(String elementId);
/**
* Scrolls an element into view (best-effort).
*
* @param elementId element id
*/
void scrollIntoView(String elementId); void scrollIntoView(String elementId);
/**
* Returns viewport width or -1 if unknown.
*
* @return width
*/
int viewportWidth(); int viewportWidth();
/**
* Returns viewport height or -1 if unknown.
*
* @return height
*/
int viewportHeight(); int viewportHeight();
/**
* Returns current host time in milliseconds.
*
* @return millis
*/
long nowMillis(); long nowMillis();
} }

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: * 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. * 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 { public final class LuaScriptBootstrap {
@@ -36,11 +38,11 @@ public final class LuaScriptBootstrap {
EventHost eventHost = services.events().orElseThrow(() -> new IllegalStateException("EventHost not provided")); EventHost eventHost = services.events().orElseThrow(() -> new IllegalStateException("EventHost not provided"));
ResourceHost resources = services.resources().orElseThrow(() -> new IllegalStateException("ResourceHost 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(); 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()) { for (String elementId : dom.getAllElementIds()) {
String tag = safeLower(dom.getTagName(elementId)); String tag = safeLower(dom.getTagName(elementId));
if (!"script".equals(tag)) continue; if (!"script".equals(tag)) continue;