summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPekka Hyvönen <pekka@vaadin.com>2017-05-17 15:59:15 +0300
committerGitHub <noreply@github.com>2017-05-17 15:59:15 +0300
commit431debc477bc669a0a6c491e62ee7174ae883fd0 (patch)
treec97fa60a5f70e8b9ffe4715308f263c241b67ef1
parent56546862721e2ab0482e9c23f679b5eee590c36a (diff)
downloadvaadin-framework-431debc477bc669a0a6c491e62ee7174ae883fd0.tar.gz
vaadin-framework-431debc477bc669a0a6c491e62ee7174ae883fd0.zip
Fix NPE when detaching DragSource & DropTarget on client side (#9341)
When the component the extensions have been attached to are removed, there was an NPE due to getParent() not being available anymore. Fixed by not doing clean up on those cases as it is not necessary. Fixes #9101 * Add back missing detach call
-rw-r--r--client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java4
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java24
-rw-r--r--client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java45
-rw-r--r--uitest/src/main/java/com/vaadin/tests/dnd/DragSourcesInTabSheet.java83
4 files changed, 124 insertions, 32 deletions
diff --git a/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java b/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java
index 7f8fb429a2..f1ac15703e 100644
--- a/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java
+++ b/client/src/main/java/com/vaadin/client/connectors/grid/GridDragSourceConnector.java
@@ -352,8 +352,6 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector {
@Override
public void onUnregister() {
- super.onUnregister();
-
// Remove draggable from all row elements in the escalator
Range visibleRange = getEscalator().getVisibleRowRange();
for (int i = visibleRange.getStart(); i < visibleRange.getEnd(); i++) {
@@ -371,6 +369,8 @@ public class GridDragSourceConnector extends DragSourceExtensionConnector {
.setDelayToCancelTouchScroll(-1);
touchScrollDelayUsed = false;
}
+
+ super.onUnregister();
}
private Grid<JsonObject> getGrid() {
diff --git a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java
index 61ff4dea8a..a0f6280ca5 100644
--- a/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java
+++ b/client/src/main/java/com/vaadin/client/extensions/DragSourceExtensionConnector.java
@@ -27,7 +27,6 @@ import com.google.gwt.dom.client.Style.Position;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
-import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.annotations.OnStateChange;
import com.vaadin.client.ui.AbstractComponentConnector;
@@ -62,15 +61,11 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector {
private final EventListener dragStartListener = this::onDragStart;
private final EventListener dragEndListener = this::onDragEnd;
- /**
- * Widget of the drag source component.
- */
private Widget dragSourceWidget;
@Override
protected void extend(ServerConnector target) {
- dragSourceWidget = ((ComponentConnector) target).getWidget();
-
+ dragSourceWidget = ((AbstractComponentConnector) target).getWidget();
// HTML5 DnD is by default not enabled for mobile devices
if (BrowserInfo.get().isTouchDevice() && !getConnection()
.getUIConnector().isMobileHTML5DndEnabled()) {
@@ -138,14 +133,21 @@ public class DragSourceExtensionConnector extends AbstractExtensionConnector {
@Override
public void onUnregister() {
- super.onUnregister();
+ AbstractComponentConnector parent = (AbstractComponentConnector) getParent();
+ // if parent is null, the whole component has been removed,
+ // no need to do clean up then
+ if (parent != null) {
+ parent.onDragSourceDetached();
- Element dragSource = getDraggableElement();
+ Element dragSource = getDraggableElement();
- removeDraggable(dragSource);
- removeDragListeners(dragSource);
+ removeDraggable(dragSource);
+ removeDragListeners(dragSource);
- ((AbstractComponentConnector) getParent()).onDragSourceDetached();
+ dragSourceWidget = null;
+ }
+
+ super.onUnregister();
}
@OnStateChange("resources")
diff --git a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java
index 060a12f658..cd23b3bc74 100644
--- a/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java
+++ b/client/src/main/java/com/vaadin/client/extensions/DropTargetExtensionConnector.java
@@ -26,7 +26,6 @@ import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.BrowserInfo;
-import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ServerConnector;
import com.vaadin.client.ui.AbstractComponentConnector;
import com.vaadin.shared.ui.Connect;
@@ -76,9 +75,6 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
private final EventListener dragLeaveListener = this::onDragLeave;
private final EventListener dropListener = this::onDrop;
- /**
- * Widget of the drop target component.
- */
private Widget dropTargetWidget;
/**
@@ -89,7 +85,7 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
@Override
protected void extend(ServerConnector target) {
- dropTargetWidget = ((ComponentConnector) target).getWidget();
+ dropTargetWidget = ((AbstractComponentConnector) target).getWidget();
// HTML5 DnD is by default not enabled for mobile devices
if (BrowserInfo.get().isTouchDevice() && !getConnection()
@@ -139,13 +135,21 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
@Override
public void onUnregister() {
- super.onUnregister();
+ AbstractComponentConnector parent = (AbstractComponentConnector) getParent();
+ // parent is null when the component has been removed,
+ // clean up only if only the extension was removed
+ if (parent != null) {
+ parent.onDropTargetDetached();
- removeDropListeners(getDropTargetElement());
- ((AbstractComponentConnector) getParent()).onDropTargetDetached();
+ removeDropListeners(getDropTargetElement());
- // Remove drop target indicator
- removeDropTargetStyle();
+ // Remove drop target indicator
+ removeDropTargetStyle();
+
+ dropTargetWidget = null;
+ }
+
+ super.onUnregister();
}
/**
@@ -313,12 +317,13 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
* Initiates a server RPC for the drop event.
*
* @param types
- * List of data types from {@code DataTransfer.types} object.
+ * List of data types from {@code DataTransfer.types} object.
* @param data
- * Map containing all types and corresponding data from the {@code
+ * Map containing all types and corresponding data from the
+ * {@code
* DataTransfer} object.
* @param dropEffect
- * The desired drop effect.
+ * The desired drop effect.
*/
protected void sendDropEventToServer(List<String> types,
Map<String, String> data, String dropEffect,
@@ -329,15 +334,17 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
/**
* Add class name for the drop target element indicating that data can be
* dropped onto it. The class name has the following format:
+ *
* <pre>
* [primaryStyleName]-droptarget
* </pre>
- * The added class name is update
- * automatically by the framework when the primary style name changes.
+ *
+ * The added class name is update automatically by the framework when the
+ * primary style name changes.
*/
protected void addDropTargetStyle() {
- getDropTargetElement().addClassName(
- getStylePrimaryName(getDropTargetElement())
+ getDropTargetElement()
+ .addClassName(getStylePrimaryName(getDropTargetElement())
+ STYLE_SUFFIX_DROPTARGET);
}
@@ -346,8 +353,8 @@ public class DropTargetExtensionConnector extends AbstractExtensionConnector {
* be dropped onto it.
*/
protected void removeDropTargetStyle() {
- getDropTargetElement().removeClassName(
- getStylePrimaryName(getDropTargetElement())
+ getDropTargetElement()
+ .removeClassName(getStylePrimaryName(getDropTargetElement())
+ STYLE_SUFFIX_DROPTARGET);
}
diff --git a/uitest/src/main/java/com/vaadin/tests/dnd/DragSourcesInTabSheet.java b/uitest/src/main/java/com/vaadin/tests/dnd/DragSourcesInTabSheet.java
new file mode 100644
index 0000000000..a493f987a3
--- /dev/null
+++ b/uitest/src/main/java/com/vaadin/tests/dnd/DragSourcesInTabSheet.java
@@ -0,0 +1,83 @@
+package com.vaadin.tests.dnd;
+
+import com.vaadin.annotations.Widgetset;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.grid.DropMode;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.tests.data.bean.Person;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Grid;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.VerticalLayout;
+import com.vaadin.ui.Window;
+import com.vaadin.ui.components.grid.GridDragSource;
+import com.vaadin.ui.components.grid.GridDropTarget;
+import com.vaadin.ui.dnd.DragSourceExtension;
+import com.vaadin.ui.dnd.DropTargetExtension;
+
+@Widgetset("com.vaadin.DefaultWidgetSet")
+public class DragSourcesInTabSheet extends AbstractTestUIWithLog {
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ TabSheet tabSheet = new TabSheet();
+ addComponent(tabSheet);
+
+ tabSheet.addTab(new VerticalLayout(createLabels()))
+ .setCaption("Labels");
+
+ Button dsButton = new Button("DragSource");
+ new DragSourceExtension<>(dsButton)
+ .addDragEndListener(event -> log("drag end button"));
+ Button dtButton = new Button("DropTarget");
+ new DropTargetExtension<>(dtButton).addDropListener(event -> log("drop "
+ + event.getDragSourceComponent().orElse(null) + " on button"));
+ tabSheet.addTab(new VerticalLayout(dsButton, dtButton))
+ .setCaption("Buttons");
+
+ tabSheet.addTab(new VerticalLayout(createGrids())).setCaption("Grids");
+
+ addComponent(new Button("Open window", event -> openWindow()));
+ }
+
+ private Label[] createLabels() {
+ Label dragSource = new Label("DragSource");
+ new DragSourceExtension<>(dragSource)
+ .addDragEndListener(event -> log("drag end label"));
+ Label dropTarget = new Label("DropTarget");
+ new DropTargetExtension<>(dropTarget).addDropListener(event -> log(
+ "drop " + event.getDragSourceComponent().orElse(null)
+ + " on label"));
+ return new Label[] { dragSource, dropTarget };
+ }
+
+ private Grid[] createGrids() {
+ Grid<Person> dsGrid = new Grid<>(Person.class);
+ dsGrid.setItems(Person.createTestPerson1(), Person.createTestPerson2());
+ new GridDragSource<>(dsGrid).addGridDragEndListener(event -> log(
+ "drag end " + event.getDraggedItems().iterator().next()));
+ Grid<Person> dtGrid = new Grid<>(Person.class);
+ dtGrid.setItems(Person.createTestPerson1(), Person.createTestPerson2());
+ new GridDropTarget<>(dtGrid, DropMode.BETWEEN)
+ .addGridDropListener(event -> log("drop on grid row "
+ + event.getDropTargetRow().orElse(null) + " "
+ + event.getDragData().orElse(null)));
+ return new Grid[] { dsGrid, dtGrid };
+ }
+
+ private void openWindow() {
+ Window window = new Window("Window with drag sources");
+ VerticalLayout layout = new VerticalLayout();
+ layout.addComponents(createLabels());
+ layout.addComponents(createGrids());
+ window.setContent(layout);
+ addWindow(window);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Verify that removing drag source and drop target components in a tabsheet/window works";
+ }
+
+}