|
|
|
@ -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,36 @@ 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 +62,70 @@ import static com.fr.design.ui.ModernUIConstants.WINDOW;
|
|
|
|
|
*/ |
|
|
|
|
public class JxUIPane<T> extends ModernUIPane<T> { |
|
|
|
|
|
|
|
|
|
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<Browser> initCallback = null; |
|
|
|
|
|
|
|
|
|
private JxUIPane() { |
|
|
|
|
super(); |
|
|
|
|
private JxUIPane(JxEngine jxEngine) { |
|
|
|
|
this.jxEngine = jxEngine; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void initialize() { |
|
|
|
|
private void initialize(Consumer<Browser> 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<Browser, Void>() { |
|
|
|
|
|
|
|
|
|
@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 +137,47 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 加载组件时显示一个进度条 |
|
|
|
|
*/ |
|
|
|
|
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<String, String> headers) { |
|
|
|
|
warpCallback(browser -> { |
|
|
|
|
if (JxEngine.getInstance() != jxEngine) { |
|
|
|
|
jxEngine.addXHRHeaders(headers); |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 异步链式调用 |
|
|
|
|
* |
|
|
|
|
* @param then 后续任务 |
|
|
|
|
*/ |
|
|
|
|
private void warpCallback(Consumer<Browser> then) { |
|
|
|
|
if (initCallback != null) { |
|
|
|
|
initCallback = initCallback.andThen(then); |
|
|
|
|
} else { |
|
|
|
|
then.accept(browser); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 按需初始化debug界面UI |
|
|
|
|
*/ |
|
|
|
@ -124,7 +217,7 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
* |
|
|
|
|
* @param injectJsCallback 回调 |
|
|
|
|
*/ |
|
|
|
|
private void setInjectJsCallback(InjectJsCallback injectJsCallback) { |
|
|
|
|
private synchronized void setInjectJsCallback(InjectJsCallback injectJsCallback) { |
|
|
|
|
Optional<InjectJsCallback> callback = browser.get(InjectJsCallback.class); |
|
|
|
|
if (callback.isPresent()) { |
|
|
|
|
browser.set(InjectJsCallback.class, params -> { |
|
|
|
@ -142,9 +235,8 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
* |
|
|
|
|
* @param url 新的地址 |
|
|
|
|
*/ |
|
|
|
|
@Override |
|
|
|
|
public void redirect(String url) { |
|
|
|
|
browser.navigation().loadUrl(encodeWindowsPath(url)); |
|
|
|
|
warpCallback(browser -> browser.navigation().loadUrl(encodeWindowsPath(url))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -153,18 +245,17 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
* @param url 新的地址 |
|
|
|
|
* @param map 初始化参数 |
|
|
|
|
*/ |
|
|
|
|
@Override |
|
|
|
|
public void redirect(String url, Map<String, String> map) { |
|
|
|
|
setMap(map); |
|
|
|
|
browser.navigation().loadUrl(encodeWindowsPath(url)); |
|
|
|
|
warpCallback(browser -> browser.navigation().loadUrl(encodeWindowsPath(url))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void setMap(Map<String, String> map) { |
|
|
|
|
JxEngine.getInstance().setMap(map); |
|
|
|
|
jxEngine.setMap(map); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void setComponent(AssembleComponent component) { |
|
|
|
|
JxEngine.getInstance().setComponent(component); |
|
|
|
|
jxEngine.setComponent(component); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
@ -173,20 +264,23 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@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 +292,10 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
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 +319,18 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 执行一段js |
|
|
|
|
* |
|
|
|
|
* @param javaScript 待执行的js脚本 |
|
|
|
|
* @see JxUIPane#executeJS(String) |
|
|
|
|
*/ |
|
|
|
|
public <T> void executeJavaScript(String javaScript, Consumer<T> consumer) { |
|
|
|
|
if (browser != null) { |
|
|
|
|
browser.mainFrame().ifPresent(frame -> frame.executeJavaScript(javaScript, consumer)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 获取js对象 |
|
|
|
|
* 注意:类内部使用,用于简化编码,提供 Optional 包装 |
|
|
|
@ -271,6 +377,7 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
* @param <T> 参数 |
|
|
|
|
*/ |
|
|
|
|
public static class Builder<T> extends ModernUIPane.Builder<T> { |
|
|
|
|
private JxEngine jxEngine; |
|
|
|
|
private String namespace; |
|
|
|
|
private String variable; |
|
|
|
|
private String expression; |
|
|
|
@ -290,6 +397,7 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
public Builder() { |
|
|
|
|
// 为了兼容继承关系,但又不允许创建,用这个方式先处理一下
|
|
|
|
|
super((ModernUIPane<T>) null); |
|
|
|
|
this.jxEngine = JxEngine.getInstance(); |
|
|
|
|
this.namespace = DEFAULT_NAMESPACE; |
|
|
|
|
this.variable = DEFAULT_VARIABLE; |
|
|
|
|
this.expression = DEFAULT_EXPRESSION; |
|
|
|
@ -305,6 +413,17 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
this.html = StringUtils.EMPTY; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 自定义引擎 |
|
|
|
|
* |
|
|
|
|
* @param jxEngine 引擎 |
|
|
|
|
* @return builder |
|
|
|
|
*/ |
|
|
|
|
public Builder<T> engine(JxEngine jxEngine) { |
|
|
|
|
this.jxEngine = jxEngine; |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 注入一个回调,回调的js会在初始化进行执行 |
|
|
|
|
* |
|
|
|
@ -316,6 +435,25 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 注册一个监听器 |
|
|
|
|
* |
|
|
|
|
* @param event 事件 |
|
|
|
|
* @param listener 监听器 |
|
|
|
|
* @return builder |
|
|
|
|
*/ |
|
|
|
|
public JxUIPane.Builder<T> prepare(Class event, Observer listener) { |
|
|
|
|
listenerPair = new Pair<>(event, listener); |
|
|
|
|
return this; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* 下面XXXV6/XXXV7相关的四个方法不要删 兼容扩展图表插件用 |
|
|
|
|
* |
|
|
|
|
* @param contextListener 上下文监听器 |
|
|
|
|
* @return builder |
|
|
|
|
*/ |
|
|
|
|
@Override |
|
|
|
|
public Builder<T> prepareForV6(ScriptContextListener contextListener) { |
|
|
|
|
return this; |
|
|
|
@ -334,8 +472,7 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
public JxUIPane.Builder<T> prepareForV7(Class event, Observer listener) { |
|
|
|
|
listenerPair = new Pair<>(event, listener); |
|
|
|
|
return this; |
|
|
|
|
return prepare(event, listener); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
@ -527,22 +664,23 @@ public class JxUIPane<T> extends ModernUIPane<T> {
|
|
|
|
|
* 构建 |
|
|
|
|
*/ |
|
|
|
|
public JxUIPane<T> build() { |
|
|
|
|
JxUIPane<T> pane = new JxUIPane<>(); |
|
|
|
|
JxUIPane<T> 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|