From a37caebf7febff2dbdf0332bbd26ba39239335ff Mon Sep 17 00:00:00 2001 From: lemon Date: Fri, 14 Mar 2025 12:18:16 +0800 Subject: [PATCH 1/4] =?UTF-8?q?REPORT-146147=20feat:=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E5=86=85=E5=B5=8C=E5=BC=8F=E5=AE=B9=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/start/server/FineEmbedServerActivator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java b/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java index 8360989818..ab17ab15eb 100644 --- a/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java +++ b/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java @@ -2,6 +2,7 @@ package com.fr.start.server; import com.fr.cbb.websocket.core.WebSocketEndpoint; import com.fr.design.DesignerEnvManager; +import com.fr.event.EventDispatcher; import com.fr.log.FineLoggerFactory; import com.fr.module.Activator; import com.fr.module.ModuleRole; @@ -12,6 +13,7 @@ import com.fr.startup.FineWebApplicationInitializer; import com.fr.third.guava.collect.Sets; import com.fr.third.springframework.web.SpringServletContainerInitializer; import com.fr.third.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import com.fr.web.utils.event.WebEvent; import com.fr.workspace.WorkContext; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; @@ -41,6 +43,7 @@ public class FineEmbedServerActivator extends Activator { //初始化tomcat initTomcat(); tomcat.start(); + EventDispatcher.asyncFire(WebEvent.WEB_STATUS_CHANGE); } catch (LifecycleException e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); @@ -56,6 +59,7 @@ public class FineEmbedServerActivator extends Activator { stopSpring(); stopServerActivator(); stopTomcat(); + EventDispatcher.asyncFire(WebEvent.WEB_STATUS_CHANGE); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } From 9b619cceb7ba4bb810bee664ed7d55f13392e274 Mon Sep 17 00:00:00 2001 From: Zhanying <2446962908@qq.com> Date: Thu, 20 Mar 2025 14:16:27 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20frm=E5=BC=95=E5=AF=BCfvs=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E9=93=BE=E6=8E=A5=E6=94=AF=E6=8C=81=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E7=9A=84=E5=B8=AE=E5=8A=A9=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=20#REPORT-150676?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/design/mainframe/guide/FvsGuidePane.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java b/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java index ee6f0d3dc7..a401cb251d 100644 --- a/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java +++ b/designer-form/src/main/java/com/fr/design/mainframe/guide/FvsGuidePane.java @@ -43,8 +43,6 @@ public class FvsGuidePane extends JPanel { private static final Color BORDER_COLOR = new Color(255, 229, 143); private static final Icon TIP_ICON = UIManager.getIcon("OptionPane.circularWarningIcon"); private static final Icon CLOSE_ICON = IconUtils.readIcon("/com/fr/design/standard/close/close"); - // 引导URL - private static final String GUIDE_URL = "https://help.fanruan.com/finereport/doc-view-4222.html?source=3"; private static final int MAX_PANE_HEIGHT = 80; private static final int MAX_CONTENT_HEIGHT = 60; private static final int LINE_HEIGHT = 20; @@ -168,6 +166,10 @@ public class FvsGuidePane extends JPanel { return Toolkit.i18nText("Fine-Design_Form_Guide_Use_Fvs_Link_Tips"); } + private String guideUrl() { + return Toolkit.i18nText("Fine-Design_Form_Guide_Use_Fvs_Link_Url"); + } + private void close(ActionEvent e) { parent.remove(this); parent.revalidate(); @@ -234,7 +236,7 @@ public class FvsGuidePane extends JPanel { htmlBuilder.append(chars[i]); } } - htmlBuilder.append("").append(linkContent).append(""); + htmlBuilder.append("").append(linkContent).append(""); return htmlBuilder.toString(); } From c2007412445fef7a105139207d7388d9aae4379c Mon Sep 17 00:00:00 2001 From: lemon Date: Mon, 31 Mar 2025 15:13:04 +0800 Subject: [PATCH 3/4] =?UTF-8?q?REPORT-150879=20fix:=20=E6=96=B0=E8=80=81?= =?UTF-8?q?=E6=96=B9=E6=A1=88=E5=85=BC=E5=AE=B9=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/fr/start/server/FineEmbedServerActivator.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java b/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java index ab17ab15eb..8360989818 100644 --- a/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java +++ b/designer-base/src/main/java/com/fr/start/server/FineEmbedServerActivator.java @@ -2,7 +2,6 @@ package com.fr.start.server; import com.fr.cbb.websocket.core.WebSocketEndpoint; import com.fr.design.DesignerEnvManager; -import com.fr.event.EventDispatcher; import com.fr.log.FineLoggerFactory; import com.fr.module.Activator; import com.fr.module.ModuleRole; @@ -13,7 +12,6 @@ import com.fr.startup.FineWebApplicationInitializer; import com.fr.third.guava.collect.Sets; import com.fr.third.springframework.web.SpringServletContainerInitializer; import com.fr.third.springframework.web.context.support.AnnotationConfigWebApplicationContext; -import com.fr.web.utils.event.WebEvent; import com.fr.workspace.WorkContext; import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; @@ -43,7 +41,6 @@ public class FineEmbedServerActivator extends Activator { //初始化tomcat initTomcat(); tomcat.start(); - EventDispatcher.asyncFire(WebEvent.WEB_STATUS_CHANGE); } catch (LifecycleException e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); @@ -59,7 +56,6 @@ public class FineEmbedServerActivator extends Activator { stopSpring(); stopServerActivator(); stopTomcat(); - EventDispatcher.asyncFire(WebEvent.WEB_STATUS_CHANGE); } catch (Exception e) { FineLoggerFactory.getLogger().error(e.getMessage(), e); } From 4dc18f5d488457ca55de7bf3e25eb2a49aa4ef89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Levy=2EXie-=E8=A7=A3=E5=AE=89=E6=A3=AE?= Date: Thu, 3 Apr 2025 15:27:55 +0800 Subject: [PATCH 4/4] =?UTF-8?q?REPORT-149471=20feat:FR11=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8F=96=E6=95=B0=E6=95=B0=E6=8D=AE=E4=B8=AD=E5=BF=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/fr/design/jxbrowser/JxEngine.java | 60 +++++ .../com/fr/design/jxbrowser/JxUIPane.java | 216 +++++++++++++----- 2 files changed, 222 insertions(+), 54 deletions(-) diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java b/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java index 7738082a4c..78c5e7a832 100644 --- a/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/JxEngine.java @@ -11,15 +11,21 @@ import com.teamdev.jxbrowser.engine.Engine; import com.teamdev.jxbrowser.engine.EngineOptions; import com.teamdev.jxbrowser.engine.RenderingMode; import com.teamdev.jxbrowser.engine.event.EngineCrashed; +import com.teamdev.jxbrowser.net.HttpHeader; import com.teamdev.jxbrowser.net.Network; +import com.teamdev.jxbrowser.net.ResourceType; import com.teamdev.jxbrowser.net.Scheme; +import com.teamdev.jxbrowser.net.UrlRequest; +import com.teamdev.jxbrowser.net.callback.BeforeStartTransactionCallback; import com.teamdev.jxbrowser.net.callback.VerifyCertificateCallback; import com.teamdev.jxbrowser.os.Environment; import org.jetbrains.annotations.NotNull; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -38,6 +44,7 @@ public class JxEngine { private AssembleComponent component; private Map parameterMap = Collections.emptyMap(); + private boolean disableWebSecurity = false; private final ClearableLazyValue ENGINE = ClearableLazyValue.create(() -> { @@ -46,6 +53,9 @@ public class JxEngine { .addSwitch("--disable-google-traffic") .addScheme(Scheme.of(ModernUIConstants.EMB_TAG), new NxInterceptRequestCallback(this::getComponent, this::getParameterMap)); + if (disableWebSecurity) { + builder.addSwitch("--disable-web-security"); + } Engine engine; try { engine = Engine.newInstance(builder.build()); @@ -70,6 +80,14 @@ public class JxEngine { return engine; }); + public JxEngine() { + } + + public JxEngine(boolean disableWebSecurity) { + this.disableWebSecurity = disableWebSecurity; + } + + public Map getParameterMap() { return Collections.unmodifiableMap(parameterMap); } @@ -125,6 +143,38 @@ public class JxEngine { return ENGINE.getValue(); } + /** + * 是否禁用安全属性 + * 注:对客户端来说,安全属性可以忽略 + * + * @return 是否禁用 + */ + public boolean isDisableWebSecurity() { + return disableWebSecurity; + } + + /** + * 添加XHR请求头 + * + * @param headers 请求头 + */ + public void addXHRHeaders(Map headers) { + Network network = ENGINE.getValue().network(); + network.set(BeforeStartTransactionCallback.class, (params) -> { + UrlRequest urlRequest = params.urlRequest(); + ResourceType resourceType = urlRequest.resourceType(); + if (resourceType == ResourceType.XHR) { + List httpHeaders = new ArrayList<>(params.httpHeaders()); + for (Map.Entry header : headers.entrySet()) { + httpHeaders.add(HttpHeader.of(header.getKey(), header.getValue())); + } + return BeforeStartTransactionCallback.Response.override(httpHeaders); + } else { + return BeforeStartTransactionCallback.Response.proceed(); + } + }); + } + /** * 关闭引擎 */ @@ -153,4 +203,14 @@ public class JxEngine { public static JxEngine newInstance() { return new JxEngine(); } + + /** + * 创建一个新的引擎,创建引擎使用后需负责关闭引擎 + * 但可以独立使用 map 和 component + * + * @return 引擎 + */ + public static JxEngine newInstance(boolean disableWebSecurity) { + return new JxEngine(disableWebSecurity); + } } diff --git a/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java b/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java index b2a0673ed6..1733502a9a 100644 --- a/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java +++ b/designer-base/src/main/java/com/fr/design/jxbrowser/JxUIPane.java @@ -1,5 +1,6 @@ package com.fr.design.jxbrowser; +import com.fr.concurrent.NamedThreadFactory; import com.fr.design.DesignerEnvManager; import com.fr.design.gui.ibutton.UIButton; import com.fr.design.gui.itoolbar.UIToolbar; @@ -11,24 +12,34 @@ import com.fr.stable.collections.combination.Pair; import com.fr.stable.os.OperatingSystem; import com.fr.web.struct.AssembleComponent; import com.teamdev.jxbrowser.browser.Browser; +import com.teamdev.jxbrowser.browser.callback.CertificateErrorCallback; import com.teamdev.jxbrowser.browser.callback.InjectJsCallback; -import com.teamdev.jxbrowser.chromium.events.LoadListener; -import com.teamdev.jxbrowser.chromium.events.ScriptContextListener; import com.teamdev.jxbrowser.event.Observer; import com.teamdev.jxbrowser.frame.Frame; import com.teamdev.jxbrowser.js.JsObject; +import com.teamdev.jxbrowser.net.callback.BeforeStartTransactionCallback; import com.teamdev.jxbrowser.view.swing.BrowserView; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.JProgressBar; import javax.swing.SwingUtilities; +import javax.swing.SwingWorker; import java.awt.BorderLayout; import java.awt.Desktop; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; - +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.function.Consumer; + +import static com.fine.swing.ui.layout.Layouts.cell; +import static com.fine.swing.ui.layout.Layouts.column; +import static com.fine.swing.ui.layout.Layouts.flex; +import static com.fine.swing.ui.layout.Layouts.row; import static com.fr.design.ui.ModernUIConstants.COMPONENT_TAG; import static com.fr.design.ui.ModernUIConstants.DEFAULT_EXPRESSION; import static com.fr.design.ui.ModernUIConstants.DEFAULT_NAMESPACE; @@ -49,31 +60,70 @@ import static com.fr.design.ui.ModernUIConstants.WINDOW; */ public class JxUIPane extends ModernUIPane { + public static final ExecutorService DEFAULT_EXECUTOR = + Executors.newSingleThreadExecutor(new NamedThreadFactory("jx-simple", true)); + /** * 冒号 */ public static final String COLON = ":"; private static final String COLON_ESCAPE = "\\:"; - private Browser browser; private String namespace = "Pool"; private String variable = "data"; private String expression = "update()"; + private final JxEngine jxEngine; + private Consumer initCallback = null; - private JxUIPane() { - super(); + private JxUIPane(JxEngine jxEngine) { + this.jxEngine = jxEngine; } - private void initialize() { + private void initialize(Consumer consumer) { setLayout(new BorderLayout()); if (browser != null) { return; } hackInITInnovationLinuxDesktop(); + initCallback = consumer; initDebugIfNeeded(); - // 使用公共引擎创建浏览器 - browser = JxEngine.getPublicEngineInstance().newBrowser(); - add(BrowserView.newInstance(browser), BorderLayout.CENTER); + asyncInitBrowser(); + } + + /** + * 启动 jxbrowser 引擎,过程包含解压文件等过程,异步操作。 + */ + private void asyncInitBrowser() { + JProgressBar jProgressBar = showProgressBar(); + new SwingWorker() { + + @Override + protected Browser doInBackground() { + browser = jxEngine.getEngine().newBrowser(); + if (jxEngine.isDisableWebSecurity()) { + // 忽略证书验证,兼容有些情况下自定义证书与实际域名不匹配的情况。 + // 虽然不是个正确的方式,但真有这么用的还是兼容一下 + browser.set(CertificateErrorCallback.class, (params, action) -> action.allow()); + } + return browser; + } + + @Override + protected void done() { + jProgressBar.setVisible(false); + try { + Browser mBrowser = get(); + add(BrowserView.newInstance(mBrowser), BorderLayout.CENTER); + if (initCallback != null) { + initCallback.accept(mBrowser); + } + initCallback = null; + revalidate(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + }.execute(); } /** @@ -85,6 +135,47 @@ public class JxUIPane extends ModernUIPane { } } + /** + * 加载组件时显示一个进度条 + */ + private @NotNull JProgressBar showProgressBar() { + JProgressBar jProgressBar = new JProgressBar(); + jProgressBar.setIndeterminate(true); + add(row( + flex(), + column(flex(), cell(jProgressBar), flex()), + flex() + ).getComponent(), BorderLayout.CENTER); + return jProgressBar; + } + + /** + * 添加自定义XHR请求头,只在自定义引擎下生效, + * 公共引擎暂不支持 + * + * @param headers 自定义头 + */ + public void addXHRHeaders(Map headers) { + warpCallback(browser -> { + if (JxEngine.getInstance() != jxEngine) { + jxEngine.addXHRHeaders(headers); + } + }); + } + + /** + * 异步链式调用 + * + * @param then 后续任务 + */ + private void warpCallback(Consumer then) { + if (initCallback != null) { + initCallback = initCallback.andThen(then); + } else { + then.accept(browser); + } + } + /** * 按需初始化debug界面UI */ @@ -124,7 +215,7 @@ public class JxUIPane extends ModernUIPane { * * @param injectJsCallback 回调 */ - private void setInjectJsCallback(InjectJsCallback injectJsCallback) { + private synchronized void setInjectJsCallback(InjectJsCallback injectJsCallback) { Optional callback = browser.get(InjectJsCallback.class); if (callback.isPresent()) { browser.set(InjectJsCallback.class, params -> { @@ -142,9 +233,8 @@ public class JxUIPane extends ModernUIPane { * * @param url 新的地址 */ - @Override public void redirect(String url) { - browser.navigation().loadUrl(encodeWindowsPath(url)); + warpCallback(browser -> browser.navigation().loadUrl(encodeWindowsPath(url))); } /** @@ -153,18 +243,17 @@ public class JxUIPane extends ModernUIPane { * @param url 新的地址 * @param map 初始化参数 */ - @Override public void redirect(String url, Map map) { setMap(map); - browser.navigation().loadUrl(encodeWindowsPath(url)); + warpCallback(browser -> browser.navigation().loadUrl(encodeWindowsPath(url))); } private void setMap(Map map) { - JxEngine.getInstance().setMap(map); + jxEngine.setMap(map); } private void setComponent(AssembleComponent component) { - JxEngine.getInstance().setComponent(component); + jxEngine.setComponent(component); } @Override @@ -173,20 +262,23 @@ public class JxUIPane extends ModernUIPane { } - @Override + /** + * 更新数据到界面 + * + * @param t 数据类 + */ public void populate(final T t) { - setInjectJsCallback(params -> { + warpCallback(browser -> setInjectJsCallback(params -> { executeJsObject(params.frame(), WINDOW + DOT + namespace) .ifPresent(ns -> ns.putProperty(variable, t)); return InjectJsCallback.Response.proceed(); - }); + })); } - @Override @Nullable public T update() { if (browser.mainFrame().isPresent()) { - return browser.mainFrame().get().executeJavaScript("window." + namespace + "." + expression); + return browser.mainFrame().get().executeJavaScript(WINDOW + DOT + namespace + DOT + expression); } return null; } @@ -198,10 +290,10 @@ public class JxUIPane extends ModernUIPane { if (browser != null) { browser.close(); browser = null; - JxEngine.getInstance().clearMap(); - JxEngine.getInstance().clearComponent(); + jxEngine.clearMap(); + jxEngine.clearComponent(); + jxEngine.getEngine().network().remove(BeforeStartTransactionCallback.class); } - } /** @@ -225,6 +317,18 @@ public class JxUIPane extends ModernUIPane { } } + /** + * 执行一段js + * + * @param javaScript 待执行的js脚本 + * @see JxUIPane#executeJS(String) + */ + public void executeJavaScript(String javaScript, Consumer consumer) { + if (browser != null) { + browser.mainFrame().ifPresent(frame -> frame.executeJavaScript(javaScript, consumer)); + } + } + /** * 获取js对象 * 注意:类内部使用,用于简化编码,提供 Optional 包装 @@ -271,6 +375,7 @@ public class JxUIPane extends ModernUIPane { * @param 参数 */ public static class Builder extends ModernUIPane.Builder { + private JxEngine jxEngine; private String namespace; private String variable; private String expression; @@ -290,6 +395,7 @@ public class JxUIPane extends ModernUIPane { public Builder() { // 为了兼容继承关系,但又不允许创建,用这个方式先处理一下 super((ModernUIPane) null); + this.jxEngine = JxEngine.getInstance(); this.namespace = DEFAULT_NAMESPACE; this.variable = DEFAULT_VARIABLE; this.expression = DEFAULT_EXPRESSION; @@ -305,6 +411,17 @@ public class JxUIPane extends ModernUIPane { this.html = StringUtils.EMPTY; } + /** + * 自定义引擎 + * + * @param jxEngine 引擎 + * @return builder + */ + public Builder engine(JxEngine jxEngine) { + this.jxEngine = jxEngine; + return this; + } + /** * 注入一个回调,回调的js会在初始化进行执行 * @@ -316,24 +433,14 @@ public class JxUIPane extends ModernUIPane { return this; } - @Override - public Builder prepareForV6(ScriptContextListener contextListener) { - return this; - } - - @Override - public Builder prepareForV6(LoadListener loadListener) { - return this; - } - - @Override - public JxUIPane.Builder prepareForV7(InjectJsCallback callback) { - prepare(callback); - return this; - } - - @Override - public JxUIPane.Builder prepareForV7(Class event, Observer listener) { + /** + * 注册一个监听器 + * + * @param event 事件 + * @param listener 监听器 + * @return builder + */ + public JxUIPane.Builder prepare(Class event, Observer listener) { listenerPair = new Pair<>(event, listener); return this; } @@ -527,22 +634,23 @@ public class JxUIPane extends ModernUIPane { * 构建 */ public JxUIPane build() { - JxUIPane pane = new JxUIPane<>(); + JxUIPane pane = new JxUIPane<>(jxEngine); pane.namespace = namespace; pane.variable = variable; pane.expression = expression; pane.setMap(parameterMap); pane.setComponent(component); - pane.initialize(); - injectJs(pane); - if (!Objects.isNull(listenerPair)) { - pane.browser.navigation().on(listenerPair.getFirst(), listenerPair.getSecond()); - } - if (StringUtils.isNotEmpty(this.url)) { - pane.browser.navigation().loadUrl(encodeWindowsPath(this.url)); - } else if (StringUtils.isNotEmpty(this.html)) { - pane.browser.mainFrame().ifPresent(f -> f.loadHtml(html)); - } + pane.initialize(browser -> { + injectJs(pane); + if (!Objects.isNull(listenerPair)) { + browser.navigation().on(listenerPair.getFirst(), listenerPair.getSecond()); + } + if (StringUtils.isNotEmpty(url)) { + browser.navigation().loadUrl(encodeWindowsPath(url)); + } else if (StringUtils.isNotEmpty(html)) { + browser.mainFrame().ifPresent(f -> f.loadHtml(html)); + } + }); return pane; }