--add-exports
java.base/sun.security.x509=ALL-UNNAMED
diff --git a/src/main/java/org/openautonomousconnection/webclient/ClientImpl.java b/src/main/java/org/openautonomousconnection/webclient/ClientImpl.java
index dba0639..036c33b 100644
--- a/src/main/java/org/openautonomousconnection/webclient/ClientImpl.java
+++ b/src/main/java/org/openautonomousconnection/webclient/ClientImpl.java
@@ -1,90 +1,98 @@
package org.openautonomousconnection.webclient;
import dev.unlegitdqrk.unlegitlibrary.event.Listener;
-import org.openautonomousconnection.infonamelib.LibClientImpl;
-import org.openautonomousconnection.infonamelib.OacWebUrlInstaller;
+import lombok.Getter;
import org.openautonomousconnection.oacswing.component.OACOptionPane;
import org.openautonomousconnection.protocol.side.client.ProtocolClient;
import org.openautonomousconnection.protocol.side.client.events.ConnectedToProtocolINSServerEvent;
+import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.LibClientImpl_v1_0_1_B;
+import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.WebFlagInspector;
+import org.openautonomousconnection.protocol.urlhandler.v1_0_1.beta.web.WebRequestContextProvider;
+import org.openautonomousconnection.protocol.versions.v1_0_1.beta.WebPacketHeader;
+import org.openautonomousconnection.webclient.ui.BrowserTab;
-import java.awt.*;
+import java.awt.Component;
+import java.io.ByteArrayOutputStream;
+import java.net.URL;
+import java.util.Map;
import java.util.Objects;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
/**
- * Protocol client implementation for the WebClient.
+ * WebClient Protocol implementation (v1.0.1).
+ *
+ * Implements full stream assembly with strict correlation via:
+ * requestId + tabId + pageId + frameId.
*/
-public class ClientImpl extends ProtocolClient {
+public final class ClientImpl extends ProtocolClient {
+ private static final long MAX_STREAM_BYTES = 64L * 1024L * 1024L; // 64MB safety cap
+ private static final int MAX_CONCURRENT_STREAMS = 256;
+
+ @Getter
private final LibImpl libImpl = new LibImpl();
- private final AtomicBoolean connectedInitialized = new AtomicBoolean(false);
+
+ private final AtomicBoolean serverConnectionInitialized = new AtomicBoolean(false);
private final Component dialogParent;
private final Runnable onServerReady;
public ClientImpl(Component dialogParent, Runnable onServerReady) {
this.dialogParent = dialogParent;
- this.onServerReady = Objects.requireNonNull(onServerReady, "onServerReady");
+ this.onServerReady = Objects.requireNonNull(onServerReady);
}
@Override
public boolean trustINS(String caFingerprint) {
Object[] options = {"Continue", "Cancel"};
- int result = OACOptionPane.showOptionDialog(
+ return OACOptionPane.showOptionDialog(
dialogParent,
- "You never connected to this INS before!\n" +
- "Fingerprint: " + caFingerprint + "\nDo you want to connect?",
+ "Fingerprint: " + caFingerprint + "\nContinue?",
"INS Connection",
OACOptionPane.YES_NO_OPTION,
OACOptionPane.INFORMATION_MESSAGE,
null,
options,
options[0]
- );
-
- return result == 0;
+ ) == 0;
}
@Override
public boolean trustNewINSFingerprint(String oldCAFingerprint, String newCAFingerprint) {
Object[] options = {"Continue", "Cancel"};
-
- int result = OACOptionPane.showOptionDialog(
+ return OACOptionPane.showOptionDialog(
dialogParent,
- "The fingerprint does not match with the saved fingerprint!\n" +
- "Saved Fingerprint: " + oldCAFingerprint + "\n" +
- "New Fingerprint: " + newCAFingerprint + "\n" +
- "Do you want to connect?",
+ "Saved: " + oldCAFingerprint + "\nNew: " + newCAFingerprint + "\nContinue?",
"INS Connection",
OACOptionPane.YES_NO_OPTION,
OACOptionPane.INFORMATION_MESSAGE,
null,
options,
options[0]
- );
-
- return result == 0;
+ ) == 0;
}
@Listener
public void onConnected(ConnectedToProtocolINSServerEvent event) {
try {
- buildServerConnection(null, getProtocolBridge().getProtocolValues().ssl);
- OacWebUrlInstaller.installOnce(getProtocolBridge().getProtocolValues().eventManager, this, libImpl);
- if (connectedInitialized.compareAndSet(false, true)) {
+ if (serverConnectionInitialized.compareAndSet(false, true)) {
+ buildServerConnection(null, getProtocolBridge().getProtocolValues().ssl);
onServerReady.run();
}
} catch (Exception e) {
- getProtocolBridge().getLogger().exception("Failed to build Server connection", e);
- OACOptionPane.showMessageDialog(
- dialogParent,
- "Failed to to build Server connection:\n" + e.getMessage(),
- "Server Connection",
- OACOptionPane.ERROR_MESSAGE
- );
+ serverConnectionInitialized.set(false);
+ throw new RuntimeException(e);
}
}
- private class LibImpl extends LibClientImpl {
+ public final class LibImpl extends LibClientImpl_v1_0_1_B {
+
+ private final WebRequestContextProvider provider = new WebRequestContextProvider.Default();
+ private final WebFlagInspector inspector = new WebFlagInspector.Default();
+ private final ConcurrentHashMap streams = new ConcurrentHashMap<>();
+
+ private BrowserTab currentTab;
+
@Override
public void serverConnectionFailed(Exception exception) {
getProtocolBridge().getLogger().exception("Failed to connect to server", exception);
@@ -95,5 +103,141 @@ public class ClientImpl extends ProtocolClient {
OACOptionPane.ERROR_MESSAGE
);
}
+
+ public void bindTab(BrowserTab tab) {
+ this.currentTab = tab;
+ }
+
+ @Override
+ public boolean isStream(WebPacketHeader header) {
+ return inspector.isStream(header);
+ }
+
+ @Override
+ public WebRequestContext contextFor(URL url) {
+ return provider.contextFor(url);
+ }
+
+ @Override
+ public void streamStart(WebPacketHeader header,
+ int statusCode,
+ String contentType,
+ Map headers,
+ long totalLength) {
+
+ if (streams.size() >= MAX_CONCURRENT_STREAMS) {
+ throw new IllegalStateException("Too many concurrent streams");
+ }
+
+ StreamKey key = new StreamKey(header);
+ streams.put(key, new StreamState(statusCode, contentType, headers, totalLength));
+ }
+
+ @Override
+ public void streamChunk(WebPacketHeader header, int seq, byte[] data) {
+
+ StreamState state = streams.get(new StreamKey(header));
+ if (state == null) {
+ throw new IllegalStateException("Chunk without streamStart");
+ }
+
+ state.append(seq, data);
+ }
+
+ @Override
+ public void streamEnd(WebPacketHeader header, boolean ok, String error) {
+
+ StreamState state = streams.get(new StreamKey(header));
+ if (state != null) {
+ state.markEnd(ok, error);
+ }
+ }
+
+ @Override
+ public void streamFinish(WebPacketHeader header, byte[] ignored) {
+
+ StreamKey key = new StreamKey(header);
+ StreamState state = streams.remove(key);
+ if (state == null) return;
+
+ byte[] content = state.finish();
+
+ if (currentTab != null) {
+ currentTab.handleStreamFinished(
+ header.getRequestId(),
+ header.getTabId(),
+ header.getPageId(),
+ header.getFrameId(),
+ state.statusCode,
+ state.contentType,
+ state.headers,
+ content
+ );
+ }
+ }
}
-}
+
+ private record StreamKey(long requestId, long tabId, long pageId, long frameId) {
+
+ StreamKey(WebPacketHeader h) {
+ this(h.getRequestId(), h.getTabId(), h.getPageId(), h.getFrameId());
+ }
+ }
+
+ private static final class StreamState {
+
+ private final int statusCode;
+ private final String contentType;
+ private final Map headers;
+ private final long declaredLength;
+
+ private final ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ private int expectedSeq = 0;
+ private long written = 0;
+ private boolean ended = false;
+ private boolean ok = true;
+
+ StreamState(int statusCode,
+ String contentType,
+ Map headers,
+ long declaredLength) {
+
+ this.statusCode = statusCode;
+ this.contentType = contentType == null ? "application/octet-stream" : contentType;
+ this.headers = headers == null ? Map.of() : Map.copyOf(headers);
+ this.declaredLength = declaredLength;
+ }
+
+ void append(int seq, byte[] data) {
+
+ if (ended) throw new IllegalStateException("Chunk after end");
+ if (seq != expectedSeq) throw new IllegalStateException("Out-of-order chunk");
+
+ expectedSeq++;
+
+ if (data == null || data.length == 0) return;
+
+ written += data.length;
+ if (written > MAX_STREAM_BYTES)
+ throw new IllegalStateException("Stream exceeds limit");
+
+ buffer.writeBytes(data);
+ }
+
+ void markEnd(boolean ok, String error) {
+ this.ended = true;
+ this.ok = ok;
+ }
+
+ byte[] finish() {
+ if (!ok) return new byte[0];
+ byte[] data = buffer.toByteArray();
+
+ if (declaredLength > 0 && data.length != declaredLength) {
+ // tolerated but can log if needed
+ }
+
+ return data;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/openautonomousconnection/webclient/Main.java b/src/main/java/org/openautonomousconnection/webclient/Main.java
index 07cc55e..70602a1 100644
--- a/src/main/java/org/openautonomousconnection/webclient/Main.java
+++ b/src/main/java/org/openautonomousconnection/webclient/Main.java
@@ -1,6 +1,8 @@
package org.openautonomousconnection.webclient;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
+import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
+import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import org.openautonomousconnection.oacswing.component.design.Design;
import org.openautonomousconnection.oacswing.component.design.DesignManager;
import org.openautonomousconnection.webclient.settings.AppSettings;
@@ -8,6 +10,7 @@ import org.openautonomousconnection.webclient.settings.SettingsManager;
import org.openautonomousconnection.webclient.ui.BrowserUI;
import javax.swing.*;
+import java.io.File;
import java.io.IOException;
import java.net.CookieHandler;
import java.net.CookieManager;
@@ -23,6 +26,8 @@ public class Main {
private static AppSettings settings;
private static AddonLoader addonLoader;
+ private static Logger logger;
+ private static EventManager eventManager;
public static BrowserUI getUi() {
return ui;
@@ -32,6 +37,14 @@ public class Main {
return settings;
}
+ public static AddonLoader getAddonLoader() {
+ return addonLoader;
+ }
+
+ public static Logger getLogger() {
+ return logger;
+ }
+
private static void installDefaultCookieManager() {
if (CookieHandler.getDefault() != null) return;
@@ -40,7 +53,10 @@ public class Main {
CookieHandler.setDefault(cm);
}
- public static void main(String[] args) throws IOException {
+ public static void main(String[] args) throws IOException, NoSuchFieldException, IllegalAccessException {
+ eventManager = new EventManager();
+ logger = new Logger(new File("logs", "client"), false, true);
+ addonLoader = new AddonLoader(eventManager, logger);
settings = SettingsManager.load();
FxBootstrap.ensureInitialized();
diff --git a/src/main/java/org/openautonomousconnection/webclient/ui/BrowserTab.java b/src/main/java/org/openautonomousconnection/webclient/ui/BrowserTab.java
index 71692c3..60af345 100644
--- a/src/main/java/org/openautonomousconnection/webclient/ui/BrowserTab.java
+++ b/src/main/java/org/openautonomousconnection/webclient/ui/BrowserTab.java
@@ -26,7 +26,16 @@ import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.awt.*;
+import java.io.File;
+import java.io.IOException;
import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.time.Instant;
+import java.util.Base64;
+import java.util.Locale;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -64,8 +73,14 @@ public final class BrowserTab extends OACPanel {
* @param onLocationChange callback invoked on URL changes
* @param luaEnabled whether Lua is enabled for this tab
* @param luaPolicy execution policy for Lua
+ * @param protocolClient protocol client used for OAC network requests
*/
- public BrowserTab(String key, String initialUrl, Consumer onLocationChange, boolean luaEnabled, LuaExecutionPolicy luaPolicy, ClientImpl protocolClient) {
+ public BrowserTab(String key,
+ String initialUrl,
+ Consumer onLocationChange,
+ boolean luaEnabled,
+ LuaExecutionPolicy luaPolicy,
+ ClientImpl protocolClient) {
super();
this.key = Objects.requireNonNull(key, "key");
this.onLocationChanged = Objects.requireNonNull(onLocationChange, "onLocationChange");
@@ -98,6 +113,11 @@ public final class BrowserTab extends OACPanel {
return key;
}
+ /**
+ * Returns the protocol client used by this tab.
+ *
+ * @return protocol client
+ */
public ClientImpl getProtocolClient() {
return protocolClient;
}
@@ -270,6 +290,78 @@ public final class BrowserTab extends OACPanel {
}
}
+ /**
+ * Receives a fully assembled stream payload from the protocol layer and renders or saves it.
+ *
+ * Rules:
+ *
+ * - Renderable: HTML/text/images/pdf are rendered directly (no wrapper pages for non-html).
+ * - Not renderable: opens "Save As" dialog.
+ * - If user cancels "Save As": content is shown raw in the browser via data: URL.
+ *
+ *
+ * @param requestId request correlation id
+ * @param tabId protocol tab id
+ * @param pageId protocol page id
+ * @param frameId protocol frame id
+ * @param statusCode http-like status code
+ * @param contentType mime type
+ * @param headers response headers
+ * @param content full payload bytes
+ */
+ public void handleStreamFinished(long requestId,
+ long tabId,
+ long pageId,
+ long frameId,
+ int statusCode,
+ String contentType,
+ Map headers,
+ byte[] content) {
+
+ String ct = (contentType == null || contentType.isBlank())
+ ? "application/octet-stream"
+ : contentType.trim();
+
+ byte[] data = (content == null) ? new byte[0] : content;
+
+ // ---- Renderable types -> render without extra wrapper pages ----
+ if (isHtml(ct)) {
+ Charset cs = charsetFromContentType(ct, StandardCharsets.UTF_8);
+ String html = new String(data, cs);
+ Platform.runLater(() -> {
+ WebEngine e = engine;
+ if (e != null) e.loadContent(html, "text/html");
+ });
+ return;
+ }
+
+ if (isText(ct)) {
+ Charset cs = charsetFromContentType(ct, StandardCharsets.UTF_8);
+ String text = new String(data, cs);
+ Platform.runLater(() -> {
+ WebEngine e = engine;
+ if (e != null) e.loadContent(text, "text/plain");
+ });
+ return;
+ }
+
+ if (isImage(ct) || isPdf(ct)) {
+ renderRawDataUrl(ct, data);
+ return;
+ }
+
+ // ---- Not renderable -> Save As; if cancelled -> raw data: URL ----
+ String suggested = extractFilenameFromContentDisposition(headers);
+ if (suggested == null || suggested.isBlank()) {
+ String ext = extensionFromContentType(ct);
+ suggested = "download_" + requestId + "_" + Instant.now().toEpochMilli() + ext;
+ } else {
+ suggested = sanitizeFilename(suggested);
+ }
+
+ showSaveAsDialogAndWriteBytes(suggested, ct, data);
+ }
+
/**
* Releases resources.
*/
@@ -377,4 +469,179 @@ public final class BrowserTab extends OACPanel {
// Best-effort shutdown.
}
}
-}
+
+ // -------------------- Stream render/save helpers --------------------
+
+ private void showSaveAsDialogAndWriteBytes(String suggestedFilename, String contentType, byte[] data) {
+ SwingUtilities.invokeLater(() -> {
+ Window parent = SwingUtilities.getWindowAncestor(this);
+
+ JFileChooser chooser = new JFileChooser();
+ chooser.setDialogTitle("Save As");
+ chooser.setSelectedFile(new File(suggestedFilename));
+
+ int result = chooser.showSaveDialog(parent);
+ if (result != JFileChooser.APPROVE_OPTION) {
+ // Cancel -> show raw in browser
+ renderRawDataUrl(contentType, data);
+ return;
+ }
+
+ File file = chooser.getSelectedFile();
+ if (file == null) {
+ renderRawDataUrl(contentType, data);
+ return;
+ }
+
+ if (file.isDirectory()) {
+ JOptionPane.showMessageDialog(parent, "Please choose a file, not a directory.", "Save As", JOptionPane.WARNING_MESSAGE);
+ renderRawDataUrl(contentType, data);
+ return;
+ }
+
+ if (file.exists()) {
+ int overwrite = JOptionPane.showConfirmDialog(
+ parent,
+ "File already exists. Overwrite?\n" + file.getAbsolutePath(),
+ "Confirm Overwrite",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.WARNING_MESSAGE
+ );
+ if (overwrite != JOptionPane.YES_OPTION) {
+ renderRawDataUrl(contentType, data);
+ return;
+ }
+ }
+
+ try {
+ Files.write(file.toPath(), data);
+ } catch (IOException ex) {
+ JOptionPane.showMessageDialog(
+ parent,
+ "Failed to save file:\n" + ex.getMessage(),
+ "Save As",
+ JOptionPane.ERROR_MESSAGE
+ );
+ // On failure, still show raw so user can at least see bytes
+ renderRawDataUrl(contentType, data);
+ }
+ });
+ }
+
+ private void renderRawDataUrl(String contentType, byte[] data) {
+ String mime = normalizeMime(contentType);
+ String b64 = Base64.getEncoder().encodeToString(data == null ? new byte[0] : data);
+ String dataUrl = "data:" + mime + ";base64," + b64;
+
+ Platform.runLater(() -> {
+ WebEngine e = engine;
+ if (e != null) e.load(dataUrl);
+ });
+ }
+
+ private static String normalizeMime(String contentType) {
+ String ct = (contentType == null || contentType.isBlank()) ? "application/octet-stream" : contentType.trim();
+ int semi = ct.indexOf(';');
+ String base = (semi >= 0 ? ct.substring(0, semi) : ct).trim();
+ return base.isEmpty() ? "application/octet-stream" : base;
+ }
+
+ private static boolean isHtml(String contentType) {
+ String ct = normalizeMime(contentType).toLowerCase(Locale.ROOT);
+ return ct.equals("text/html") || ct.equals("application/xhtml+xml");
+ }
+
+ private static boolean isText(String contentType) {
+ String ct = normalizeMime(contentType).toLowerCase(Locale.ROOT);
+ return ct.startsWith("text/") || ct.equals("application/json") || ct.equals("application/xml") || ct.endsWith("+json") || ct.endsWith("+xml");
+ }
+
+ private static boolean isImage(String contentType) {
+ String ct = normalizeMime(contentType).toLowerCase(Locale.ROOT);
+ return ct.startsWith("image/");
+ }
+
+ private static boolean isPdf(String contentType) {
+ String ct = normalizeMime(contentType).toLowerCase(Locale.ROOT);
+ return ct.equals("application/pdf");
+ }
+
+ private static Charset charsetFromContentType(String contentType, Charset def) {
+ if (contentType == null) return def;
+ String[] parts = contentType.split(";");
+ for (String p : parts) {
+ String s = p.trim();
+ if (s.toLowerCase(Locale.ROOT).startsWith("charset=")) {
+ String name = s.substring("charset=".length()).trim();
+ try {
+ return Charset.forName(name);
+ } catch (Exception ignored) {
+ return def;
+ }
+ }
+ }
+ return def;
+ }
+
+ private static String extractFilenameFromContentDisposition(Map headers) {
+ if (headers == null || headers.isEmpty()) return null;
+
+ String cd = null;
+ for (Map.Entry e : headers.entrySet()) {
+ if (e.getKey() != null && e.getKey().equalsIgnoreCase("content-disposition")) {
+ cd = e.getValue();
+ break;
+ }
+ }
+ if (cd == null || cd.isBlank()) return null;
+
+ String lower = cd.toLowerCase(Locale.ROOT);
+ int fn = lower.indexOf("filename=");
+ if (fn < 0) return null;
+
+ String v = cd.substring(fn + "filename=".length()).trim();
+ if (v.startsWith("\"")) {
+ int end = v.indexOf('"', 1);
+ if (end > 1) return v.substring(1, end);
+ return null;
+ }
+ int semi = v.indexOf(';');
+ if (semi >= 0) v = v.substring(0, semi).trim();
+ return v.isBlank() ? null : v;
+ }
+
+ private static String sanitizeFilename(String name) {
+ String s = name.replace('\\', '_').replace('/', '_');
+ s = s.replace("..", "_");
+ s = s.replace(':', '_').replace('*', '_').replace('?', '_').replace('"', '_')
+ .replace('<', '_').replace('>', '_').replace('|', '_');
+ return s.isBlank() ? "download.bin" : s;
+ }
+
+ private static String extensionFromContentType(String contentType) {
+ String ct = normalizeMime(contentType).toLowerCase(Locale.ROOT);
+
+ if (ct.equals("application/pdf")) return ".pdf";
+ if (ct.equals("application/zip")) return ".zip";
+ if (ct.equals("application/x-7z-compressed")) return ".7z";
+ if (ct.equals("application/x-rar-compressed")) return ".rar";
+ if (ct.equals("application/gzip")) return ".gz";
+ if (ct.equals("application/json")) return ".json";
+ if (ct.equals("application/xml") || ct.endsWith("+xml")) return ".xml";
+
+ if (ct.startsWith("image/")) {
+ int slash = ct.indexOf('/');
+ if (slash > 0 && slash < ct.length() - 1) {
+ String ext = ct.substring(slash + 1).trim();
+ if (!ext.isEmpty()) return "." + ext;
+ }
+ }
+
+ if (ct.startsWith("text/")) return ".txt";
+ return ".bin";
+ }
+
+ public void bindProtocolClient() {
+ protocolClient.getLibImpl().bindTab(this);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/openautonomousconnection/webclient/ui/BrowserUI.java b/src/main/java/org/openautonomousconnection/webclient/ui/BrowserUI.java
index 01c46be..d4686b8 100644
--- a/src/main/java/org/openautonomousconnection/webclient/ui/BrowserUI.java
+++ b/src/main/java/org/openautonomousconnection/webclient/ui/BrowserUI.java
@@ -3,13 +3,13 @@ package org.openautonomousconnection.webclient.ui;
import dev.unlegitdqrk.unlegitlibrary.addon.AddonLoader;
import dev.unlegitdqrk.unlegitlibrary.event.EventManager;
import dev.unlegitdqrk.unlegitlibrary.network.system.packets.PacketHandler;
-import dev.unlegitdqrk.unlegitlibrary.utils.Logger;
import org.openautonomousconnection.oacswing.component.*;
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.ClientImpl;
+import org.openautonomousconnection.webclient.Main;
import org.openautonomousconnection.webclient.settings.AppSettings;
import org.openautonomousconnection.webclient.settings.HistoryStore;
import org.openautonomousconnection.webclient.settings.InsEndpoint;
@@ -247,6 +247,9 @@ public class BrowserUI extends OACFrame {
client
);
+ // Bind stream callbacks to this tab (v1.0.1 stream assembly -> tab render).
+ tab.bindProtocolClient();
+
tabRef.set(tab);
tab.setOpenInNewTabCallback(() -> openNewTab(
tab.getEngineLocation() == null ? settings.getStartPageUrl() : tab.getEngineLocation()
@@ -285,14 +288,15 @@ public class BrowserUI extends OACFrame {
values.packetHandler = new PacketHandler();
values.eventManager = new EventManager();
values.ssl = settings.isSslEnabled();
- AddonLoader addonLoader = new AddonLoader(values.eventManager,
- new Logger(new File(logsFolder, "addons"), false, true));
+ AddonLoader addonLoader = Main.getAddonLoader();
ProtocolBridge bridge = new ProtocolBridge(
client,
values,
- ProtocolVersion.PV_1_0_0_BETA,
- new File(logsFolder, "client")
+ ProtocolVersion.PV_1_0_1_BETA,
+ Main.getLogger(),
+ Main.getAddonLoader(),
+ client.getLibImpl()
);
protocolByKey.put(key, bridge);
@@ -351,12 +355,6 @@ public class BrowserUI extends OACFrame {
}
}
- private void closeCurrentTab() {
- String key = getSelectedTabKey();
- if (key == null) return;
- closeTabByKey(key);
- }
-
private void closeTabByKey(String key) {
int idx = findTabIndexByKey(key);
if (idx < 0) return;
@@ -417,12 +415,9 @@ public class BrowserUI extends OACFrame {
if (idx < 0) return;
if (plusTabSupport.isPlusTab(idx)) {
- if (suppressPlusAutoOpen) {
- return;
- }
- if (tabsByKey.isEmpty()) {
- return;
- }
+ if (suppressPlusAutoOpen) return;
+ if (tabsByKey.isEmpty()) return;
+
handlingTabSwitch = true;
try {
int fallback = lastSelectedRealTab >= 0
@@ -452,12 +447,22 @@ public class BrowserUI extends OACFrame {
}
}
+ /**
+ * Returns the currently active tab (excluding the plus-tab).
+ *
+ * @return current tab or null
+ */
public BrowserTab getCurrentTab() {
String key = getSelectedTabKey();
if (key == null) return null;
return tabsByKey.get(key);
}
+ /**
+ * Returns the addon loader associated with the current tab.
+ *
+ * @return addon loader or null
+ */
public AddonLoader getCurrentAddonLoader() {
String key = getSelectedTabKey();
if (key == null) return null;
@@ -470,4 +475,4 @@ public class BrowserUI extends OACFrame {
if (plusTabSupport.isPlusTab(idx)) return null;
return getTitleBar().getTabs().getTitleAt(idx);
}
-}
+}
\ No newline at end of file