diff options
author | Aleksi Hietanen <aleksi@vaadin.com> | 2017-04-19 10:33:01 +0300 |
---|---|---|
committer | Henri Sara <henri.sara@gmail.com> | 2017-04-19 10:33:01 +0300 |
commit | 2d9fb530cced8a4329a57293d308760b424af067 (patch) | |
tree | 79c2c59da668bd0786083b718abf2866544033c0 | |
parent | de63e1f36a479b9fdbf7faff5b96aa75745f5970 (diff) | |
download | vaadin-framework-2d9fb530cced8a4329a57293d308760b424af067.tar.gz vaadin-framework-2d9fb530cced8a4329a57293d308760b424af067.zip |
Fix client-side memory leak caused by Grid events (#9062)
Refactors AbstractGridKeyEvent, AbstractGridMouseEvent and their
descendants to follow the pattern used in other GWT DomEvents.
Fixes #7633
9 files changed, 226 insertions, 71 deletions
diff --git a/all/src/main/templates/release-notes.html b/all/src/main/templates/release-notes.html index 4a012694fb..1160582ff6 100644 --- a/all/src/main/templates/release-notes.html +++ b/all/src/main/templates/release-notes.html @@ -116,6 +116,7 @@ See also <a href="http://dev.vaadin.com/ticket/8171">#8171</a></li> <li>The way we handle GWT dependencies has been completely changed. See <a href="#gwtdep">this section</a> for more details.</li> <li>Client side compilation with Maven is now in strict mode by default, failing the compilation on any errors.</li> + <li>Client side classes extending either AbstractGridMouseEvent or AbstractGridKeyEvent must now override the method getAssociatedType.</li> </ul> <h3 id="knownissues">Known Issues and Limitations</h3> <ul> diff --git a/client/src/main/java/com/vaadin/client/widget/grid/EventCellReference.java b/client/src/main/java/com/vaadin/client/widget/grid/EventCellReference.java index 60a1c676e8..ab5d55dbb2 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/EventCellReference.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/EventCellReference.java @@ -87,7 +87,7 @@ public class EventCellReference<T> extends CellReference<T> { * Is the cell reference for a cell in the header of the Grid. * * @since 7.5 - * @return <code>true</true> if referenced cell is in the header, + * @return <code>true</true> if referenced cell is in the header, * <code>false</code> if not */ public boolean isHeader() { @@ -98,7 +98,7 @@ public class EventCellReference<T> extends CellReference<T> { * Is the cell reference for a cell in the body of the Grid. * * @since 7.5 - * @return <code>true</true> if referenced cell is in the body, + * @return <code>true</true> if referenced cell is in the body, * <code>false</code> if not */ public boolean isBody() { @@ -109,7 +109,7 @@ public class EventCellReference<T> extends CellReference<T> { * Is the cell reference for a cell in the footer of the Grid. * * @since 7.5 - * @return <code>true</true> if referenced cell is in the footer, + * @return <code>true</true> if referenced cell is in the footer, * <code>false</code> if not */ public boolean isFooter() { diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridClickEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridClickEvent.java index 08d08f3011..84b7cdfca1 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/GridClickEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridClickEvent.java @@ -30,8 +30,26 @@ import com.vaadin.shared.ui.grid.GridConstants.Section; */ public class GridClickEvent extends AbstractGridMouseEvent<GridClickHandler> { + public static final Type<GridClickHandler> TYPE = new Type<GridClickHandler>( + BrowserEvents.CLICK, new GridClickEvent()); + + /** + * @since 7.7.9 + */ + public GridClickEvent() { + } + + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public GridClickEvent(Grid<?> grid, CellReference<?> targetCell) { - super(grid, targetCell); + } + + @Override + public Type<GridClickHandler> getAssociatedType() { + return TYPE; } @Override diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridDoubleClickEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridDoubleClickEvent.java index 55fe118426..ec564b6d8a 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/GridDoubleClickEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridDoubleClickEvent.java @@ -31,8 +31,26 @@ import com.vaadin.shared.ui.grid.GridConstants.Section; public class GridDoubleClickEvent extends AbstractGridMouseEvent<GridDoubleClickHandler> { + public static final Type<GridDoubleClickHandler> TYPE = new Type<GridDoubleClickHandler>( + BrowserEvents.DBLCLICK, new GridDoubleClickEvent()); + + /** + * @since 7.7.9 + */ + public GridDoubleClickEvent() { + } + + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public GridDoubleClickEvent(Grid<?> grid, CellReference<?> targetCell) { - super(grid, targetCell); + } + + @Override + public Type<GridDoubleClickHandler> getAssociatedType() { + return TYPE; } @Override @@ -51,5 +69,4 @@ public class GridDoubleClickEvent handler.onDoubleClick(this); } } - } diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyDownEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyDownEvent.java index 2b5b3dbeef..53a668e893 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyDownEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyDownEvent.java @@ -31,8 +31,26 @@ import com.vaadin.shared.ui.grid.GridConstants.Section; */ public class GridKeyDownEvent extends AbstractGridKeyEvent<GridKeyDownHandler> { + public static final Type<GridKeyDownHandler> TYPE = new Type<GridKeyDownHandler>( + BrowserEvents.KEYDOWN, new GridKeyDownEvent()); + + /** + * @since 7.7.9 + */ + public GridKeyDownEvent() { + } + + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public GridKeyDownEvent(Grid<?> grid, CellReference<?> targetCell) { - super(grid, targetCell); + } + + @Override + public Type<GridKeyDownHandler> getAssociatedType() { + return TYPE; } @Override diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyPressEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyPressEvent.java index 0b6abce26b..12c507c52c 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyPressEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyPressEvent.java @@ -31,8 +31,26 @@ import com.vaadin.shared.ui.grid.GridConstants.Section; public class GridKeyPressEvent extends AbstractGridKeyEvent<GridKeyPressHandler> { + public static final Type<GridKeyPressHandler> TYPE = new Type<GridKeyPressHandler>( + BrowserEvents.KEYPRESS, new GridKeyPressEvent()); + + /** + * @since 7.7.9 + */ + public GridKeyPressEvent() { + } + + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public GridKeyPressEvent(Grid<?> grid, CellReference<?> targetCell) { - super(grid, targetCell); + } + + @Override + public Type<GridKeyPressHandler> getAssociatedType() { + return TYPE; } @Override diff --git a/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyUpEvent.java b/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyUpEvent.java index 8cf61f9032..f40e8d4367 100644 --- a/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyUpEvent.java +++ b/client/src/main/java/com/vaadin/client/widget/grid/events/GridKeyUpEvent.java @@ -31,8 +31,21 @@ import com.vaadin.shared.ui.grid.GridConstants.Section; */ public class GridKeyUpEvent extends AbstractGridKeyEvent<GridKeyUpHandler> { + public static final Type<GridKeyUpHandler> TYPE = new Type<GridKeyUpHandler>( + BrowserEvents.KEYUP, new GridKeyUpEvent()); + + /** + * @since 7.7.9 + */ + public GridKeyUpEvent() { + } + + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public GridKeyUpEvent(Grid<?> grid, CellReference<?> targetCell) { - super(grid, targetCell); } @Override @@ -47,6 +60,11 @@ public class GridKeyUpEvent extends AbstractGridKeyEvent<GridKeyUpHandler> { } @Override + public Type<GridKeyUpHandler> getAssociatedType() { + return TYPE; + } + + @Override protected String getBrowserEventType() { return BrowserEvents.KEYUP; } diff --git a/client/src/main/java/com/vaadin/client/widgets/Grid.java b/client/src/main/java/com/vaadin/client/widgets/Grid.java index f9cf19e48e..c76ea3e436 100755 --- a/client/src/main/java/com/vaadin/client/widgets/Grid.java +++ b/client/src/main/java/com/vaadin/client/widgets/Grid.java @@ -2317,47 +2317,59 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, public static abstract class AbstractGridKeyEvent<HANDLER extends AbstractGridKeyEventHandler> extends KeyEvent<HANDLER> { - private Grid<?> grid; - private final Type<HANDLER> associatedType = new Type<HANDLER>( - getBrowserEventType(), this); - private final CellReference<?> targetCell; + /** + * @since 7.7.9 + */ + public AbstractGridKeyEvent() { + } + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public AbstractGridKeyEvent(Grid<?> grid, CellReference<?> targetCell) { - this.grid = grid; - this.targetCell = targetCell; } protected abstract String getBrowserEventType(); /** - * Gets the Grid instance for this event. + * Gets the Grid instance for this event, if it originated from a Grid. * - * @return grid + * @return the grid this event originated from, or {@code null} if this + * event did not originate from a grid */ public Grid<?> getGrid() { - return grid; + EventTarget target = getNativeEvent().getEventTarget(); + if (!Element.is(target)) { + return null; + } + return WidgetUtil.findWidget(Element.as(target), Grid.class); } /** - * Gets the focused cell for this event. + * Gets the reference of target cell for this event, if this event + * originated from a Grid. * - * @return focused cell + * @return target cell, or {@code null} if this event did not originate + * from a grid */ public CellReference<?> getFocusedCell() { - return targetCell; + return getGrid().getEventCell(); } @Override protected void dispatch(HANDLER handler) { EventTarget target = getNativeEvent().getEventTarget(); - if (Element.is(target) + Grid<?> grid = getGrid(); + if (Element.is(target) && grid != null && !grid.isElementInChildWidget(Element.as(target))) { Section section = Section.FOOTER; final RowContainer container = grid.cellFocusHandler.containerWithFocus; if (container == grid.escalator.getHeader()) { section = Section.HEADER; - } else if (container == grid.escalator.getBody()) { + } else if (container == getGrid().escalator.getBody()) { section = Section.BODY; } @@ -2366,45 +2378,55 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } protected abstract void doDispatch(HANDLER handler, Section section); - - @Override - public Type<HANDLER> getAssociatedType() { - return associatedType; - } } public static abstract class AbstractGridMouseEvent<HANDLER extends AbstractGridMouseEventHandler> extends MouseEvent<HANDLER> { - private Grid<?> grid; - private final CellReference<?> targetCell; - private final Type<HANDLER> associatedType = new Type<HANDLER>( - getBrowserEventType(), this); + /** + * @since 7.7.9 + */ + public AbstractGridMouseEvent() { + } + /** + * @deprecated This constructor's arguments are no longer used. Use the + * no-args constructor instead. + */ + @Deprecated public AbstractGridMouseEvent(Grid<?> grid, CellReference<?> targetCell) { - this.grid = grid; - this.targetCell = targetCell; } protected abstract String getBrowserEventType(); /** - * Gets the Grid instance for this event. + * Gets the Grid instance for this event, if it originated from a Grid. * - * @return grid + * @return the grid this event originated from, or {@code null} if this + * event did not originate from a grid */ public Grid<?> getGrid() { - return grid; + EventTarget target = getNativeEvent().getEventTarget(); + if (!Element.is(target)) { + return null; + } + return WidgetUtil.findWidget(Element.as(target), Grid.class); } /** - * Gets the reference of target cell for this event. + * Gets the reference of target cell for this event, if this event + * originated from a Grid. * - * @return target cell + * @return target cell, or {@code null} if this event did not originate + * from a grid */ public CellReference<?> getTargetCell() { - return targetCell; + Grid<?> grid = getGrid(); + if (grid == null) { + return null; + } + return grid.getEventCell(); } @Override @@ -2415,6 +2437,12 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, return; } + Grid<?> grid = getGrid(); + if (grid == null) { + // Target is not an element of a grid + return; + } + Element targetElement = Element.as(target); if (grid.isElementInChildWidget(targetElement)) { // Target is some widget inside of Grid @@ -2439,11 +2467,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, } protected abstract void doDispatch(HANDLER handler, Section section); - - @Override - public Type<HANDLER> getAssociatedType() { - return associatedType; - } } private static final String CUSTOM_STYLE_PROPERTY_NAME = "customStyle"; @@ -2457,12 +2480,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, private static final double DETAILS_ROW_INITIAL_HEIGHT = 50; private EventCellReference<T> eventCell = new EventCellReference<T>(this); - private GridKeyDownEvent keyDown = new GridKeyDownEvent(this, eventCell); - private GridKeyUpEvent keyUp = new GridKeyUpEvent(this, eventCell); - private GridKeyPressEvent keyPress = new GridKeyPressEvent(this, eventCell); - private GridClickEvent clickEvent = new GridClickEvent(this, eventCell); - private GridDoubleClickEvent doubleClickEvent = new GridDoubleClickEvent( - this, eventCell); private class CellFocusHandler { @@ -3091,7 +3108,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, /** * Sets whether the user is allowed to change the selection. - * + * * @param userSelectionAllowed * <code>true</code> if the user is allowed to change the * selection, <code>false</code> otherwise @@ -6228,7 +6245,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, * {@link ColumnResizeMode.ANIMATED}. * * @return a ColumnResizeMode value - * + * * @since 7.7.5 */ public ColumnResizeMode getColumnResizeMode() { @@ -6895,7 +6912,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, /** * Gets the {@link Escalator} used by this Grid instnace. - * + * * @return the escalator instance, never <code>null</code> */ public Escalator getEscalator() { @@ -8184,7 +8201,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addBodyKeyDownHandler( BodyKeyDownHandler handler) { - return addHandler(handler, keyDown.getAssociatedType()); + return addHandler(handler, GridKeyDownEvent.TYPE); } /** @@ -8197,7 +8214,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, * @return the registration for the event */ public HandlerRegistration addBodyKeyUpHandler(BodyKeyUpHandler handler) { - return addHandler(handler, keyUp.getAssociatedType()); + return addHandler(handler, GridKeyUpEvent.TYPE); } /** @@ -8211,7 +8228,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addBodyKeyPressHandler( BodyKeyPressHandler handler) { - return addHandler(handler, keyPress.getAssociatedType()); + return addHandler(handler, GridKeyPressEvent.TYPE); } /** @@ -8225,7 +8242,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addHeaderKeyDownHandler( HeaderKeyDownHandler handler) { - return addHandler(handler, keyDown.getAssociatedType()); + return addHandler(handler, GridKeyDownEvent.TYPE); } /** @@ -8239,7 +8256,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addHeaderKeyUpHandler( HeaderKeyUpHandler handler) { - return addHandler(handler, keyUp.getAssociatedType()); + return addHandler(handler, GridKeyUpEvent.TYPE); } /** @@ -8253,7 +8270,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addHeaderKeyPressHandler( HeaderKeyPressHandler handler) { - return addHandler(handler, keyPress.getAssociatedType()); + return addHandler(handler, GridKeyPressEvent.TYPE); } /** @@ -8267,7 +8284,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addFooterKeyDownHandler( FooterKeyDownHandler handler) { - return addHandler(handler, keyDown.getAssociatedType()); + return addHandler(handler, GridKeyDownEvent.TYPE); } /** @@ -8281,7 +8298,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addFooterKeyUpHandler( FooterKeyUpHandler handler) { - return addHandler(handler, keyUp.getAssociatedType()); + return addHandler(handler, GridKeyUpEvent.TYPE); } /** @@ -8295,7 +8312,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addFooterKeyPressHandler( FooterKeyPressHandler handler) { - return addHandler(handler, keyPress.getAssociatedType()); + return addHandler(handler, GridKeyPressEvent.TYPE); } /** @@ -8307,7 +8324,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, * @return the registration for the event */ public HandlerRegistration addBodyClickHandler(BodyClickHandler handler) { - return addHandler(handler, clickEvent.getAssociatedType()); + return addHandler(handler, GridClickEvent.TYPE); } /** @@ -8320,7 +8337,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addHeaderClickHandler( HeaderClickHandler handler) { - return addHandler(handler, clickEvent.getAssociatedType()); + return addHandler(handler, GridClickEvent.TYPE); } /** @@ -8333,7 +8350,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addFooterClickHandler( FooterClickHandler handler) { - return addHandler(handler, clickEvent.getAssociatedType()); + return addHandler(handler, GridClickEvent.TYPE); } /** @@ -8347,7 +8364,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addBodyDoubleClickHandler( BodyDoubleClickHandler handler) { - return addHandler(handler, doubleClickEvent.getAssociatedType()); + return addHandler(handler, GridDoubleClickEvent.TYPE); } /** @@ -8361,7 +8378,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addHeaderDoubleClickHandler( HeaderDoubleClickHandler handler) { - return addHandler(handler, doubleClickEvent.getAssociatedType()); + return addHandler(handler, GridDoubleClickEvent.TYPE); } /** @@ -8375,7 +8392,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, */ public HandlerRegistration addFooterDoubleClickHandler( FooterDoubleClickHandler handler) { - return addHandler(handler, doubleClickEvent.getAssociatedType()); + return addHandler(handler, GridDoubleClickEvent.TYPE); } /** @@ -8829,7 +8846,6 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, for (int row : details) { setDetailsVisible(row, false); } - super.onDetach(); } @@ -9245,7 +9261,7 @@ public class Grid<T> extends ResizeComposite implements HasSelectionHandlers<T>, /** * Checks if selection by the user is allowed in the grid. - * + * * @return <code>true</code> if selection by the user is allowed by the * selection model (the default), <code>false</code> otherwise * @since 7.7.7 diff --git a/uitest/src/main/java/com/vaadin/tests/components/grid/GridClientMemoryLeak.java b/uitest/src/main/java/com/vaadin/tests/components/grid/GridClientMemoryLeak.java new file mode 100644 index 0000000000..3db4d003e4 --- /dev/null +++ b/uitest/src/main/java/com/vaadin/tests/components/grid/GridClientMemoryLeak.java @@ -0,0 +1,49 @@ +package com.vaadin.tests.components.grid; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Grid; +import com.vaadin.ui.Label; +import com.vaadin.ui.VerticalLayout; + +public class GridClientMemoryLeak extends AbstractTestUI { + + private static final String INSTRUCTIONS = "This UI is for manually testing that the client side grid does not leak memory. " + + "Steps to take:\n" + + "\t1. Click the newGrid button 1-n times\n" + + "\t2. Capture a JS heap dump in your browser\n" + + "\t3. The heap dump should only contain 1 instance of each of the following:\n" + + "\t\tGrid, GridKeyDownEvent, GridKeyPressEvent, GridKeyUpEvent, GridClickEvent, GridDoubleClickEvent"; + + @Override + protected void setup(VaadinRequest request) { + final Label instructionLabel = new Label(INSTRUCTIONS, + ContentMode.PREFORMATTED); + final VerticalLayout layout = new VerticalLayout(); + final Button btn = new Button("newGrid"); + btn.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + layout.removeComponent(layout.getComponent(1)); + layout.addComponent(grid()); + } + }); + layout.addComponent(instructionLabel); + layout.addComponent(btn); + layout.addComponent(grid()); + addComponent(layout); + } + + private Grid grid() { + Grid grid = new Grid(); + grid.addColumn("col1", String.class); + grid.addColumn("col2", String.class); + grid.addRow("a", "b" + System.currentTimeMillis()); + grid.addRow("d", "e"); + return grid; + } +} |