From 7a20c3988fae0e5c868b655cbd19b9fe418def50 Mon Sep 17 00:00:00 2001 From: tzdwindows 7 <3076584115@qq.com> Date: Sat, 3 Jan 2026 09:22:09 +0800 Subject: [PATCH] =?UTF-8?q?feat(browser):=20=E6=B7=BB=E5=8A=A0=E6=B3=9B?= =?UTF-8?q?=E5=9E=8B=E6=94=AF=E6=8C=81=E5=92=8C=E6=B5=8F=E8=A7=88=E5=99=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 为 BaseBrowserBuilder 添加泛型参数 R 以支持不同返回类型 - 新增 BrowserLog 日志工具类,提供格式化输出和异常处理 - 实现 BrowserPanel 组件,支持嵌入到 Swing 容器中 - 更新 BrowserCore 支持 Component 父容器参数 - 替换 System.out.println 为 BrowserLog 日志记录 - 修改 Builder 构建方法返回泛型 R 类型 - 添加 JsBridgeController attach 容器绑定功能 - 优化弹窗创建时的父组件尺寸获取逻辑 --- .../box/browser/BaseBrowserBuilder.java | 11 +- .../innovators/box/browser/BrowserCore.java | 149 ++++++++++++------ .../innovators/box/browser/BrowserLog.java | 112 +++++++++++++ .../innovators/box/browser/BrowserPanel.java | 114 ++++++++++++++ .../innovators/box/browser/BrowserWindow.java | 6 +- .../box/browser/BrowserWindowJDialog.java | 6 +- .../innovators/box/browser/CefAppManager.java | 4 - .../browser/bridge/JsBridgeController.java | 13 ++ src/main/resources/build/build.properties | 2 +- 9 files changed, 351 insertions(+), 66 deletions(-) create mode 100644 src/main/java/com/axis/innovators/box/browser/BrowserLog.java create mode 100644 src/main/java/com/axis/innovators/box/browser/BrowserPanel.java diff --git a/src/main/java/com/axis/innovators/box/browser/BaseBrowserBuilder.java b/src/main/java/com/axis/innovators/box/browser/BaseBrowserBuilder.java index bbcd96d..3a43b7f 100644 --- a/src/main/java/com/axis/innovators/box/browser/BaseBrowserBuilder.java +++ b/src/main/java/com/axis/innovators/box/browser/BaseBrowserBuilder.java @@ -5,8 +5,9 @@ import com.axis.innovators.box.events.BrowserCreationCallback; import javax.swing.*; import java.awt.*; +// 增加泛型参数 R,表示 build 方法返回的类型 @SuppressWarnings("unchecked") -public abstract class BaseBrowserBuilder> { +public abstract class BaseBrowserBuilder, R> { protected String windowId; protected String title = "JCEF Window"; protected Dimension size = new Dimension(800, 600); @@ -20,22 +21,22 @@ public abstract class BaseBrowserBuilder> { protected boolean openLinksInExternalBrowser = true; protected BrowserCreationCallback browserCreationCallback; protected JsBridgeController controller; + public BaseBrowserBuilder(String windowId) { this.windowId = windowId; } public T title(String title) { this.title = title; return (T) this; } public T size(int width, int height) { this.size = new Dimension(width, height); return (T) this; } - public T operationHandler(WindowOperationHandler handler) { this.operationHandler = handler; return (T) this; } public T htmlPath(String path) { this.htmlPath = path; return (T) this; } public T htmlUrl(String url) { this.htmlUrl = url; return (T) this; } public T icon(Image icon) { this.icon = icon; return (T) this; } public T resizable(boolean resizable) { this.resizable = resizable; return (T) this; } - public T controller(JsBridgeController controller) {this.controller = controller;return (T) this;} + public T controller(JsBridgeController controller) { this.controller = controller; return (T) this; } public T openLinksInBrowser(boolean openInBrowser) { this.openLinksInExternalBrowser = !openInBrowser; return (T) this; } public T setBrowserCreationCallback(BrowserCreationCallback callback) { this.browserCreationCallback = callback; return (T) this; } - // 抽象构建方法 - public abstract Window build(); + // 抽象构建方法返回泛型 R + public abstract R build(); } \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserCore.java b/src/main/java/com/axis/innovators/box/browser/BrowserCore.java index a0c5fbc..e9e3757 100644 --- a/src/main/java/com/axis/innovators/box/browser/BrowserCore.java +++ b/src/main/java/com/axis/innovators/box/browser/BrowserCore.java @@ -25,7 +25,9 @@ import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; +import java.util.Queue; import java.util.Vector; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.function.Consumer; import static org.cef.callback.CefMenuModel.MenuId.MENU_ID_USER_FIRST; @@ -41,15 +43,24 @@ public class BrowserCore { private CefClient client; private CefBrowser browser; private CefMessageRouter msgRouter; - private final BaseBrowserBuilder config; - private final java.util.Queue pendingScripts = new java.util.concurrent.ConcurrentLinkedQueue<>(); + private final BaseBrowserBuilder config; + private final Queue pendingScripts = new ConcurrentLinkedQueue<>(); private volatile boolean isPageLoaded = false; private JsBridgeController jsController; + private final Component parentComponent; - public BrowserCore(Window parentWindow, String windowId, BaseBrowserBuilder config) { + public BrowserCore(Window parentWindow, String windowId, BaseBrowserBuilder config) { this.parentWindow = parentWindow; this.windowId = windowId; this.config = config; + this.parentComponent = null; + } + + public BrowserCore(Component parentComponent, String windowId, BaseBrowserBuilder config) { + this.parentComponent = parentComponent; + this.windowId = windowId; + this.config = config; + this.parentWindow = null; } public Component initialize() throws MalformedURLException { @@ -76,10 +87,10 @@ public class BrowserCore { String targetUrl; if (config.htmlUrl != null && !config.htmlUrl.isEmpty()) { targetUrl = config.htmlUrl; - System.out.println("Loading URL: " + targetUrl); + BrowserLog.info("Loading URL: {}", targetUrl); } else if (config.htmlPath != null && !config.htmlPath.isEmpty()) { targetUrl = new File(config.htmlPath).toURI().toURL().toString(); - System.out.println("Loading File: " + targetUrl); + BrowserLog.info("Loading File: {}", targetUrl); } else { throw new IllegalArgumentException("URL or HTML path must be provided"); } @@ -102,7 +113,7 @@ public class BrowserCore { if (controller != null) { controller.attach(this); if (browser != null && isPageLoaded) { - System.out.println("🔄 [BrowserCore] 动态更新 JSBridge 控制器,正在重新注入 JS..."); + BrowserLog.bridge(windowId, "update", "{}"); String bridgeScript = controller.generateInjectionJs(); browser.executeJavaScript(bridgeScript, browser.getURL(), 0); } @@ -132,14 +143,14 @@ public class BrowserCore { // 只有主框架加载完毕,且没有发生严重错误,才执行脚本 // 只有主框架加载完毕才注入 if (frame.isMain()) { - System.out.println("✅ [BrowserCore] 页面加载完成 (Code: " + httpStatusCode + ")"); + BrowserLog.info("✅ [BrowserCore] 页面加载完成 (Code: {})",httpStatusCode); isPageLoaded = true; if (jsController != null) { - System.out.println("💉 [BrowserCore] 正在注入 JSBridge 代码..."); + BrowserLog.bridge(windowId, "update", "{}"); String bridgeScript = jsController.generateInjectionJs(); frame.executeJavaScript(bridgeScript, frame.getURL(), 0); } else { - System.err.println("⚠️ [BrowserCore] jsController 为空,未注入 JS 对象"); + BrowserLog.bridge(windowId, "update", "{}"); } while (!pendingScripts.isEmpty()) { String script = pendingScripts.poll(); @@ -147,7 +158,7 @@ public class BrowserCore { try { frame.executeJavaScript(script, frame.getURL(), 0); } catch (Exception e) { - e.printStackTrace(); + BrowserLog.error("❌ [BrowserCore] 脚本执行失败", e); } } } @@ -157,9 +168,7 @@ public class BrowserCore { @Override public void onLoadError(CefBrowser browser, CefFrame frame, ErrorCode errorCode, String errorText, String failedUrl) { if (frame.isMain()) { - System.err.println("❌ [BrowserCore] 页面加载失败: " + errorText); - // 加载失败通常不应该执行脚本,或者你可以选择清空队列 - // pendingScripts.clear(); + BrowserLog.error("❌ [BrowserCore] 错误码: {}", errorCode); } } }); @@ -172,23 +181,26 @@ public class BrowserCore { client.addDisplayHandler(new CefDisplayHandlerAdapter() { @Override public boolean onConsoleMessage(CefBrowser browser, CefSettings.LogSeverity level, String message, String source, int line) { - String symbol = switch (level) { - case LOGSEVERITY_ERROR -> "⛔"; - case LOGSEVERITY_WARNING -> "⚠️"; - case LOGSEVERITY_DEFAULT -> "🐞"; - default -> "ℹ️"; - }; - System.out.printf("[Browser Console] %s %s (Line %d) -> %s%n", symbol, source, line, message); + BrowserLog.console(windowId, level.name(), source, line, message); return false; } @Override public void onTitleChange(CefBrowser browser, String title) { SwingUtilities.invokeLater(() -> { - if (parentWindow instanceof JFrame) { - ((JFrame) parentWindow).setTitle(title); - } else if (parentWindow instanceof JDialog) { - ((JDialog) parentWindow).setTitle(title); + if (parentWindow != null) { + if (parentWindow instanceof JFrame) { + ((JFrame) parentWindow).setTitle(title); + } else if (parentWindow instanceof JDialog) { + ((JDialog) parentWindow).setTitle(title); + } + } else if (parentComponent != null){ + Window window = SwingUtilities.getWindowAncestor(parentComponent); + if (window instanceof JFrame) { + ((JFrame) window).setTitle(title); + } else if (window instanceof JDialog) { + ((JDialog) window).setTitle(title); + } } }); } @@ -234,12 +246,20 @@ public class BrowserCore { String request_url, CefCallback callback) { SwingUtilities.invokeLater(() -> { - int option = JOptionPane.showConfirmDialog(parentWindow, - "证书错误: " + cert_error + "\n是否继续访问?", - "安全警告", - JOptionPane.YES_NO_OPTION, - JOptionPane.WARNING_MESSAGE); - + int option = 0; + if (parentWindow != null) { + option = JOptionPane.showConfirmDialog(parentWindow, + "证书错误: " + cert_error + "\n是否继续访问?", + "安全警告", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + } else if (parentComponent != null) { + option = JOptionPane.showConfirmDialog(parentComponent, + "证书错误: " + cert_error + "\n是否继续访问?", + "安全警告", + JOptionPane.YES_NO_OPTION, + JOptionPane.WARNING_MESSAGE); + } if (option == JOptionPane.YES_OPTION) { // JCEF 122 中 CefCallback.Continue() 无参数,表示"继续/忽略错误" callback.Continue(); @@ -301,7 +321,12 @@ public class BrowserCore { fileChooser.setMultiSelectionEnabled(isMulti); // 打开对话框 - int result = fileChooser.showOpenDialog(parentWindow); + int result = 0; + if (parentWindow != null) { + result = fileChooser.showOpenDialog(parentWindow); + } else if (parentComponent != null) { + result = fileChooser.showOpenDialog(parentComponent); + } if (result == JFileChooser.APPROVE_OPTION) { Vector filePaths = new Vector<>(); if (isMulti) { @@ -360,7 +385,7 @@ public class BrowserCore { browser.executeJavaScript("if (document.activeElement) { document.activeElement.value += '" + escapedText + "'; document.dispatchEvent(new Event('input', { bubbles: true })); }", browser.getURL(), 0); } } catch (Exception e) { - e.printStackTrace(); + BrowserLog.error("Failed to paste content: " , e); } } }); @@ -372,13 +397,27 @@ public class BrowserCore { public boolean onJSDialog(CefBrowser browser, String origin_url, JSDialogType dialog_type, String message_text, String default_prompt_text, CefJSDialogCallback callback, BoolRef suppress_message) { SwingUtilities.invokeLater(() -> { if (dialog_type == JSDialogType.JSDIALOGTYPE_ALERT) { - JOptionPane.showMessageDialog(parentWindow, message_text, "警告", JOptionPane.INFORMATION_MESSAGE); + if (parentWindow != null) { + JOptionPane.showMessageDialog(parentWindow, message_text, "警告", JOptionPane.INFORMATION_MESSAGE); + } else if (parentComponent != null) { + JOptionPane.showMessageDialog(parentComponent, message_text, "警告", JOptionPane.INFORMATION_MESSAGE); + } callback.Continue(true, ""); } else if (dialog_type == JSDialogType.JSDIALOGTYPE_CONFIRM) { - int result = JOptionPane.showConfirmDialog(parentWindow, message_text, "确认", JOptionPane.YES_NO_OPTION); + int result = 0; + if (parentWindow != null) { + result = JOptionPane.showConfirmDialog(parentWindow, message_text, "确认", JOptionPane.YES_NO_OPTION); + } else if (parentComponent != null) { + result = JOptionPane.showConfirmDialog(parentComponent, message_text, "确认", JOptionPane.YES_NO_OPTION); + } callback.Continue(result == JOptionPane.YES_OPTION, ""); } else if (dialog_type == JSDialogType.JSDIALOGTYPE_PROMPT) { - String result = JOptionPane.showInputDialog(parentWindow, message_text, default_prompt_text); + String result = null; + if (parentWindow != null) { + result = JOptionPane.showInputDialog(parentWindow, message_text, default_prompt_text); + } else if (parentComponent != null) { + result = JOptionPane.showInputDialog(parentComponent, message_text, default_prompt_text); + } if (result != null) callback.Continue(true, result); else callback.Continue(false, ""); } @@ -400,14 +439,14 @@ public class BrowserCore { CefDownloadItem downloadItem, CefDownloadItemCallback callback) { if (downloadItem.isComplete()) { - System.out.println("下载完成: " + downloadItem.getFullPath()); + BrowserLog.debug("下载完成: {}" , downloadItem.getFullPath()); } else if (downloadItem.isCanceled()) { - System.out.println("下载取消"); + BrowserLog.debug("下载取消: {}" , downloadItem.getFullPath()); } else { // 获取进度百分比 int percent = downloadItem.getPercentComplete(); - if (percent % 10 == 0) { // 减少日志输出频率 - System.out.println("下载中: " + percent + "%"); + if (percent % 10 == 0) { + BrowserLog.debug("下载中: {}" , downloadItem.getFullPath()); } } } @@ -430,11 +469,19 @@ public class BrowserCore { SwingUtilities.invokeLater(() -> { String popupWindowId = windowId + "_popup_" + System.currentTimeMillis(); WindowRegistry.getInstance().createNewWindow(popupWindowId, popupBuilder -> { - popupBuilder.title("Popup") // 可以优化为获取页面标题 - .size(parentWindow.getWidth(), parentWindow.getHeight()) - .htmlUrl(targetUrl) - .icon(config.icon) - .openLinksInBrowser(true); + if (parentWindow != null) { + popupBuilder.title("Popup") // 可以优化为获取页面标题 + .size(parentWindow.getWidth(), parentWindow.getHeight()) + .htmlUrl(targetUrl) + .icon(config.icon) + .openLinksInBrowser(true); + } else if (parentComponent != null) { + popupBuilder.title("Popup") // 可以优化为获取页面标题 + .size(parentComponent.getWidth(), parentComponent.getHeight()) + .htmlUrl(targetUrl) + .icon(config.icon) + .openLinksInBrowser(true); + } if (config.operationHandler != null) { popupBuilder.operationHandler(config.operationHandler); } @@ -474,8 +521,7 @@ public class BrowserCore { handler.handleOperation(new WindowOperation(operation, targetWindow, callback)); return true; } else { - // 如果没有 handler,但这又是一个 system 请求,则报错或忽略 - System.err.println("收到 system 请求但 handler 为空: " + request); + BrowserLog.warn("收到 system 请求但 handler 为空: {}" , request); callback.failure(404, "No handler for system operation"); return true; } @@ -505,7 +551,7 @@ public class BrowserCore { private void injectJsBridge() { if (jsController != null && browser != null) { String script = jsController.generateInjectionJs(); - System.out.println("💉 [Injecting JS Bridge]"); + BrowserLog.warn("💉 [Injecting JS Bridge]"); browser.executeJavaScript(script, browser.getURL(), 0); } } @@ -514,7 +560,7 @@ public class BrowserCore { try { Desktop.getDesktop().browse(new URI(url)); } catch (Exception e) { - System.err.println("外部浏览器打开失败: " + e.getMessage()); + BrowserLog.error("外部浏览器打开失败: ", e); } } @@ -598,13 +644,12 @@ public class BrowserCore { // 场景A:页面已经加载好了,直接执行 try { browser.getMainFrame().executeJavaScript(script, browser.getMainFrame().getURL(), 0); - System.out.println("⚡ [直接执行] " + script); + BrowserLog.debug("⚡ [直接执行] {}" , script); } catch (Exception e) { - System.err.println("执行JS异常: " + e.getMessage()); + BrowserLog.error("执行JS异常: " , e); } } else { - // 场景B:页面还没好,加入队列,等待 onLoadEnd 自动处理 - System.out.println("⏳ [加入队列] 页面未就绪: " + (script.length() > 20 ? script.substring(0, 20) + "..." : script)); + BrowserLog.debug("⏳ [加入队列] 页面未就绪: {}" , (script.length() > 20 ? script.substring(0, 20) + "..." : script)); pendingScripts.add(script); } }); diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserLog.java b/src/main/java/com/axis/innovators/box/browser/BrowserLog.java new file mode 100644 index 0000000..4e7a645 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/browser/BrowserLog.java @@ -0,0 +1,112 @@ +package com.axis.innovators.box.browser; + +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.spi.ExtendedLogger; + +/** + * 浏览器模块专用日志工具类 + * 基于 Log4j 2 实现,提供格式化输出和异常处理 + */ +public class BrowserLog { + + private static final Logger LOGGER = LogManager.getLogger("BrowserSystem"); + private static final String FQCN = BrowserLog.class.getName(); + + private BrowserLog() {} + + /** + * 内部核心方法:利用 ExtendedLogger 传入 FQCN 来实现“堆栈剥离” + */ + private static void log(Level level, String format, Object... args) { + if (LOGGER instanceof ExtendedLogger) { + // 参数说明: + // FQCN: 告诉 Log4j 忽略这个类 + // level: 级别 + // null: Marker + // format / args: 消息内容 + // null: Throwable (这里统一由专门的 error 重载处理) + ((ExtendedLogger) LOGGER).logIfEnabled(FQCN, level, null, format, args); + } else { + // 兜底逻辑 + LOGGER.log(level, format, args); + } + } + + public static void trace(String format, Object... args) { + log(Level.TRACE, format, args); + } + + public static void debug(String format, Object... args) { + log(Level.DEBUG, format, args); + } + + public static void info(String format, Object... args) { + log(Level.INFO, format, args); + } + + public static void warn(String format, Object... args) { + log(Level.WARN, format, args); + } + + public static void error(String format, Object... args) { + log(Level.ERROR, format, args); + } + + /** + * 带异常堆栈的错误日志,同样会剥离本类层级 + */ + public static void error(String message, Throwable t) { + if (LOGGER instanceof ExtendedLogger) { + ((ExtendedLogger) LOGGER).logIfEnabled(FQCN, Level.ERROR, null, message, t); + } else { + LOGGER.error(message, t); + } + } + + // --- 浏览器特定场景优化 --- + + /** + * 记录来自浏览器内核(JCEF/Chromium)的控制台消息 + * @param windowId 窗口ID,方便追踪是哪个页面 + * @param level 日志等级 (INFO, ERR, etc.) + * @param source 源码文件 + * @param line 行号 + * @param message 消息内容 + */ + public static void console(String windowId, String level, String source, int line, String message) { + String formatted = String.format("[%s][Console][%s] (%s:%d) %s", + windowId, level, source, line, message); + + // 根据 JS 控制台级别映射到 Log4j 级别 + if (level.contains("ERROR")) { + LOGGER.error(formatted); + } else if (level.contains("WARNING")) { + LOGGER.warn(formatted); + } else { + LOGGER.info(formatted); + } + } + + /** + * 记录 JSBridge 的调用过程 + */ + public static void bridge(String windowId, String action, String data) { + LOGGER.debug("[{}][JSBridge] Action: {} | Data: {}", windowId, action, data); + } + + /** + * 记录生命周期事件 + */ + public static void lifecycle(String windowId, String event) { + LOGGER.info("[{}][Lifecycle] {}", windowId, event); + } + + /** + * 核心初始化日志 + */ + public static void core(String message) { + LOGGER.info("[BrowserCore] {}", message); + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserPanel.java b/src/main/java/com/axis/innovators/box/browser/BrowserPanel.java new file mode 100644 index 0000000..39fde12 --- /dev/null +++ b/src/main/java/com/axis/innovators/box/browser/BrowserPanel.java @@ -0,0 +1,114 @@ +package com.axis.innovators.box.browser; + +import com.axis.innovators.box.browser.bridge.JsBridgeController; +import org.cef.browser.CefBrowser; +import org.cef.browser.CefMessageRouter; + +import javax.swing.*; +import java.awt.*; + +/** + * BrowserPanel 是一个可以嵌入到任何 Swing 容器中的浏览器组件 + */ +public class BrowserPanel extends JPanel implements BrowserContainer { + private final BrowserCore browserCore; + private final String windowId; + + public static class Builder extends BaseBrowserBuilder { + public Builder(String windowId) { + super(windowId); + } + + @Override + public BrowserPanel build() { + if ((htmlUrl == null || htmlUrl.isEmpty()) && (htmlPath == null || htmlPath.isEmpty())) { + BrowserLog.core("HTML path or URL cannot be empty"); + } + return new BrowserPanel(this); + } + } + + private BrowserPanel(Builder builder) { + this.windowId = builder.windowId; + this.setLayout(new BorderLayout()); + + this.browserCore = new BrowserCore(this, windowId, builder); + + if (builder.controller != null) { + builder.controller.attach(this); + browserCore.setJsBridgeController(builder.controller); + } + + try { + Component browserComponent = browserCore.initialize(); + if (builder.browserCreationCallback != null) { + boolean handled = builder.browserCreationCallback.onLayoutCustomization( + null, this, browserComponent, builder + ); + if (handled) return; + } + add(browserComponent, BorderLayout.CENTER); + } catch (Exception e) { + e.printStackTrace(); + add(new JLabel("浏览器初始化失败: " + e.getMessage()), BorderLayout.CENTER); + BrowserLog.error("浏览器初始化失败: {} " , e.getMessage()); + } + } + + @Override + public String getWindowId() { + return windowId; + } + + @Override + public CefMessageRouter getMsgRouter() { + return browserCore != null ? browserCore.getMsgRouter() : null; + } + + @Override + public void executingJsCode(String script) { + if (browserCore != null) { + browserCore.executingJsCode(script); + } + } + + @Override + public CefBrowser getBrowser() { + return browserCore != null ? browserCore.getBrowser() : null; + } + + @Override + public void closeWindow() { + if (browserCore != null) { + browserCore.dispose(); + } + WindowRegistry.getInstance().unregisterWindow(windowId); + Container parent = getParent(); + if (parent != null) { + parent.remove(this); + parent.revalidate(); + parent.repaint(); + } + } + + @Override + public void updateTheme() { + if (browserCore != null) { + browserCore.updateTheme(); + } + } + + @Override + public void setController(JsBridgeController controller) { + controller.attach(this); + browserCore.setJsBridgeController(controller); + } + + /** + * 当该组件被销毁时,确保释放浏览器资源 + */ + @Override + public void removeNotify() { + super.removeNotify(); + } +} \ No newline at end of file diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java b/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java index 9fdf371..96f359b 100644 --- a/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java +++ b/src/main/java/com/axis/innovators/box/browser/BrowserWindow.java @@ -12,7 +12,7 @@ public class BrowserWindow extends JFrame implements BrowserContainer { private final BrowserCore browserCore; private final String windowId; - public static class Builder extends BaseBrowserBuilder { + public static class Builder extends BaseBrowserBuilder { public Builder(String windowId) { super(windowId); } @Override @@ -23,7 +23,7 @@ public class BrowserWindow extends JFrame implements BrowserContainer { private void validatePaths() { if ((htmlUrl == null || htmlUrl.isEmpty()) && (htmlPath == null || htmlPath.isEmpty())) { - throw new IllegalArgumentException("HTML path or URL cannot be empty"); + BrowserLog.error("HTML path or URL cannot be empty"); } } } @@ -36,6 +36,7 @@ public class BrowserWindow extends JFrame implements BrowserContainer { this.browserCore = new BrowserCore(this, windowId, builder); if (builder.controller != null) { + builder.controller.attach( this); browserCore.setJsBridgeController(builder.controller); } @@ -144,6 +145,7 @@ public class BrowserWindow extends JFrame implements BrowserContainer { @Override public void setController(JsBridgeController controller) { + controller.attach( this); browserCore.setJsBridgeController(controller); } diff --git a/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java b/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java index d4afe09..0366120 100644 --- a/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java +++ b/src/main/java/com/axis/innovators/box/browser/BrowserWindowJDialog.java @@ -12,7 +12,7 @@ public class BrowserWindowJDialog extends JDialog implements BrowserContainer { private final BrowserCore browserCore; private final String windowId; - public static class Builder extends BaseBrowserBuilder { + public static class Builder extends BaseBrowserBuilder { private JFrame parentFrame; public Builder(String windowId) { super(windowId); } @@ -25,7 +25,7 @@ public class BrowserWindowJDialog extends JDialog implements BrowserContainer { @Override public BrowserWindowJDialog build() { if ((htmlUrl == null || htmlUrl.isEmpty()) && (htmlPath == null || htmlPath.isEmpty())) { - throw new IllegalArgumentException("HTML path or URL cannot be empty"); + BrowserLog.error("HTML path or URL cannot be empty"); } return new BrowserWindowJDialog(this); } @@ -39,6 +39,7 @@ public class BrowserWindowJDialog extends JDialog implements BrowserContainer { this.browserCore = new BrowserCore(this, windowId, builder); if (builder.controller != null) { + builder.controller.attach(this); browserCore.setJsBridgeController(builder.controller); } @@ -141,6 +142,7 @@ public class BrowserWindowJDialog extends JDialog implements BrowserContainer { @Override public void setController(JsBridgeController controller) { + controller.attach(this); browserCore.setJsBridgeController(controller); } diff --git a/src/main/java/com/axis/innovators/box/browser/CefAppManager.java b/src/main/java/com/axis/innovators/box/browser/CefAppManager.java index 561a7f8..9469dc6 100644 --- a/src/main/java/com/axis/innovators/box/browser/CefAppManager.java +++ b/src/main/java/com/axis/innovators/box/browser/CefAppManager.java @@ -84,9 +84,6 @@ public class CefAppManager { String processType, CefCommandLine commandLine ) { - //commandLine.appendSwitch("disable-dev-tools"); - //commandLine.appendSwitch("disable-view-source"); - LanguageManager.loadSavedLanguage(); LanguageManager.Language currentLang = LanguageManager.getLoadedLanguages(); if (currentLang != null){ @@ -98,7 +95,6 @@ public class CefAppManager { commandLine.appendSwitchWithValue("--lang", langCode); commandLine.appendSwitchWithValue("--accept-language", langCode); } - boolean isDarkTheme = isDarkTheme(); if (isDarkTheme) { commandLine.appendSwitch("force-dark-mode"); diff --git a/src/main/java/com/axis/innovators/box/browser/bridge/JsBridgeController.java b/src/main/java/com/axis/innovators/box/browser/bridge/JsBridgeController.java index 9fb0702..0485ea0 100644 --- a/src/main/java/com/axis/innovators/box/browser/bridge/JsBridgeController.java +++ b/src/main/java/com/axis/innovators/box/browser/bridge/JsBridgeController.java @@ -1,5 +1,6 @@ package com.axis.innovators.box.browser.bridge; +import com.axis.innovators.box.browser.BrowserContainer; import com.axis.innovators.box.browser.BrowserCore; import com.google.gson.Gson; import com.google.gson.JsonArray; @@ -21,6 +22,7 @@ public abstract class JsBridgeController { private final Map methodRegistry = new HashMap<>(); private final Gson gson = new Gson(); private BrowserCore browserCore; + private BrowserContainer container; public JsBridgeController() { scanMethods(); @@ -33,6 +35,13 @@ public abstract class JsBridgeController { this.browserCore = core; } + /** + * 绑定 BrowserContainer,用于执行回调或JS注入 + */ + public void attach(BrowserContainer container) { + this.container = container; + } + /** * 扫描子类中所有带 @JsMapping 的方法 */ @@ -166,4 +175,8 @@ public abstract class JsBridgeController { protected BrowserCore getBrowserCore() { return browserCore; } + + protected BrowserContainer getBrowserContainer() { + return container; + } } \ No newline at end of file diff --git a/src/main/resources/build/build.properties b/src/main/resources/build/build.properties index 4cfa6ed..3951860 100644 --- a/src/main/resources/build/build.properties +++ b/src/main/resources/build/build.properties @@ -1,4 +1,4 @@ # Auto-generated build information version=0.0.1 -buildTimestamp=2026-01-03T08:33:49.8039508 +buildTimestamp=2026-01-03T09:20:12.6386031 buildSystem=WINDOWS