Rendering und Lua working, need to work on request method via custom URL implementation
This commit is contained in:
9
pom.xml
9
pom.xml
@@ -105,11 +105,6 @@
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.openautonomousconnection</groupId>
|
||||
<artifactId>Protocol</artifactId>
|
||||
<version>1.0.0-BETA.7.7</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openautonomousconnection</groupId>
|
||||
<artifactId>OACSwing</artifactId>
|
||||
@@ -124,12 +119,12 @@
|
||||
<dependency>
|
||||
<groupId>org.openautonomousconnection</groupId>
|
||||
<artifactId>LuaScript</artifactId>
|
||||
<version>1.0.0-BETA.1.4</version>
|
||||
<version>1.0.0-BETA.1.5</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openautonomousconnection</groupId>
|
||||
<artifactId>InfoNameLib</artifactId>
|
||||
<version>1.0.0-BETA.1.1</version>
|
||||
<version>1.0.0-BETA.1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.openjfx</groupId>
|
||||
|
||||
@@ -1,24 +1,13 @@
|
||||
package org.openautonomousconnection.webclient.recode;
|
||||
package org.openautonomousconnection.webclient;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketSendEvent;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientConnectedEvent;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.state.ClientDisconnectedEvent;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||
import org.openautonomousconnection.infonamelib.OacWebUrlInstaller;
|
||||
import org.openautonomousconnection.infonamelib.ProtocolHandlerPackages;
|
||||
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
||||
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
|
||||
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolServerEvent;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ClientImpl extends ProtocolClient {
|
||||
@Override
|
||||
@@ -65,46 +54,17 @@ public class ClientImpl extends ProtocolClient {
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(INSResponseStatus status, List<INSRecord> records) {
|
||||
try {
|
||||
String host = records.getFirst().value;
|
||||
if (!host.contains(":")) host = host + ":1028";
|
||||
String[] split = host.split(":");
|
||||
Main.getClient().getClientServerConnection().connect(split[0], Integer.parseInt(split[1]));
|
||||
} catch (Exception e) {
|
||||
Main.getClient().getProtocolBridge().getLogger().exception("Failed to connect to Server", e);
|
||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to Server:\n" + e.getMessage(),
|
||||
"Server Connection", OACOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
super.onResponse(status, records);
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onConnected(ConnectedToProtocolINSServerEvent event) {
|
||||
try {
|
||||
Main.getClient().buildServerConnection(null, true);
|
||||
buildServerConnection(null, getProtocolBridge().getProtocolValues().ssl);
|
||||
ProtocolHandlerPackages.installPackage("org.openautonomousconnection.infonamelib");
|
||||
OacWebUrlInstaller.installOnce(getProtocolBridge().getProtocolValues().eventManager, this);
|
||||
SwingUtilities.invokeLater(() -> Main.getUi().openNewTab("web://info.oac/"));
|
||||
} catch (Exception e) {
|
||||
Main.getClient().getProtocolBridge().getLogger().exception("Failed to build Server connection", e);
|
||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to build Server connection:\n" + e.getMessage(),
|
||||
"Server Connection", OACOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
|
||||
Main.getUi().openNewTab("register.oac");
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onConnected(ConnectedToProtocolServerEvent event) {
|
||||
try {
|
||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
||||
"index.html",
|
||||
WebRequestMethod.GET,
|
||||
Map.of(),
|
||||
null
|
||||
), TransportProtocol.TCP);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,100 +1,69 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||
import org.openautonomousconnection.infonamelib.InfoNames;
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.oacswing.component.design.Design;
|
||||
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||
import org.openautonomousconnection.oacswing.component.design.OACColor;
|
||||
import org.openautonomousconnection.protocol.ProtocolBridge;
|
||||
import org.openautonomousconnection.protocol.ProtocolValues;
|
||||
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||
import org.openautonomousconnection.webclient.network.WebClient;
|
||||
import org.openautonomousconnection.webclient.network.handlers.ServerPacketHandler;
|
||||
import org.openautonomousconnection.webclient.packetlistener.listeners.WebPacketListener;
|
||||
import org.openautonomousconnection.webclient.ui.MainFrame;
|
||||
import org.openautonomousconnection.webclient.ui.dom.DOMContainerPanel;
|
||||
import org.openautonomousconnection.webclient.settings.INSList;
|
||||
import org.openautonomousconnection.webclient.ui.BrowserUI;
|
||||
import org.openautonomousconnection.webclient.ui.FxBootstrap;
|
||||
|
||||
import java.beans.EventHandler;
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
public class Main {
|
||||
public static MainFrame mainFrame;
|
||||
|
||||
public static final String DEFAULT_INS = "open-autonomous-connection.org";
|
||||
@Getter
|
||||
private static ClientImpl client;
|
||||
|
||||
public static final int DEFAULT_INS_PORT_TCP = 1026;
|
||||
public static final int DEFAULT_WEB_PORT_TCP = 1028;
|
||||
private static ProtocolBridge bridge;
|
||||
|
||||
private static final ProtocolVersion PROTOCOL_VERSION = ProtocolVersion.PV_1_0_0_BETA;
|
||||
|
||||
public static WebClient client;
|
||||
|
||||
public static ProtocolBridge bridge;
|
||||
|
||||
public static void main(String[] args) {
|
||||
initProtocol();
|
||||
|
||||
InfoNames.registerOACInfoNameProtocols();
|
||||
|
||||
try {
|
||||
registerEventHandlers();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
registerPacketListeners();
|
||||
|
||||
/* Darkmode, wohoo! */
|
||||
initDesigns();
|
||||
|
||||
DesignManager.setGlobalDesign(Design.DARK);
|
||||
|
||||
mainFrame = new MainFrame();
|
||||
|
||||
mainFrame.setVisible(true);
|
||||
}
|
||||
@Getter
|
||||
private static BrowserUI ui;
|
||||
|
||||
private static void initProtocol() {
|
||||
|
||||
ProtocolValues values = new ProtocolValues();
|
||||
|
||||
values.packetHandler = new PacketHandler();
|
||||
values.eventManager = new EventManager();
|
||||
values.ssl = true;
|
||||
|
||||
client = new WebClient();
|
||||
client = new ClientImpl();
|
||||
|
||||
try {
|
||||
bridge = new ProtocolBridge(
|
||||
client,
|
||||
values,
|
||||
PROTOCOL_VERSION,
|
||||
ProtocolVersion.PV_1_0_0_BETA,
|
||||
new File("logs")
|
||||
);
|
||||
|
||||
// TODO
|
||||
client.connectToINSServer(Main.DEFAULT_INS, -1);
|
||||
client.buildINSConnection();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerEventHandlers() throws Exception {
|
||||
EventManager eventManager = bridge.getProtocolValues().eventManager;
|
||||
public static void main(String[] args) {
|
||||
initProtocol();
|
||||
FxBootstrap.ensureInitialized();
|
||||
DesignManager.setGlobalDesign(Design.DARK);
|
||||
|
||||
eventManager.registerListener(new ServerPacketHandler());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
ui = new BrowserUI();
|
||||
ui.setSize(1200, 800);
|
||||
ui.setLocationRelativeTo(null);
|
||||
ui.setVisible(true);
|
||||
|
||||
try {
|
||||
bridge.getProtocolValues().eventManager.registerListener(client);
|
||||
client.getClientINSConnection().connect(INSList.DEFAULT_INS, INSList.DEFAULT_PORT);
|
||||
} catch (Exception exception) {
|
||||
exception.printStackTrace(System.out);
|
||||
}
|
||||
|
||||
private static void registerPacketListeners() {
|
||||
ServerPacketHandler.registerPacketListener(new WebPacketListener());
|
||||
}
|
||||
|
||||
private static void initDesigns() {
|
||||
//TODO
|
||||
|
||||
//DesignManager.getInstance().registerComponent(DOMContainerPanel.class, OACColor.DARK_BACKGROUND);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package org.openautonomousconnection.webclient.lua;
|
||||
|
||||
import org.openautonomousconnection.webclient.Main;
|
||||
|
||||
public class WebLogger {
|
||||
|
||||
private final String host;
|
||||
|
||||
public WebLogger(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public void log(String string) {
|
||||
Main.getClient().getProtocolBridge().getLogger().log(host + ": " + string);
|
||||
}
|
||||
|
||||
public void info(String info) {
|
||||
Main.getClient().getProtocolBridge().getLogger().info(host + ": " + info);
|
||||
}
|
||||
|
||||
public void warn(String warn) {
|
||||
Main.getClient().getProtocolBridge().getLogger().warn(host + ": " + warn);
|
||||
}
|
||||
|
||||
public void error(String error) {
|
||||
Main.getClient().getProtocolBridge().getLogger().error(host + ": " + error);
|
||||
}
|
||||
|
||||
public void exception(String infoLine, Exception exception) {
|
||||
Main.getClient().getProtocolBridge().getLogger().exception(host + ": " + infoLine, exception);
|
||||
}
|
||||
|
||||
public void debug(String debug) {
|
||||
Main.getClient().getProtocolBridge().getLogger().debug(host + ": " + debug);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package org.openautonomousconnection.webclient.lua.hosts;
|
||||
|
||||
import org.openautonomousconnection.luascript.hosts.ConsoleHost;
|
||||
import org.openautonomousconnection.webclient.lua.WebLogger;
|
||||
|
||||
public class ConsoleHostImpl implements ConsoleHost {
|
||||
private final WebLogger logger;
|
||||
|
||||
public ConsoleHostImpl(WebLogger logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String message) {
|
||||
logger.info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(String message) {
|
||||
logger.log(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String message) {
|
||||
logger.warn(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String message) {
|
||||
logger.error(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exception(String message) {
|
||||
logger.exception("", new RuntimeException(message));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
package org.openautonomousconnection.webclient.lua.hosts;
|
||||
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.openautonomousconnection.luascript.fx.FxDomHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxThreadBridge;
|
||||
import org.openautonomousconnection.luascript.hosts.UiHost;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* UiHost implementation for JavaFX WebView (no JavaScript).
|
||||
*
|
||||
* <p>Operations are implemented via W3C DOM attributes/text, and best-effort behavior for
|
||||
* value/style/class using standard attributes.</p>
|
||||
*/
|
||||
public final class UiHostImpl implements UiHost {
|
||||
|
||||
private final WebEngine engine;
|
||||
private final WebView view;
|
||||
private final FxDomHost dom;
|
||||
|
||||
/**
|
||||
* Creates a new UI host.
|
||||
*
|
||||
* @param engine web engine
|
||||
* @param view web view
|
||||
* @param dom dom host
|
||||
*/
|
||||
public UiHostImpl(WebEngine engine, WebView view, FxDomHost dom) {
|
||||
this.engine = Objects.requireNonNull(engine, "engine");
|
||||
this.view = view;
|
||||
this.dom = Objects.requireNonNull(dom, "dom");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void alert(String message) {
|
||||
// No JS: use simple JavaFX dialog-less fallback (log-style). You can replace with real Dialogs later.
|
||||
// Keeping it deterministic and non-blocking for now.
|
||||
System.out.println("[ui.alert] " + (message == null ? "" : message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean confirm(String message) {
|
||||
// No JS: deterministic default (false). Replace with JavaFX dialogs if you want UI interaction.
|
||||
System.out.println("[ui.confirm] " + (message == null ? "" : message));
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prompt(String message, String defaultValue) {
|
||||
// No JS: deterministic default.
|
||||
System.out.println("[ui.prompt] " + (message == null ? "" : message));
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setText(String elementId, String text) {
|
||||
dom.setTextContent(elementId, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(String elementId) {
|
||||
return dom.getTextContent(elementId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHtml(String elementId, String html) {
|
||||
// Without JS, safest is to set textContent (prevents HTML parsing).
|
||||
// If you need real HTML injection, we must extend DomHost with fragment parsing (not in current API).
|
||||
dom.setTextContent(elementId, html);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHtml(String elementId) {
|
||||
// Without JS, best-effort: return textContent.
|
||||
return dom.getTextContent(elementId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(String elementId, String value) {
|
||||
// Input/textarea value is commonly reflected as attribute "value".
|
||||
dom.setAttribute(elementId, "value", value == null ? "" : value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getValue(String elementId) {
|
||||
String v = dom.getAttribute(elementId, "value");
|
||||
return v == null ? "" : v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(String elementId, boolean enabled) {
|
||||
if (enabled) dom.removeAttribute(elementId, "disabled");
|
||||
else dom.setAttribute(elementId, "disabled", "disabled");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(String elementId, boolean visible) {
|
||||
// Best-effort via style attribute
|
||||
String style = dom.getAttribute(elementId, "style");
|
||||
style = style == null ? "" : style;
|
||||
|
||||
style = removeCssProp(style, "display");
|
||||
if (!visible) {
|
||||
style = style.trim();
|
||||
if (!style.isEmpty() && !style.endsWith(";")) style += ";";
|
||||
style += "display:none;";
|
||||
}
|
||||
|
||||
dom.setAttribute(elementId, "style", style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addClass(String elementId, String className) {
|
||||
String cls = Objects.requireNonNull(className, "className").trim();
|
||||
if (cls.isEmpty()) return;
|
||||
|
||||
FxThreadBridge.runAndWait(() -> {
|
||||
Element el = dom.byId(elementId);
|
||||
String c = el.getAttribute("class");
|
||||
c = (c == null) ? "" : c.trim();
|
||||
|
||||
if (c.isEmpty()) {
|
||||
el.setAttribute("class", cls);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hasClassToken(c, cls)) {
|
||||
el.setAttribute("class", c + " " + cls);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeClass(String elementId, String className) {
|
||||
String cls = Objects.requireNonNull(className, "className").trim();
|
||||
if (cls.isEmpty()) return;
|
||||
|
||||
FxThreadBridge.runAndWait(() -> {
|
||||
Element el = dom.byId(elementId);
|
||||
String c = el.getAttribute("class");
|
||||
c = (c == null) ? "" : c.trim();
|
||||
if (c.isEmpty()) return;
|
||||
|
||||
String[] parts = c.split("\\s+");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String p : parts) {
|
||||
if (p.equals(cls)) continue;
|
||||
if (!sb.isEmpty()) sb.append(' ');
|
||||
sb.append(p);
|
||||
}
|
||||
el.setAttribute("class", sb.toString());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean toggleClass(String elementId, String className) {
|
||||
if (hasClass(elementId, className)) {
|
||||
removeClass(elementId, className);
|
||||
return false;
|
||||
}
|
||||
addClass(elementId, className);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasClass(String elementId, String className) {
|
||||
String cls = Objects.requireNonNull(className, "className").trim();
|
||||
if (cls.isEmpty()) return false;
|
||||
|
||||
return FxThreadBridge.callAndWait(() -> {
|
||||
Element el = dom.byId(elementId);
|
||||
String c = el.getAttribute("class");
|
||||
c = (c == null) ? "" : c.trim();
|
||||
return !c.isEmpty() && hasClassToken(c, cls);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStyle(String elementId, String property, String value) {
|
||||
String prop = Objects.requireNonNull(property, "property").trim().toLowerCase();
|
||||
if (prop.isEmpty()) return;
|
||||
|
||||
String style = dom.getAttribute(elementId, "style");
|
||||
style = style == null ? "" : style;
|
||||
|
||||
style = removeCssProp(style, prop);
|
||||
String v = value == null ? "" : value.trim();
|
||||
|
||||
if (!v.isEmpty()) {
|
||||
style = style.trim();
|
||||
if (!style.isEmpty() && !style.endsWith(";")) style += ";";
|
||||
style += prop + ":" + v + ";";
|
||||
}
|
||||
dom.setAttribute(elementId, "style", style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStyle(String elementId, String property) {
|
||||
// Best-effort parsing from style attribute.
|
||||
String prop = Objects.requireNonNull(property, "property").trim().toLowerCase();
|
||||
if (prop.isEmpty()) return "";
|
||||
|
||||
String style = dom.getAttribute(elementId, "style");
|
||||
style = style == null ? "" : style;
|
||||
|
||||
for (String part : style.split(";")) {
|
||||
String p = part.trim();
|
||||
if (p.isEmpty()) continue;
|
||||
int idx = p.indexOf(':');
|
||||
if (idx <= 0) continue;
|
||||
|
||||
String k = p.substring(0, idx).trim().toLowerCase();
|
||||
if (k.equals(prop)) return p.substring(idx + 1).trim();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String elementId, String name, String value) {
|
||||
dom.setAttribute(elementId, name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAttribute(String elementId, String name) {
|
||||
return dom.getAttribute(elementId, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAttribute(String elementId, String name) {
|
||||
dom.removeAttribute(elementId, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focus(String elementId) {
|
||||
engine.setJavaScriptEnabled(true);
|
||||
engine.executeScript(
|
||||
"document.getElementById('" + elementId + "').focus();"
|
||||
);
|
||||
engine.setJavaScriptEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blur(String elementId) {
|
||||
engine.setJavaScriptEnabled(true);
|
||||
engine.executeScript(
|
||||
"document.getElementById('" + elementId + "').blur();"
|
||||
);
|
||||
engine.setJavaScriptEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scrollIntoView(String elementId) {
|
||||
engine.setJavaScriptEnabled(true);
|
||||
engine.executeScript(
|
||||
"document.getElementById('" + elementId + "').scrollIntoView();"
|
||||
);
|
||||
engine.setJavaScriptEnabled(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int viewportWidth() {
|
||||
return FxThreadBridge.callAndWait(() ->
|
||||
(int) Math.round(view.getWidth())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int viewportHeight() {
|
||||
return FxThreadBridge.callAndWait(() ->
|
||||
(int) Math.round(view.getHeight())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nowMillis() {
|
||||
return System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private static boolean hasClassToken(String classAttr, String cls) {
|
||||
String[] parts = classAttr.trim().split("\\s+");
|
||||
for (String p : parts) {
|
||||
if (p.equals(cls)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static String removeCssProp(String style, String propLower) {
|
||||
if (style == null || style.isBlank()) return "";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
for (String part : style.split(";")) {
|
||||
String p = part.trim();
|
||||
if (p.isEmpty()) continue;
|
||||
int idx = p.indexOf(':');
|
||||
if (idx <= 0) continue;
|
||||
|
||||
String k = p.substring(0, idx).trim().toLowerCase();
|
||||
if (k.equals(propLower)) continue;
|
||||
|
||||
if (!sb.isEmpty()) sb.append(';');
|
||||
sb.append(p);
|
||||
}
|
||||
|
||||
String out = sb.toString().trim();
|
||||
if (!out.isEmpty() && !out.endsWith(";")) out += ";";
|
||||
return out;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
package org.openautonomousconnection.webclient.network;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* An object that is still waited for to be delivered by the server
|
||||
*
|
||||
* @param <T> type of promised object
|
||||
*/
|
||||
public class Promise<T> {
|
||||
@Getter @Setter
|
||||
private T object;
|
||||
|
||||
public Promise() {
|
||||
}
|
||||
|
||||
public Promise(T object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public static <T> Promise<T> yieldPromise(int timeout, T reference) {
|
||||
try (ExecutorService executorService = Executors.newSingleThreadExecutor()) {
|
||||
Callable<T> task = () -> {
|
||||
for(int i = 0; i < timeout; i++) {
|
||||
if(reference == null) {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(reference == null)
|
||||
throw new TimeoutException();
|
||||
|
||||
return reference;
|
||||
};
|
||||
|
||||
Future<T> future = executorService.submit(task);
|
||||
|
||||
executorService.shutdown();
|
||||
|
||||
try {
|
||||
return new Promise<>(future.get());
|
||||
} catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private Class<?> getType() {
|
||||
return this.object.getClass();
|
||||
}
|
||||
}
|
||||
@@ -1,172 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecord;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSResponseStatus;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
||||
import org.openautonomousconnection.webclient.network.type.NTFType;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
|
||||
import javax.swing.text.Document;
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.URL;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.openautonomousconnection.webclient.Main.*;
|
||||
|
||||
/**
|
||||
* Uses the OAC-Protocol networking client
|
||||
* Suppressing "unchecked" warnings because of generic casts
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class WebClient extends ProtocolClient {
|
||||
/**
|
||||
* Global protocol timeout = 30 seconds
|
||||
*/
|
||||
public static final int TIMEOUT = 30;
|
||||
|
||||
|
||||
/**
|
||||
* Connect to INServer with URLs (ip based)
|
||||
* @param ip ip to server
|
||||
* @param tcpPort tcp ins port
|
||||
*/
|
||||
public void connectToINSServer(String ip, int tcpPort) throws NoSuchAlgorithmException, KeyManagementException {
|
||||
|
||||
tcpPort = tcpPort != -1 ? tcpPort : DEFAULT_INS_PORT_TCP;
|
||||
|
||||
// UDP is always preset
|
||||
|
||||
try {
|
||||
this.buildINSConnection();
|
||||
this.getClientINSConnection().connect(ip, tcpPort);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Give promise a non-null reference
|
||||
* @param type type of file
|
||||
* @param object serialized object
|
||||
* @param <T> java object type
|
||||
*/
|
||||
public <T> void fulfillPromise(NTFType type, T object) {
|
||||
if(!this.outstandingPromises.containsKey(type))
|
||||
return;
|
||||
|
||||
Object promisedValue = this.outstandingPromises.get(type);
|
||||
|
||||
if(promisedValue != null)
|
||||
return;
|
||||
|
||||
this.outstandingPromises.replace(type, object);
|
||||
}
|
||||
|
||||
@Getter
|
||||
private final Map<NTFType, Object> outstandingPromises = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Promise a serialized object that will be delivered as a file by the server
|
||||
* @param type type of file
|
||||
* @return object promise
|
||||
* @param <T> java object type
|
||||
*/
|
||||
private <T> Promise<? extends NetTransmitFile<T>> promise(NTFType type) {
|
||||
NetTransmitFile<T> ntf = NetTransmitFile.of(type, null);
|
||||
|
||||
this.outstandingPromises.put(type, ntf);
|
||||
|
||||
return Promise.yieldPromise(TIMEOUT, ntf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(INSResponseStatus insResponseStatus, List<INSRecord> list) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trustINS(String caFingerprint) {
|
||||
//TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean trustNewINSFingerprint(String oldCAFingerprint, String newCAFingerprint) {
|
||||
//TODO
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a get request for a file from the currently connected server
|
||||
* @param type type of file requested (UNKNOWN for files not supported by the protocol)
|
||||
* @param path path to file
|
||||
* @param test idfk
|
||||
* @return resolved file
|
||||
* @param <T> java object type
|
||||
*/
|
||||
public <T> Promise<? extends NetTransmitFile<T>> get(NTFType type, String path, T test) {
|
||||
try {
|
||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
||||
path,
|
||||
WebRequestMethod.GET,
|
||||
Map.of(),
|
||||
null
|
||||
), TransportProtocol.TCP);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
return this.promise(type);
|
||||
}
|
||||
|
||||
// TODO
|
||||
public void post(String path) {
|
||||
try {
|
||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
||||
path,
|
||||
WebRequestMethod.POST,
|
||||
Map.of(),
|
||||
null
|
||||
), TransportProtocol.TCP);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get index.html Document of current site
|
||||
* @return index.html
|
||||
*/
|
||||
public Promise<NetTransmitFile<Document>> getIndex() {
|
||||
try {
|
||||
this.getClientServerConnection().sendPacket(new WebRequestPacket(
|
||||
"index.html",
|
||||
WebRequestMethod.GET,
|
||||
Map.of(),
|
||||
null
|
||||
), TransportProtocol.TCP);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
System.out.println(promise(NTFType.DOCUMENT).getClass().getSimpleName());
|
||||
|
||||
|
||||
|
||||
return null;//(Promise<NetTransmitFile<Document>>) promise(NTFType.DOCUMENT);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 16 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.handlers;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketSendEvent;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.Packet;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||
import org.openautonomousconnection.protocol.packets.OACPacket;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.AuthPacket;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
|
||||
import org.openautonomousconnection.webclient.packetlistener.PacketListener;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ServerPacketHandler extends EventListener {
|
||||
|
||||
private static final List<PacketListener> listeners = new ArrayList<>();
|
||||
|
||||
public static void registerPacketListener(PacketListener packetListener) {
|
||||
listeners.add(packetListener);
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onPacketReceived(C_PacketReadEvent packetReceivedEvent) {
|
||||
|
||||
Packet packet = packetReceivedEvent.getPacket();
|
||||
TransportProtocol transport = packetReceivedEvent.getProtocol();
|
||||
|
||||
switch (packet.getPacketID()) {
|
||||
case 4 -> listeners
|
||||
.forEach(l -> l.onAuthPacketReceived((AuthPacket) packet, transport));
|
||||
case 9 -> listeners
|
||||
.forEach(l -> l.onWebResponsePacketReceived((WebResponsePacket) packet, transport));}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type;
|
||||
|
||||
public enum NTFType {
|
||||
UNKNOWN,
|
||||
STYLESHEET,
|
||||
DOCUMENT,
|
||||
ICON,
|
||||
FILE
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type;
|
||||
|
||||
import org.openautonomousconnection.webclient.network.type.application.NetAppstreamFile;
|
||||
import org.openautonomousconnection.webclient.network.type.image.NetTransmitIcon;
|
||||
import org.openautonomousconnection.webclient.network.type.text.NetTransmitDocument;
|
||||
import org.openautonomousconnection.webclient.network.type.text.NetTransmitStylesheet;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.css.CSSStyleSheet;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
public interface NetTransmitFile<T> {
|
||||
T getNtf();
|
||||
void setNtf(T ntf);
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <T> NetTransmitFile<T> of(NTFType type, T ntf) {
|
||||
return (NetTransmitFile<T>) switch (type) {
|
||||
case FILE -> new NetAppstreamFile((File) ntf);
|
||||
|
||||
case ICON -> new NetTransmitIcon((ImageIcon) ntf);
|
||||
|
||||
case DOCUMENT -> new NetTransmitDocument((Document) ntf);
|
||||
case STYLESHEET -> new NetTransmitStylesheet((CSSStyleSheet) ntf);
|
||||
|
||||
case null, default -> new NetTransmitUnknown(ntf);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
public class NetTransmitUnknown implements NetTransmitFile<Object> {
|
||||
@Getter @Setter
|
||||
private Object ntf;
|
||||
|
||||
public NetTransmitUnknown() {
|
||||
}
|
||||
|
||||
public NetTransmitUnknown(Object ntf) {
|
||||
this.ntf = ntf;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type.application;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class NetAppstreamFile implements NetTransmitFile<File> {
|
||||
@Getter
|
||||
@Setter
|
||||
private File ntf;
|
||||
|
||||
public NetAppstreamFile() {
|
||||
|
||||
}
|
||||
|
||||
public NetAppstreamFile(File ntf) {
|
||||
this.ntf = ntf;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type.image;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class NetTransmitIcon implements NetTransmitFile<ImageIcon> {
|
||||
@Getter
|
||||
@Setter
|
||||
private ImageIcon ntf;
|
||||
|
||||
public NetTransmitIcon() {
|
||||
|
||||
}
|
||||
|
||||
public NetTransmitIcon(ImageIcon ntf) {
|
||||
this.ntf = ntf;
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type.text;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
public class NetTransmitDocument implements NetTransmitFile<Document> {
|
||||
@Getter @Setter
|
||||
private Document ntf;
|
||||
|
||||
public NetTransmitDocument() {
|
||||
|
||||
}
|
||||
|
||||
public NetTransmitDocument(Document ntf) {
|
||||
this.ntf = ntf;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 17 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.type.text;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
import org.w3c.dom.css.CSSStyleSheet;
|
||||
|
||||
public class NetTransmitStylesheet implements NetTransmitFile<CSSStyleSheet> {
|
||||
@Getter
|
||||
@Setter
|
||||
private CSSStyleSheet ntf;
|
||||
|
||||
public NetTransmitStylesheet() {
|
||||
|
||||
}
|
||||
|
||||
public NetTransmitStylesheet(CSSStyleSheet ntf) {
|
||||
this.ntf = ntf;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.website;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.webclient.Main;
|
||||
import org.openautonomousconnection.webclient.network.Promise;
|
||||
import org.openautonomousconnection.webclient.network.type.NTFType;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Contains data about a site
|
||||
* Suppressing "unchecked" warnings because of generic casts
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public final class WebSite {
|
||||
@Getter
|
||||
private final URL infoName;
|
||||
|
||||
@Getter @Setter
|
||||
private Document dom;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public WebSite(URL infoName) {
|
||||
this.infoName = infoName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send new get request
|
||||
*/
|
||||
public Document requestNewDom() {
|
||||
Document test = null;
|
||||
|
||||
Promise<NetTransmitFile<Document>> promise = (Promise<NetTransmitFile<Document>>)
|
||||
Main.client.get(NTFType.DOCUMENT, this.infoName.getPath().toString(), test);
|
||||
return promise.getObject().getNtf();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get favicon of site by attempting to retrieve the favicon.ico
|
||||
*
|
||||
* @return retrieved icon or null
|
||||
*/
|
||||
public Icon getFavIcon() {
|
||||
return getFavIcon(this.infoName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get favicon of site by attempting to retrieve the favicon.ico
|
||||
*
|
||||
* @param infoName info name that leads to the site with the favicon
|
||||
* @return retrieved icon or null
|
||||
*/
|
||||
public static @Nullable Icon getFavIcon(URL infoName) {
|
||||
ImageIcon test = null;
|
||||
|
||||
Promise<NetTransmitFile<ImageIcon>> promise = (Promise<NetTransmitFile<ImageIcon>>)
|
||||
Main.client.get(NTFType.ICON, infoName.getPath().toString(), test);
|
||||
|
||||
return promise.getObject().getNtf();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Get HTML text (dom) of site
|
||||
// *
|
||||
// * @param infoName that leads to the target OAC page
|
||||
// * @return html document
|
||||
// */
|
||||
// public static Document getDom(InfoName infoName) {
|
||||
// Main.client.getDocument();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (obj == null || obj.getClass() != this.getClass()) return false;
|
||||
var that = (WebSite) obj;
|
||||
return Objects.equals(this.infoName, that.infoName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(infoName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WebSite{" +
|
||||
"infoName=" + this.infoName +
|
||||
", dom=" + this.dom +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package org.openautonomousconnection.webclient.network.website.tab;
|
||||
|
||||
import org.openautonomousconnection.webclient.network.website.WebSite;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.net.URL;
|
||||
|
||||
public interface Tab {
|
||||
Icon getFavicon();
|
||||
void setFavicon(Icon favicon);
|
||||
|
||||
URL getInfoName();
|
||||
void setInfoName(URL infoName);
|
||||
|
||||
WebSite getWebSite();
|
||||
void setWebSite(WebSite webSite);
|
||||
|
||||
void refresh() throws Exception;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.network.website.tab;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.webclient.network.WebClient;
|
||||
import org.openautonomousconnection.webclient.network.website.WebSite;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.net.URL;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.openautonomousconnection.webclient.Main.DEFAULT_WEB_PORT_TCP;
|
||||
|
||||
public final class WebTab implements Tab {
|
||||
@Getter @Setter
|
||||
private URL infoName;
|
||||
|
||||
@Getter @Setter
|
||||
private Icon favicon;
|
||||
|
||||
@Getter @Setter
|
||||
private WebSite webSite;
|
||||
|
||||
private WebClient client;
|
||||
|
||||
public WebTab(URL infoName, Icon favicon) throws Exception {
|
||||
refresh();
|
||||
|
||||
this.infoName = infoName;
|
||||
this.favicon = favicon;
|
||||
this.webSite = new WebSite(infoName);
|
||||
}
|
||||
|
||||
public WebTab(URL infoName) throws Exception {
|
||||
refresh();
|
||||
|
||||
this.infoName = infoName;
|
||||
this.favicon = WebSite.getFavIcon(infoName);
|
||||
this.webSite = new WebSite(infoName);
|
||||
}
|
||||
|
||||
public WebTab(WebSite webSite) throws Exception {
|
||||
refresh();
|
||||
|
||||
this.infoName = webSite.getInfoName();
|
||||
this.favicon = webSite.getFavIcon();
|
||||
this.webSite = webSite;
|
||||
}
|
||||
|
||||
public void refresh() throws Exception {
|
||||
|
||||
if (client.getClientServerConnection().isConnected()) client.getClientServerConnection().disconnect();
|
||||
int port = infoName.getPort() == -1 ? DEFAULT_WEB_PORT_TCP : infoName.getPort();
|
||||
client.buildServerConnection(null, true);
|
||||
|
||||
try {
|
||||
client.getClientServerConnection().connect(infoName.getHost(), port);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.webSite.requestNewDom();
|
||||
this.favicon = this.webSite.getFavIcon();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
WebTab webTab = (WebTab) o;
|
||||
return Objects.equals(infoName, webTab.infoName) && Objects.equals(favicon, webTab.favicon) && Objects.equals(webSite, webTab.webSite) && Objects.equals(client, webTab.client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(infoName, favicon, webSite, client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WebTab{" +
|
||||
"infoName=" + infoName +
|
||||
", favicon=" + favicon +
|
||||
", webSite=" + webSite +
|
||||
", client=" + client +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 16 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.packetlistener;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.AuthPacket;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||
|
||||
import java.util.concurrent.TransferQueue;
|
||||
|
||||
public abstract class PacketListener {
|
||||
|
||||
public void onAuthPacketReceived(AuthPacket authPacket, TransportProtocol transport) {
|
||||
}
|
||||
|
||||
public void onAuthPacketSent(AuthPacket authPacket, TransportProtocol transport) {
|
||||
}
|
||||
|
||||
public void onWebResponsePacketReceived(WebResponsePacket webResponsePacket, TransportProtocol transport) {
|
||||
}
|
||||
|
||||
public void onWebRequestPacketSent(WebRequestPacket webRequestPacket, TransportProtocol transport) {
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 16 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.packetlistener.listeners;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||
import org.openautonomousconnection.webclient.Main;
|
||||
import org.openautonomousconnection.webclient.network.type.NTFType;
|
||||
import org.openautonomousconnection.webclient.network.type.NetTransmitFile;
|
||||
import org.openautonomousconnection.webclient.network.type.image.NetTransmitIcon;
|
||||
import org.openautonomousconnection.webclient.network.type.text.NetTransmitDocument;
|
||||
import org.openautonomousconnection.webclient.packetlistener.PacketListener;
|
||||
import org.openautonomousconnection.webclient.ui.dom.DomSerializer;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
public class WebPacketListener extends PacketListener {
|
||||
@Override
|
||||
public void onWebResponsePacketReceived(WebResponsePacket packet, TransportProtocol transport) {
|
||||
NTFType type = NTFType.UNKNOWN;
|
||||
|
||||
NetTransmitFile<?> file = null;
|
||||
|
||||
/*
|
||||
full list of content types at https://repo.open-autonomous-connection.org/open-autonomous-connection/WebServer/src/branch/master/src/main/java/org/openautonomousconnection/webserver/ContentTypeResolver.java
|
||||
*/
|
||||
switch (packet.getContentType()) {
|
||||
case "text/html":
|
||||
type = NTFType.DOCUMENT;
|
||||
|
||||
// body is sent as byte array (string)
|
||||
|
||||
String htmlText = new String(packet.getBody());
|
||||
|
||||
Document dom = DomSerializer.fromString(Main.mainFrame.getDomContainerPanel().getWebEngine(), htmlText);
|
||||
|
||||
file = new NetTransmitDocument();
|
||||
break;
|
||||
|
||||
case "image/png": // TODO: Implement
|
||||
break;
|
||||
|
||||
case "image/ico": // the server doesn't implement this yet, so favicons won't work at this moment
|
||||
type = NTFType.ICON;
|
||||
|
||||
ImageIcon icon = new ImageIcon(packet.getBody());
|
||||
|
||||
file = new NetTransmitIcon(icon);
|
||||
break;
|
||||
|
||||
// the same as application/octet-stream
|
||||
default:
|
||||
break; //TODO: stream files
|
||||
|
||||
}
|
||||
|
||||
Main.client.fulfillPromise(type, file);
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package org.openautonomousconnection.webclient.recode;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.infonamelib.InfoNames;
|
||||
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
||||
import org.openautonomousconnection.oacswing.component.design.Design;
|
||||
import org.openautonomousconnection.oacswing.component.design.DesignManager;
|
||||
import org.openautonomousconnection.protocol.ProtocolBridge;
|
||||
import org.openautonomousconnection.protocol.ProtocolValues;
|
||||
import org.openautonomousconnection.protocol.versions.ProtocolVersion;
|
||||
import org.openautonomousconnection.webclient.recode.settings.INSList;
|
||||
import org.openautonomousconnection.webclient.recode.ui.BrowserUI;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
|
||||
public class Main {
|
||||
@Getter
|
||||
private static ClientImpl client;
|
||||
private static ProtocolBridge bridge;
|
||||
|
||||
@Getter
|
||||
private static BrowserUI ui;
|
||||
|
||||
private static void initProtocol() {
|
||||
InfoNames.registerOACInfoNameProtocols();
|
||||
|
||||
ProtocolValues values = new ProtocolValues();
|
||||
|
||||
values.packetHandler = new PacketHandler();
|
||||
values.eventManager = new EventManager();
|
||||
|
||||
client = new ClientImpl();
|
||||
|
||||
try {
|
||||
bridge = new ProtocolBridge(
|
||||
client,
|
||||
values,
|
||||
ProtocolVersion.PV_1_0_0_BETA,
|
||||
new File("logs")
|
||||
);
|
||||
|
||||
client.buildINSConnection();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
initProtocol();
|
||||
FxBootstrap.ensureInitialized();
|
||||
DesignManager.setGlobalDesign(Design.DARK);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
ui = new BrowserUI();
|
||||
ui.setSize(1200, 800);
|
||||
ui.setLocationRelativeTo(null);
|
||||
ui.setVisible(true);
|
||||
|
||||
try {
|
||||
bridge.getProtocolValues().eventManager.registerListener(client);
|
||||
client.getClientINSConnection().connect(INSList.DEFAULT_INS, INSList.DEFAULT_PORT);
|
||||
} catch (Exception exception) {
|
||||
client.getProtocolBridge().getLogger().exception("Failed to connect to INS", exception);
|
||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to connect to INS Server:\n" + exception.getMessage(),
|
||||
"INS Connection", OACOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
package org.openautonomousconnection.webclient.recode.ui;
|
||||
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.EventListener;
|
||||
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.client.events.packets.C_PacketReadEvent;
|
||||
import dev.unlegitdqrk.unlegitlibrary.network.system.utils.TransportProtocol;
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebHistory;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.openautonomousconnection.infonamelib.InfoNames;
|
||||
import org.openautonomousconnection.oacswing.component.OACOptionPane;
|
||||
import org.openautonomousconnection.oacswing.component.OACPanel;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebRequestPacket;
|
||||
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.INSRecordType;
|
||||
import org.openautonomousconnection.protocol.versions.v1_0_0.beta.WebRequestMethod;
|
||||
import org.openautonomousconnection.webclient.recode.Main;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A Swing panel that embeds a JavaFX WebView.
|
||||
* The JavaFX scene is initialized on first addNotify().
|
||||
*/
|
||||
public final class TabView extends OACPanel {
|
||||
|
||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
private JFXPanel fxPanel;
|
||||
private WebView webView;
|
||||
private WebEngine engine;
|
||||
|
||||
private final Consumer<String> onLocationChanged;
|
||||
|
||||
/**
|
||||
* Creates a new tab view.
|
||||
*
|
||||
* @param onLocationChanged callback invoked when the WebEngine location changes
|
||||
*/
|
||||
public TabView(Consumer<String> onLocationChanged) {
|
||||
super();
|
||||
this.onLocationChanged = Objects.requireNonNull(onLocationChanged, "onLocationChanged");
|
||||
setLayout(new BorderLayout());
|
||||
Main.getClient().getProtocolBridge().getProtocolValues().eventManager.registerListener(new TabListener(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
|
||||
if (!initialized.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fxPanel = new JFXPanel();
|
||||
add(fxPanel, BorderLayout.CENTER);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
webView = new WebView();
|
||||
webView.setContextMenuEnabled(false);
|
||||
webView.getEngine().setJavaScriptEnabled(false);
|
||||
engine = webView.getEngine();
|
||||
|
||||
engine.locationProperty().addListener((obs, oldV, newV) -> {
|
||||
if (newV != null) {
|
||||
onLocationChanged.accept(newV);
|
||||
}
|
||||
});
|
||||
|
||||
fxPanel.setScene(new Scene(webView));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a URL in this tab.
|
||||
*
|
||||
* @param url URL to load
|
||||
*/
|
||||
public void load(String url) {
|
||||
String[] parts = url.split("\\.");
|
||||
if (parts.length < 2 || parts.length > 3) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid INS address format: " + url +
|
||||
" (expected name.tln or sub.name.tln)"
|
||||
);
|
||||
}
|
||||
|
||||
String tln = parts[parts.length - 1];
|
||||
String name = parts[parts.length - 2];
|
||||
String sub = (parts.length == 3) ? parts[0] : null;
|
||||
|
||||
try {
|
||||
Main.getClient().sendINSQuery(tln, name, sub, INSRecordType.A);
|
||||
} catch (Exception e) {
|
||||
Main.getClient().getProtocolBridge().getLogger().exception("Failed to send INS Query", e);
|
||||
OACOptionPane.showMessageDialog(Main.getUi(), "Failed to send INS Query:\n" + e.getMessage(),
|
||||
"INS Connection", OACOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public static class TabListener extends EventListener {
|
||||
private TabView view;
|
||||
|
||||
public TabListener(TabView view) {
|
||||
this.view = view;
|
||||
}
|
||||
|
||||
@Listener
|
||||
public void onListen(C_PacketReadEvent event) {
|
||||
if (event.getPacket() instanceof WebResponsePacket response) {
|
||||
view.parseHtml(new String(response.getBody()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void parseHtml(String html) {
|
||||
Platform.runLater(() -> {
|
||||
if (engine != null) engine.loadContent(html);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the current page.
|
||||
*/
|
||||
public void reload() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine != null) {
|
||||
engine.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates one step back in history if possible.
|
||||
*/
|
||||
public void back() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine == null) return;
|
||||
WebHistory h = engine.getHistory();
|
||||
if (h.getCurrentIndex() > 0) {
|
||||
h.go(-1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates one step forward in history if possible.
|
||||
*/
|
||||
public void forward() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine == null) return;
|
||||
WebHistory h = engine.getHistory();
|
||||
if (h.getCurrentIndex() < h.getEntries().size() - 1) {
|
||||
h.go(1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current location (best-effort, may be null until initialized).
|
||||
*
|
||||
* @return current location or null
|
||||
*/
|
||||
public String getEngineLocation() {
|
||||
// No blocking: best-effort for UI sync.
|
||||
return engine != null ? engine.getLocation() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes JavaFX scene references (best-effort).
|
||||
*/
|
||||
public void dispose() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine != null) {
|
||||
try {
|
||||
engine.load(null);
|
||||
} catch (Exception ignored) {
|
||||
// Best-effort cleanup.
|
||||
}
|
||||
}
|
||||
engine = null;
|
||||
webView = null;
|
||||
if (fxPanel != null) {
|
||||
fxPanel.setScene(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package org.openautonomousconnection.webclient.settings;
|
||||
|
||||
import javafx.concurrent.Worker;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.luaj.vm2.Globals;
|
||||
import org.openautonomousconnection.luascript.fx.FxDomHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxEventHost;
|
||||
import org.openautonomousconnection.luascript.fx.FxWebViewResourceHost;
|
||||
import org.openautonomousconnection.luascript.hosts.HostServices;
|
||||
import org.openautonomousconnection.luascript.runtime.LuaRuntime;
|
||||
import org.openautonomousconnection.luascript.security.LuaExecutionPolicy;
|
||||
import org.openautonomousconnection.luascript.utils.LuaGlobalsFactory;
|
||||
import org.openautonomousconnection.webclient.lua.WebLogger;
|
||||
import org.openautonomousconnection.webclient.lua.hosts.ConsoleHostImpl;
|
||||
import org.openautonomousconnection.webclient.lua.hosts.UiHostImpl;
|
||||
|
||||
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 FxEngine implements AutoCloseable {
|
||||
|
||||
private final WebEngine engine;
|
||||
private final WebView webView;
|
||||
private final LuaExecutionPolicy policy;
|
||||
private final WebLogger logger;
|
||||
|
||||
private final AtomicBoolean bootstrapped = new AtomicBoolean(false);
|
||||
|
||||
private LuaRuntime runtime;
|
||||
|
||||
/**
|
||||
* Creates an integration engine with default UI execution policy.
|
||||
*
|
||||
* @param engine web engine
|
||||
* @param webView web view
|
||||
*/
|
||||
public FxEngine(WebEngine engine, WebView webView, WebLogger logger) {
|
||||
this(engine, webView, LuaExecutionPolicy.uiDefault(), logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an integration engine with a custom execution policy.
|
||||
*
|
||||
* @param engine web engine
|
||||
* @param webView web view
|
||||
* @param policy execution policy
|
||||
*/
|
||||
public FxEngine(WebEngine engine, WebView webView, LuaExecutionPolicy policy, WebLogger logger) {
|
||||
this.engine = Objects.requireNonNull(engine, "engine");
|
||||
this.webView = webView;
|
||||
this.policy = Objects.requireNonNull(policy, "policy");
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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).
|
||||
ConsoleHostImpl console = new ConsoleHostImpl(logger);
|
||||
UiHostImpl uiHost = new UiHostImpl(engine, webView, 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.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
package org.openautonomousconnection.webclient.recode.settings;
|
||||
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
package org.openautonomousconnection.webclient.settings;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import org.openautonomousconnection.oacswing.component.OACFrame;
|
||||
|
||||
public abstract class BrowserFrame extends OACFrame {
|
||||
|
||||
protected BrowserFrame() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
package org.openautonomousconnection.webclient.recode.ui;
|
||||
|
||||
import org.openautonomousconnection.webclient.recode.Main;
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
@@ -22,7 +20,7 @@ public class BrowserTab {
|
||||
*/
|
||||
public BrowserTab(String initialUrl, Consumer<String> onLocationChange) {
|
||||
this.key = Objects.requireNonNull(initialUrl, "initialUrl"); // placeholder key overwritten by BrowserUI
|
||||
this.view = new TabView(onLocationChange);
|
||||
this.view = new TabView(onLocationChange, initialUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,8 +39,8 @@ public class BrowserTab {
|
||||
*
|
||||
* @param url URL
|
||||
*/
|
||||
public void load(String url) {
|
||||
view.load(url);
|
||||
public void loadUrl(String url) {
|
||||
view.loadUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -125,8 +123,8 @@ public class BrowserTab {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(String url) {
|
||||
fixedView.load(url);
|
||||
public void loadUrl(String url) {
|
||||
fixedView.loadUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.openautonomousconnection.webclient.recode.ui;
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import org.openautonomousconnection.oacswing.component.OACButton;
|
||||
import org.openautonomousconnection.oacswing.component.OACFrame;
|
||||
@@ -109,7 +109,7 @@ public class BrowserUI extends OACFrame {
|
||||
if (tab != null) tab.reload();
|
||||
});
|
||||
|
||||
newTabButton.addActionListener(e -> openNewTab("info.oac"));
|
||||
newTabButton.addActionListener(e -> openNewTab("web://info.oac/"));
|
||||
closeTabButton.addActionListener(e -> closeCurrentTab());
|
||||
|
||||
// Create first tab
|
||||
@@ -143,7 +143,7 @@ public class BrowserUI extends OACFrame {
|
||||
getTitleBar().getTabs().setSelectedIndex(idx);
|
||||
|
||||
// Navigate
|
||||
tab.load(url);
|
||||
tab.loadUrl(url);
|
||||
|
||||
// Show content
|
||||
cardLayout.show(pageHost, key);
|
||||
@@ -165,7 +165,7 @@ public class BrowserUI extends OACFrame {
|
||||
|
||||
String url = normalizeUrl(input);
|
||||
addressField.setText(url);
|
||||
tab.load(url);
|
||||
tab.loadUrl(url);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -187,7 +187,7 @@ public class BrowserUI extends OACFrame {
|
||||
|
||||
// If no tabs left, open a new one
|
||||
if (getTitleBar().getTabs().getTabCount() == 0) {
|
||||
openNewTab("info.oac");
|
||||
openNewTab("web://info.oac/");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -225,8 +225,13 @@ public class BrowserUI extends OACFrame {
|
||||
|
||||
private static String normalizeUrl(String input) {
|
||||
String s = input == null ? "" : input.trim();
|
||||
if (s.isEmpty()) return "info.oac";
|
||||
if (s.startsWith("web://")) return s;
|
||||
return "web://" + s;
|
||||
if (s.isEmpty()) return "web://info.oac/";
|
||||
if (s.startsWith("web://")) {
|
||||
// Ensure trailing slash for "host only" URLs
|
||||
String rest = s.substring("web://".length());
|
||||
if (!rest.contains("/")) return s + "/";
|
||||
return s;
|
||||
}
|
||||
return "web://" + s + (s.contains("/") ? "" : "/");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package org.openautonomousconnection.webclient.recode;
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
@@ -1,70 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.webclient.network.website.tab.WebTab;
|
||||
import org.openautonomousconnection.webclient.ui.dom.DOMContainerPanel;
|
||||
|
||||
import java.awt.*;
|
||||
import java.net.URL;
|
||||
|
||||
public final class MainFrame extends BrowserFrame {
|
||||
@Getter
|
||||
private WebTab openWebTab;
|
||||
|
||||
@Getter
|
||||
private final DOMContainerPanel domContainerPanel;
|
||||
|
||||
public MainFrame() {
|
||||
super();
|
||||
|
||||
this.setSize(800, 600);
|
||||
|
||||
try {
|
||||
// TODO this.openTab = new Tab(URI.create("web://127.0.0.1").toURL());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.domContainerPanel = new DOMContainerPanel();
|
||||
this.add(this.domContainerPanel, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.open(this.openWebTab);
|
||||
}
|
||||
|
||||
public void setOpenWebTab(int index) {
|
||||
//TODO
|
||||
}
|
||||
|
||||
public void setOpenWebTab(WebTab webTab) {
|
||||
//TODO
|
||||
// for(TabButton button : this.getTopBar().getTabButtons())
|
||||
// if(button.getTab().equals(tab)) {
|
||||
// this.openTab = button.getTab();
|
||||
// button.grabFocus();
|
||||
// }
|
||||
}
|
||||
|
||||
public void open(URL url) throws Exception {
|
||||
//TODO
|
||||
// TabButton button = new TabButton(url);
|
||||
//
|
||||
// this.getTopBar().addButton(button);
|
||||
//
|
||||
// button.grabFocus();
|
||||
}
|
||||
|
||||
public void open(WebTab webTab) {
|
||||
//TODO
|
||||
// TabButton button = new TabButton(tab);
|
||||
//
|
||||
// this.getTopBar().addButton(button);
|
||||
//
|
||||
// button.grabFocus();
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 18 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.webclient.Main;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
|
||||
public class MenuButton extends JButton {
|
||||
@Getter
|
||||
private ClickAction clickAction;
|
||||
|
||||
public MenuButton(Icon icon, ClickAction clickAction) {
|
||||
this.setIcon(icon);
|
||||
|
||||
this.clickAction = clickAction;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
public MenuButton(String text, ClickAction clickAction) {
|
||||
this.setText(text);
|
||||
|
||||
this.clickAction = clickAction;
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.setBackground(Color.gray);
|
||||
|
||||
this.setForeground(Color.lightGray);
|
||||
|
||||
this.setBorderPainted(false);
|
||||
|
||||
this.addActionListener(e -> {
|
||||
|
||||
|
||||
switch (this.clickAction) {
|
||||
case CLOSE -> Main.mainFrame.dispose();
|
||||
case MINIMIZE -> Main.mainFrame.setState(JFrame.ICONIFIED);
|
||||
case MAXIMIZE -> {
|
||||
if(Main.mainFrame.getExtendedState() == JFrame.NORMAL) {
|
||||
this.setText("(_)");
|
||||
|
||||
Main.mainFrame.setExtendedState(
|
||||
Main.mainFrame.getExtendedState() | JFrame.MAXIMIZED_BOTH
|
||||
);
|
||||
}
|
||||
else {
|
||||
this.setText("[]");
|
||||
Main.mainFrame.setExtendedState(JFrame.NORMAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.addFocusListener(new FocusListener() {
|
||||
@Override
|
||||
public void focusGained(FocusEvent e) {
|
||||
setBorderPainted(false);
|
||||
// setBackground(Color.getHSBColor(0.65f, 0.5f, 0.6f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e) {
|
||||
// setBackground(Color.gray);
|
||||
}
|
||||
});
|
||||
|
||||
this.addMouseListener(new MouseListener() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
setBackground(Color.getHSBColor(0.65f, 0.3f, 0.5f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
setBackground(Color.gray);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public enum ClickAction {
|
||||
CLOSE,
|
||||
MINIMIZE,
|
||||
MAXIMIZE
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 19 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import javax.swing.border.Border;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.RoundRectangle2D;
|
||||
|
||||
public class RoundedBorder implements Border {
|
||||
|
||||
private final int radius;
|
||||
|
||||
public RoundedBorder(int radius) {
|
||||
this.radius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Insets getBorderInsets(Component c) {
|
||||
return new Insets(radius, radius, radius, radius);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBorderOpaque() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
|
||||
Graphics2D g2 = (Graphics2D) g.create();
|
||||
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||||
RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
|
||||
g2.setColor(c.getForeground());
|
||||
g2.draw(new RoundRectangle2D.Double(
|
||||
x, y,
|
||||
width - 1, height - 1,
|
||||
radius, radius
|
||||
));
|
||||
|
||||
g2.dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebHistory;
|
||||
import javafx.scene.web.WebView;
|
||||
import org.openautonomousconnection.oacswing.component.OACPanel;
|
||||
import org.openautonomousconnection.webclient.lua.WebLogger;
|
||||
import org.openautonomousconnection.webclient.settings.FxEngine;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A Swing panel that embeds a JavaFX WebView and runs LuaScript (no JavaScript).
|
||||
*
|
||||
* <p>Loads "web://" URLs so JavaFX can request subresources (CSS, images, href navigations)
|
||||
* through URLConnection (mapped to OAC WebRequestPacket).</p>
|
||||
*/
|
||||
public final class TabView extends OACPanel {
|
||||
|
||||
private final AtomicBoolean initialized = new AtomicBoolean(false);
|
||||
|
||||
private JFXPanel fxPanel;
|
||||
private WebView webView;
|
||||
private WebEngine engine;
|
||||
|
||||
private final Consumer<String> onLocationChanged;
|
||||
private final WebLogger webLogger;
|
||||
|
||||
private volatile FxEngine luaEngine;
|
||||
|
||||
/**
|
||||
* Creates a new tab view.
|
||||
*
|
||||
* @param onLocationChanged callback invoked when the WebEngine location changes
|
||||
* @param url callback invoked on URL changes
|
||||
*/
|
||||
public TabView(Consumer<String> onLocationChanged, String url) {
|
||||
super();
|
||||
this.onLocationChanged = Objects.requireNonNull(onLocationChanged, "onLocationChanged");
|
||||
this.webLogger = new WebLogger(url);
|
||||
setLayout(new BorderLayout());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify() {
|
||||
super.addNotify();
|
||||
|
||||
if (!initialized.compareAndSet(false, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
fxPanel = new JFXPanel();
|
||||
add(fxPanel, BorderLayout.CENTER);
|
||||
|
||||
Platform.runLater(() -> {
|
||||
webView = new WebView();
|
||||
webView.setContextMenuEnabled(false);
|
||||
|
||||
engine = webView.getEngine();
|
||||
engine.setJavaScriptEnabled(false);
|
||||
|
||||
engine.locationProperty().addListener((obs, oldV, newV) -> {
|
||||
if (newV != null) {
|
||||
onLocationChanged.accept(newV);
|
||||
}
|
||||
});
|
||||
|
||||
// Proper Lua integration from your library
|
||||
luaEngine = new FxEngine(engine, webView, webLogger);
|
||||
luaEngine.install();
|
||||
|
||||
fxPanel.setScene(new Scene(webView));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a normalized URL (expected: web://...).
|
||||
*
|
||||
* @param url URL to load
|
||||
*/
|
||||
public void loadUrl(String url) {
|
||||
String u = Objects.requireNonNull(url, "url").trim();
|
||||
if (u.isEmpty()) return;
|
||||
|
||||
Platform.runLater(() -> {
|
||||
if (engine != null) {
|
||||
engine.load(u);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the current page.
|
||||
*/
|
||||
public void reload() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine != null) engine.reload();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates one step back in history if possible.
|
||||
*/
|
||||
public void back() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine == null) return;
|
||||
WebHistory h = engine.getHistory();
|
||||
if (h.getCurrentIndex() > 0) h.go(-1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates one step forward in history if possible.
|
||||
*/
|
||||
public void forward() {
|
||||
Platform.runLater(() -> {
|
||||
if (engine == null) return;
|
||||
WebHistory h = engine.getHistory();
|
||||
if (h.getCurrentIndex() < h.getEntries().size() - 1) h.go(1);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current engine location.
|
||||
*
|
||||
* @return location or null
|
||||
*/
|
||||
public String getEngineLocation() {
|
||||
return engine != null ? engine.getLocation() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes resources.
|
||||
*/
|
||||
public void dispose() {
|
||||
FxEngine le = luaEngine;
|
||||
luaEngine = null;
|
||||
if (le != null) {
|
||||
try {
|
||||
le.close();
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
Platform.runLater(() -> {
|
||||
if (engine != null) {
|
||||
try {
|
||||
engine.load(null);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
engine = null;
|
||||
webView = null;
|
||||
if (fxPanel != null) {
|
||||
fxPanel.setScene(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Jan. 18 2026
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.webclient.Main;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseMotionAdapter;
|
||||
|
||||
@Deprecated(forRemoval = true)
|
||||
public class TopBar extends JPanel {
|
||||
@Getter
|
||||
private MenuButton closeButton, minimizeButton, maximizeButton;
|
||||
|
||||
@Getter
|
||||
private JPanel buttonPanel;
|
||||
|
||||
private Point dragOffset;
|
||||
|
||||
public TopBar() {
|
||||
this.setPreferredSize(new Dimension(0, 40));
|
||||
|
||||
this.buttonPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
|
||||
|
||||
this.closeButton = new MenuButton("X", MenuButton.ClickAction.CLOSE);
|
||||
this.minimizeButton = new MenuButton("-", MenuButton.ClickAction.MINIMIZE);
|
||||
this.maximizeButton = new MenuButton("[]", MenuButton.ClickAction.MAXIMIZE);
|
||||
|
||||
this.buttonPanel.add(this.closeButton);
|
||||
this.buttonPanel.add(this.minimizeButton);
|
||||
this.buttonPanel.add(this.maximizeButton);
|
||||
|
||||
this.init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
Point mouseOnScreen = e.getLocationOnScreen();
|
||||
Point frameLocation = Main.mainFrame.getLocation();
|
||||
|
||||
dragOffset = new Point(
|
||||
mouseOnScreen.x - frameLocation.x,
|
||||
mouseOnScreen.y - frameLocation.y
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
dragOffset = null;
|
||||
}
|
||||
});
|
||||
|
||||
this.addMouseMotionListener(new MouseMotionAdapter() {
|
||||
@Override
|
||||
public void mouseDragged(MouseEvent e) {
|
||||
if (dragOffset != null) {
|
||||
Point mouseOnScreen = e.getLocationOnScreen();
|
||||
|
||||
int newX = mouseOnScreen.x - dragOffset.x;
|
||||
int newY = mouseOnScreen.y - dragOffset.y;
|
||||
|
||||
Main.mainFrame.setLocation(newX, newY);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void initButtonPanel(String alignment) {
|
||||
this.setLayout(new BorderLayout());
|
||||
|
||||
this.add(this.buttonPanel, alignment);
|
||||
|
||||
this.repaint();
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui.dom;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.embed.swing.JFXPanel;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.web.WebEngine;
|
||||
import javafx.scene.web.WebView;
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.oacswing.component.OACPanel;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class DOMContainerPanel extends OACPanel {
|
||||
@Getter
|
||||
private final JFXPanel dom;
|
||||
|
||||
@Getter
|
||||
private WebView webView;
|
||||
|
||||
@Getter
|
||||
private WebEngine webEngine;
|
||||
|
||||
public void loadContent(String html) {
|
||||
this.webEngine.loadContent(html);
|
||||
}
|
||||
|
||||
public void loadContent(Document html) {
|
||||
this.loadContent(DomSerializer.toString(html));
|
||||
}
|
||||
|
||||
public DOMContainerPanel() {
|
||||
this.setBackground(Color.LIGHT_GRAY);
|
||||
|
||||
//TODO: Turn this into designable OAC-JFXpanel
|
||||
this.dom = new JFXPanel();
|
||||
|
||||
Platform.runLater(() -> {
|
||||
this.webView = new WebView();
|
||||
this.webEngine = this.webView.getEngine();
|
||||
this.webEngine.setJavaScriptEnabled(false);
|
||||
this.dom.setScene(new Scene(this.webView));
|
||||
});
|
||||
|
||||
this.add(this.dom);
|
||||
}
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package org.openautonomousconnection.webclient.ui.dom;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import javax.xml.transform.OutputKeys;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.StringWriter;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.web.WebEngine;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Serializes a W3C DOM Document to an HTML string.
|
||||
*
|
||||
* <p>Works with JavaFX WebEngine DOM.</p>
|
||||
*/
|
||||
public final class DomSerializer {
|
||||
|
||||
private DomSerializer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a DOM {@link Document} to a string.
|
||||
*
|
||||
* @param document DOM document
|
||||
* @return serialized HTML
|
||||
*/
|
||||
public static String toString(Document document) {
|
||||
if (document == null) {
|
||||
throw new IllegalArgumentException("document is null");
|
||||
}
|
||||
|
||||
try {
|
||||
TransformerFactory tf = TransformerFactory.newInstance();
|
||||
Transformer transformer = tf.newTransformer();
|
||||
|
||||
transformer.setOutputProperty(OutputKeys.METHOD, "html");
|
||||
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
|
||||
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
|
||||
transformer.setOutputProperty(OutputKeys.INDENT, "no");
|
||||
|
||||
StringWriter writer = new StringWriter();
|
||||
transformer.transform(
|
||||
new DOMSource(document),
|
||||
new StreamResult(writer)
|
||||
);
|
||||
return writer.toString();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Failed to serialize DOM document", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an HTML string into a JavaFX {@link WebEngine} and returns the resulting DOM {@link Document}.
|
||||
*
|
||||
* <p>No JavaScript required. Parsing is done by WebKit.</p>
|
||||
*
|
||||
* @param engine JavaFX WebEngine (must not be null)
|
||||
* @param html HTML source
|
||||
* @return parsed DOM document
|
||||
*/
|
||||
public static Document fromString(WebEngine engine, String html) {
|
||||
Objects.requireNonNull(engine, "engine");
|
||||
Objects.requireNonNull(html, "html");
|
||||
|
||||
if (!Platform.isFxApplicationThread()) {
|
||||
final Document[] result = new Document[1];
|
||||
final RuntimeException[] error = new RuntimeException[1];
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
Platform.runLater(() -> {
|
||||
try {
|
||||
result[0] = fromString(engine, html);
|
||||
} catch (RuntimeException e) {
|
||||
error[0] = e;
|
||||
} finally {
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
if (!latch.await(5, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Timed out while parsing HTML");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("Interrupted while parsing HTML", e);
|
||||
}
|
||||
|
||||
if (error[0] != null) throw error[0];
|
||||
return result[0];
|
||||
}
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
|
||||
engine.getLoadWorker().stateProperty().addListener((obs, o, n) -> {
|
||||
switch (n) {
|
||||
case SUCCEEDED, FAILED, CANCELLED -> latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
engine.loadContent(html, "text/html");
|
||||
|
||||
try {
|
||||
if (!latch.await(5, TimeUnit.SECONDS)) {
|
||||
throw new RuntimeException("Timed out while parsing HTML");
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new RuntimeException("Interrupted while parsing HTML", e);
|
||||
}
|
||||
|
||||
Document doc = engine.getDocument();
|
||||
if (doc == null) {
|
||||
throw new IllegalStateException("WebEngine did not produce a DOM document");
|
||||
}
|
||||
|
||||
return doc;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui.tab;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import org.openautonomousconnection.oacswing.component.OACButton;
|
||||
import org.openautonomousconnection.webclient.network.website.tab.WebTab;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Button that contains Tab data
|
||||
*/
|
||||
@Getter @Setter
|
||||
public class TabButton extends OACButton {
|
||||
private WebTab webTab;
|
||||
|
||||
public TabButton(@NonNull String infoName) throws Exception {
|
||||
this(URI.create(infoName).toURL());
|
||||
}
|
||||
|
||||
public TabButton(@NonNull URL infoName) throws Exception {
|
||||
this.webTab = new WebTab(infoName);
|
||||
}
|
||||
|
||||
public TabButton(@NonNull WebTab webTab) {
|
||||
this.webTab = webTab;
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
/* Author: Maple
|
||||
* Dec. 12 2025
|
||||
* */
|
||||
|
||||
package org.openautonomousconnection.webclient.ui.tab;
|
||||
|
||||
import lombok.Getter;
|
||||
import org.openautonomousconnection.oacswing.component.OACTitleBar;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* View at the top of the screen that contains the tabs
|
||||
* @deprecated the OAC-Swing library's solution fits better and works globally
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.0.0-BETA.1.4")
|
||||
public class TabButtonView extends OACTitleBar {
|
||||
@Getter
|
||||
private final List<TabButton> tabButtons;
|
||||
|
||||
public TabButtonView() {
|
||||
this(new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with preset buttons
|
||||
* @param tabButtons already created buttons
|
||||
*/
|
||||
public TabButtonView(Collection<TabButton> tabButtons) {
|
||||
super(null);
|
||||
this.tabButtons = (List<TabButton>) tabButtons;
|
||||
|
||||
FlowLayout layoutStyle = new FlowLayout(FlowLayout.RIGHT, 0, 0);
|
||||
|
||||
this.setLayout(layoutStyle);
|
||||
|
||||
this.setBackground(Color.gray);
|
||||
}
|
||||
|
||||
public void addButton(TabButton tabButton) {
|
||||
this.tabButtons.add(tabButton);
|
||||
}
|
||||
|
||||
public TabButton getButton(int index) {
|
||||
return this.tabButtons.get(index);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user