IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak) (#1648)

* IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak)

- Fix NPE

* IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak)

- refine NPE fix
- fix a potential leak in billboard

* IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak)

- reduce detached html elemenet leak for change role
This commit is contained in:
hengsin 2023-01-26 18:20:03 +08:00 committed by GitHub
parent a9ae3b8652
commit d4c3685761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 107 additions and 18 deletions

View File

@ -628,17 +628,19 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
if (Env.getCtx().get(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER) != null) if (Env.getCtx().get(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER) != null)
properties.put(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER, Env.getCtx().get(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER)); properties.put(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER, Env.getCtx().get(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER));
//desktop cleanup
IDesktop appDesktop = getAppDeskop();
if (appDesktop != null)
appDesktop.logout(T -> {if (T) asyncChangeRole(httpRequest.getSession(), locale, properties);});
}
private void asyncChangeRole(HttpSession httpSession, Locale locale, Properties properties) {
//stop key listener //stop key listener
if (keyListener != null) { if (keyListener != null) {
keyListener.detach(); keyListener.detach();
keyListener = null; keyListener = null;
} }
//desktop cleanup
IDesktop appDesktop = getAppDeskop();
if (appDesktop != null)
appDesktop.logout();
//remove all children component //remove all children component
getChildren().clear(); getChildren().clear();
@ -648,7 +650,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
this.setPage(page); this.setPage(page);
//clear session attributes //clear session attributes
Enumeration<String> attributes = httpRequest.getSession().getAttributeNames(); Enumeration<String> attributes = httpSession.getAttributeNames();
while(attributes.hasMoreElements()) { while(attributes.hasMoreElements()) {
String attribute = attributes.nextElement(); String attribute = attributes.nextElement();
@ -656,7 +658,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
if (attribute.contains("zkoss.")) if (attribute.contains("zkoss."))
continue; continue;
httpRequest.getSession().removeAttribute(attribute); httpSession.removeAttribute(attribute);
} }
//logout ad_session //logout ad_session

View File

@ -80,6 +80,7 @@ import org.zkoss.zk.ui.Execution;
import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zul.Button;
import org.zkoss.zul.impl.InputElement; import org.zkoss.zul.impl.InputElement;
import com.lowagie.text.DocumentException; import com.lowagie.text.DocumentException;
@ -881,7 +882,7 @@ public final class AEnv
* @param c * @param c
*/ */
public static void detachInputElement(Component c) { public static void detachInputElement(Component c) {
if (c instanceof InputElement) { if (c instanceof InputElement || c instanceof Button) {
c.detach(); c.detach();
} }
if (c.getChildren().size() > 0) { if (c.getChildren().size() > 0) {

View File

@ -184,7 +184,9 @@ public class Tab extends org.zkoss.zul.Tab
} }
//Workaround for detached HTML input element leak //Workaround for detached HTML input element leak
boolean attached = getDesktop() != null;
if (panel != null) { if (panel != null) {
if (attached)
Executions.schedule(getDesktop(), e -> panel.detach(), new Event("onCloseLinkedPanel")); Executions.schedule(getDesktop(), e -> panel.detach(), new Event("onCloseLinkedPanel"));
} }
@ -192,7 +194,7 @@ public class Tab extends org.zkoss.zul.Tab
if (panel != null) { if (panel != null) {
//Workaround for detached HTML input element leak //Workaround for detached HTML input element leak
if (panel.getChildren().size() > 0) { if (attached && panel.getChildren().size() > 0) {
Component[] childs = panel.getChildren().toArray(new Component[0]); Component[] childs = panel.getChildren().toArray(new Component[0]);
for(Component c : childs) { for(Component c : childs) {
AEnv.detachInputElement(c); AEnv.detachInputElement(c);
@ -204,6 +206,9 @@ public class Tab extends org.zkoss.zul.Tab
if (include instanceof Include) { if (include instanceof Include) {
include.detach(); include.detach();
} }
if (!attached)
panel.detach();
} }
} }

View File

@ -30,6 +30,7 @@ import org.adempiere.base.event.EventManager;
import org.adempiere.base.event.IEventManager; import org.adempiere.base.event.IEventManager;
import org.adempiere.base.event.IEventTopics; import org.adempiere.base.event.IEventTopics;
import org.adempiere.model.MBroadcastMessage; import org.adempiere.model.MBroadcastMessage;
import org.adempiere.util.Callback;
import org.adempiere.webui.ClientInfo; import org.adempiere.webui.ClientInfo;
import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.adwindow.ADWindow; import org.adempiere.webui.adwindow.ADWindow;
@ -806,7 +807,30 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
return layout; return layout;
} }
@Override
public void logout() { public void logout() {
logout(null);
}
@Override
public void logout(Callback<Boolean> callback) {
if (layout != null && layout.getDesktop() != null
&& Executions.getCurrent() != null && Executions.getCurrent().getNativeRequest() != null) {
//close all tabs
List<Component> tabs = windowContainer.getComponent().getTabs().getChildren();
int end = tabs.size() - 1;
for (int i = end; i >= 0; i--) {
((Tab)tabs.get( i )).close();
}
AEnv.detachInputElement(layout);
//schedule async logout
Executions.schedule(layout.getDesktop(), e -> asyncLogout(callback), new Event("onAsyncLogout"));
} else {
asyncLogout(callback);
}
}
private void asyncLogout(Callback<Boolean> callback) {
unbindEventManager(); unbindEventManager();
if (dashboardController != null) { if (dashboardController != null) {
dashboardController.onLogOut(); dashboardController.onLogOut();
@ -817,9 +841,16 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
sideController.onLogOut(); sideController.onLogOut();
sideController = null; sideController = null;
} }
if (layout != null) {
layout.detach(); if (callback != null) {
if (layout != null && layout.getDesktop() != null
&& Executions.getCurrent() != null && Executions.getCurrent().getNativeRequest() != null) {
Executions.schedule(layout.getDesktop(), e -> callback.onCallback(Boolean.TRUE), new Event("onAsyncLogoutCallback"));
} else {
callback.onCallback(Boolean.TRUE);
} }
}
layout = null; layout = null;
pnlHead = null; pnlHead = null;
max = null; max = null;
@ -1012,6 +1043,7 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
updateHelpContext(X_AD_CtxHelp.CTXTYPE_Task, taskId); updateHelpContext(X_AD_CtxHelp.CTXTYPE_Task, taskId);
} }
@Override
public boolean isPendingWindow() { public boolean isPendingWindow() {
List<Object> windows = getWindows(); List<Object> windows = getWindows();
if (windows != null) { if (windows != null) {

View File

@ -201,17 +201,60 @@ public interface IDesktop extends UIPart {
*/ */
public void logout(); public void logout();
/**
*
* @param callback callback after logout
*/
public default void logout(Callback<Boolean> callback) {
logout();
if (callback != null) {
callback.onCallback(Boolean.TRUE);
}
}
/**
* update help content in help/info panel
* @param ctxTypes
* @param recordId
*/
public void updateHelpContext(String ctxType, int recordId); public void updateHelpContext(String ctxType, int recordId);
/**
* update tooltip content in help/info panel
* @param gridField
*/
public void updateHelpTooltip(GridField gridField); public void updateHelpTooltip(GridField gridField);
/**
* update tooltip content in help/info panel
* @param hdr
* @param desc
* @param help
* @param otherContent
*/
public void updateHelpTooltip(String hdr, String desc, String help, String otherContent); public void updateHelpTooltip(String hdr, String desc, String help, String otherContent);
/**
* update quick info (status line) in help/info panel
* @param gridTab
*/
public void updateHelpQuickInfo(GridTab gridTab); public void updateHelpQuickInfo(GridTab gridTab);
/**
*
* @return true if there are changes not save yet
*/
public boolean isPendingWindow(); public boolean isPendingWindow();
/**
* update tab title by windowNo
* @param title
* @param windowNo
*/
public void setTabTitle(String title, int windowNo); public void setTabTitle(String title, int windowNo);
/**
* render home tab
*/
public void renderHomeTab(); public void renderHomeTab();
} }

View File

@ -424,7 +424,7 @@ public class WindowContainer extends AbstractUIPart implements EventListener<Eve
Tab tab = (Tab)event.getTarget(); Tab tab = (Tab)event.getTarget();
org.zkoss.zul.Tabpanel panel = tab.getLinkedPanel(); org.zkoss.zul.Tabpanel panel = tab.getLinkedPanel();
if (panel == null) { if (panel == null) {
System.console().printf("error"); return;
} }
Component component = panel.getFirstChild(); Component component = panel.getFirstChild();
if (component != null && component.getAttribute(ITabOnSelectHandler.ATTRIBUTE_KEY) instanceof ITabOnSelectHandler) if (component != null && component.getAttribute(ITabOnSelectHandler.ATTRIBUTE_KEY) instanceof ITabOnSelectHandler)
@ -740,6 +740,9 @@ public class WindowContainer extends AbstractUIPart implements EventListener<Eve
*/ */
public void setTabTitle(int tabNo, String title, String tooltip) public void setTabTitle(int tabNo, String title, String tooltip)
{ {
if (tabNo < 0 || tabNo >= tabbox.getTabs().getChildren().size())
return;
org.zkoss.zul.Tabs tabs = tabbox.getTabs(); org.zkoss.zul.Tabs tabs = tabbox.getTabs();
Tab tab = (Tab) tabs.getChildren().get(tabNo); Tab tab = (Tab) tabs.getChildren().get(tabNo);
setTabTitle(title, tab); setTabTitle(title, tab);

View File

@ -45,6 +45,9 @@ public class ServerPushTemplate {
* @param callback * @param callback
*/ */
public void executeAsync(final IServerPushCallback callback) { public void executeAsync(final IServerPushCallback callback) {
if (!desktop.isAlive())
return;
try { try {
EventListener<Event> task = new EventListener<Event>() { EventListener<Event> task = new EventListener<Event>() {
@Override @Override

View File

@ -188,9 +188,9 @@ public class ChartRendererServiceImpl implements IChartRendererService {
if (Executions.getCurrent() != null) if (Executions.getCurrent() != null)
{ {
String script = "var parent = jq('#" + parent.getUuid() + "');"; String script = "(function() {let parent = jq('#" + parent.getUuid() + "');";
script += "var billboard = parent.children().first(); "; script += "let billboard = parent.children().first(); ";
script += "var div = parent.children().eq(1); "; script += "let div = parent.children().eq(1); ";
script += "if (billboard.children().length == 0) {"; script += "if (billboard.children().length == 0) {";
script += "div.show(); "; script += "div.show(); ";
script += "billboard.hide(); "; script += "billboard.hide(); ";
@ -198,7 +198,7 @@ public class ChartRendererServiceImpl implements IChartRendererService {
script += "else {"; script += "else {";
script += "div.hide(); "; script += "div.hide(); ";
script += "billboard.show(); "; script += "billboard.show(); ";
script += "}"; script += "}})();";
Clients.response(new AuScript(script)); Clients.response(new AuScript(script));
} }