Merged the legecy EventPreview stack into the new NativePreviewHandler stack so legacy EventPreviews will only fire if they are at the top of the combined stack. A previous change took PopupPanels off the legacy stack, which promoted other EventPreview to the top and broke legacy code.
Patch by: jlabanca
Review by: ecc (desk)
Issue: 3285
git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/1.6@4464 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/DOM.java b/user/src/com/google/gwt/user/client/DOM.java
index 082a87e..a51597c 100644
--- a/user/src/com/google/gwt/user/client/DOM.java
+++ b/user/src/com/google/gwt/user/client/DOM.java
@@ -23,8 +23,6 @@
import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.user.client.impl.DOMImpl;
-import java.util.ArrayList;
-
/**
* This class provides a set of static methods that allow you to manipulate the
* browser's Document Object Model (DOM). It contains methods for manipulating
@@ -32,16 +30,11 @@
* {@link com.google.gwt.user.client.Event events}.
*/
public class DOM {
-
// The current event being fired
private static Event currentEvent = null;
private static final DOMImpl impl = GWT.create(DOMImpl.class);
private static Element sCaptureElem;
- // <BrowserEventPreview>
- @SuppressWarnings("deprecation")
- private static ArrayList<EventPreview> sEventPreviewStack;
-
/**
* Adds an event preview to the preview stack. As long as this preview remains
* on the top of the stack, it will receive all events before they are fired
@@ -55,14 +48,7 @@
*/
@Deprecated
public static void addEventPreview(EventPreview preview) {
- impl.maybeInitializeEventSystem();
-
- // Add the event preview to the stack. It will automatically
- // begin receiving events.
- if (sEventPreviewStack == null) {
- sEventPreviewStack = new ArrayList<EventPreview>();
- }
- sEventPreviewStack.add(preview);
+ ListenerWrapper.NativePreview.add(preview);
}
/**
@@ -532,7 +518,7 @@
}
/**
- * Gets the key-repeat state of this event. Only IE supports this attribute.
+ * Gets the key-repeat state of this event. Only IE supports this attribute.
*
* @param evt the event to be tested
* @return <code>true</code> if this key event was an auto-repeat
@@ -909,8 +895,8 @@
*
* @param parent the parent element
* @param child the child element to add to <code>parent</code>
- * @param before an existing child element of <code>parent</code> before
- * which <code>child</code> will be inserted
+ * @param before an existing child element of <code>parent</code> before which
+ * <code>child</code> will be inserted
*/
public static void insertBefore(Element parent, Element child, Element before) {
parent.insertBefore(child, before);
@@ -930,10 +916,10 @@
}
/**
- * Creates an <code><option></code> element and inserts it as a child
- * of the specified <code><select></code> element. If the index is
- * less than zero, or greater than or equal to the length of the list, then
- * the option element will be appended to the end of the list.
+ * Creates an <code><option></code> element and inserts it as a child of
+ * the specified <code><select></code> element. If the index is less
+ * than zero, or greater than or equal to the length of the list, then the
+ * option element will be appended to the end of the list.
*
* @param selectElem the <code><select></code> element
* @param item the text of the new item; cannot be <code>null</code>
@@ -1012,12 +998,7 @@
*/
@Deprecated
public static void removeEventPreview(EventPreview preview) {
- // Remove the event preview from the stack. If it was on top,
- // any preview underneath it will automatically begin to
- // receive events.
- if (sEventPreviewStack != null) {
- sEventPreviewStack.remove(preview);
- }
+ ListenerWrapper.NativePreview.remove(preview);
}
/**
@@ -1270,6 +1251,13 @@
}
/**
+ * Initialize the event system if it has not already been initialized.
+ */
+ static void maybeInitializeEventSystem() {
+ impl.maybeInitializeEventSystem();
+ }
+
+ /**
* This method is called directly by native code when event preview is being
* used.
*
@@ -1281,12 +1269,6 @@
// Fire a NativePreviewEvent to NativePreviewHandlers
boolean ret = Event.fireNativePreviewEvent(evt);
- // If event previews are present, redirect events to the topmost of them.
- if (sEventPreviewStack != null && sEventPreviewStack.size() > 0) {
- EventPreview preview = sEventPreviewStack.get(sEventPreviewStack.size() - 1);
- ret = preview.onEventPreview(evt) && ret;
- }
-
// If the preview cancels the event, stop it from bubbling and performing
// its default action. Check for a null evt to allow unit tests to run.
if (!ret && evt != null) {
diff --git a/user/src/com/google/gwt/user/client/Event.java b/user/src/com/google/gwt/user/client/Event.java
index ff156f1..4ef3269 100644
--- a/user/src/com/google/gwt/user/client/Event.java
+++ b/user/src/com/google/gwt/user/client/Event.java
@@ -30,9 +30,9 @@
* An opaque handle to a native DOM Event. An <code>Event</code> cannot be
* created directly. Instead, use the <code>Event</code> type when returning a
* native DOM event from JSNI methods. An <code>Event</code> passed back into
- * JSNI becomes the original DOM event the <code>Event</code> was created
- * from, and can be accessed in JavaScript code as expected. This is typically
- * done by calling methods in the {@link com.google.gwt.user.client.DOM} class.
+ * JSNI becomes the original DOM event the <code>Event</code> was created from,
+ * and can be accessed in JavaScript code as expected. This is typically done by
+ * calling methods in the {@link com.google.gwt.user.client.DOM} class.
* </p>
*/
public class Event extends JavaScriptObject {
@@ -85,6 +85,7 @@
int numHandlers = handlers.size();
for (int i = numHandlers - 1; i >= 0; i--) {
handlers.get(i).onPreviewNativeEvent(singleton);
+ singleton.isFirstHandler = false;
}
// Kill the event and return
@@ -106,6 +107,12 @@
private boolean isConsumed = false;
/**
+ * A boolean indicating that the current handler is at the top of the event
+ * preview stack.
+ */
+ private boolean isFirstHandler = false;
+
+ /**
* The event being previewed.
*/
private Event nativeEvent;
@@ -162,6 +169,15 @@
return isConsumed;
}
+ /**
+ * Is the current handler the first to preview this event?
+ *
+ * @return true if the current handler is the first to preview the event
+ */
+ public boolean isFirstHandler() {
+ return isFirstHandler;
+ }
+
@Override
protected void dispatch(NativePreviewHandler handler) {
handler.onPreviewNativeEvent(this);
@@ -172,6 +188,7 @@
super.revive();
isCanceled = false;
isConsumed = false;
+ isFirstHandler = true;
nativeEvent = null;
}
@@ -304,7 +321,8 @@
public static final int ONSCROLL = 0x04000;
/**
- * Fired when the user requests an element's context menu (usually by right-clicking).
+ * Fired when the user requests an element's context menu (usually by
+ * right-clicking).
*
* Note that not all browsers will fire this event (notably Opera, as of 9.5).
*/
@@ -328,7 +346,7 @@
| ONMOUSEOVER | ONMOUSEOUT;
/**
- * Value returned by accessors when the actual integer value is undefined. In
+ * Value returned by accessors when the actual integer value is undefined. In
* hosted mode, most accessors assert that the requested attribute is reliable
* across all supported browsers.
*
@@ -338,12 +356,11 @@
public static final int UNDEFINED = 0;
/**
- * The list of {@link NativePreviewHandler}. We use a list instead of a
+ * The list of {@link NativePreviewHandler}. We use a list instead of a
* handler manager for efficiency and because we want to fire the handlers in
- * reverse order. When the last handler is removed, handlers is reset to
- * null.
+ * reverse order. When the last handler is removed, handlers is reset to null.
*/
- private static ArrayList<NativePreviewHandler> handlers;
+ static ArrayList<NativePreviewHandler> handlers;
/**
* Adds an event preview to the preview stack. As long as this preview remains
@@ -381,12 +398,14 @@
public static HandlerRegistration addNativePreviewHandler(
final NativePreviewHandler handler) {
assert handler != null : "Cannot add a null handler";
+ DOM.maybeInitializeEventSystem();
+
// Initialize the type
NativePreviewEvent.getType();
if (handlers == null) {
handlers = new ArrayList<NativePreviewHandler>();
}
- handlers.add(handler);
+ handlers.add(handler);
return new HandlerRegistration() {
public void removeHandler() {
if (handlers != null) {
@@ -430,7 +449,7 @@
* @see #setCapture(Element)
*/
public static void releaseCapture(Element elem) {
- DOM.releaseCapture(elem.<com.google.gwt.user.client.Element>cast());
+ DOM.releaseCapture(elem.<com.google.gwt.user.client.Element> cast());
}
/**
@@ -453,7 +472,7 @@
* @param elem the element on which to set mouse capture
*/
public static void setCapture(Element elem) {
- DOM.setCapture(elem.<com.google.gwt.user.client.Element>cast());
+ DOM.setCapture(elem.<com.google.gwt.user.client.Element> cast());
}
/**
diff --git a/user/src/com/google/gwt/user/client/ListenerWrapper.java b/user/src/com/google/gwt/user/client/ListenerWrapper.java
index bcd0115..6a80c85 100644
--- a/user/src/com/google/gwt/user/client/ListenerWrapper.java
+++ b/user/src/com/google/gwt/user/client/ListenerWrapper.java
@@ -25,21 +25,21 @@
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.HandlerManager;
import com.google.gwt.event.shared.GwtEvent.Type;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
import java.util.EventListener;
/**
- * Legacy listener support hierarchy for <code>com.google.gwt.user.client</code>.
- * Gathers the bulk of the legacy glue code in one place, for easy deletion when
- * Listener methods are deleted.
+ * Legacy listener support for <code>com.google.gwt.user.client</code>. Gathers
+ * the bulk of the legacy glue code in one place, for easy deletion when
+ * Listener methods are deleted.
*
- * @see com.google.gwt.user.ListenerWrapper
* @param <T> listener type
*/
@Deprecated
abstract class ListenerWrapper<T> implements EventHandler {
- public static class HistoryChange extends ListenerWrapper<HistoryListener> implements
- ValueChangeHandler<String> {
+ public static class HistoryChange extends ListenerWrapper<HistoryListener>
+ implements ValueChangeHandler<String> {
@Deprecated
public static void add(HistoryListener listener) {
History.addValueChangeHandler(new HistoryChange(listener));
@@ -58,8 +58,46 @@
}
}
- public static class WindowClose extends ListenerWrapper<WindowCloseListener> implements
- Window.ClosingHandler, CloseHandler<Window> {
+ public static class NativePreview extends ListenerWrapper<EventPreview>
+ implements Event.NativePreviewHandler {
+ @Deprecated
+ public static void add(EventPreview listener) {
+ Event.addNativePreviewHandler(new NativePreview(listener));
+ }
+
+ public static void remove(EventPreview listener) {
+ if (Event.handlers == null) {
+ return;
+ }
+ int handlerCount = Event.handlers.size();
+ // We only want to remove the first instance, as the legacy listener does
+ for (int i = 0; i < handlerCount; i++) {
+ Event.NativePreviewHandler handler = Event.handlers.get(i);
+ if (handler instanceof NativePreview
+ && ((NativePreview) handler).listener.equals(listener)) {
+ Event.handlers.remove(handler);
+ return;
+ }
+ }
+ }
+
+ private NativePreview(EventPreview listener) {
+ super(listener);
+ }
+
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ // The legacy EventHandler should only fire if it is on the top of the
+ // stack (ie. the last one added).
+ if (event.isFirstHandler()) {
+ if (!listener.onEventPreview(event.getNativeEvent())) {
+ event.cancel();
+ }
+ }
+ }
+ }
+
+ public static class WindowClose extends ListenerWrapper<WindowCloseListener>
+ implements Window.ClosingHandler, CloseHandler<Window> {
@Deprecated
public static void add(WindowCloseListener listener) {
WindowClose handler = new WindowClose(listener);
@@ -89,8 +127,8 @@
}
}
- public static class WindowResize extends ListenerWrapper<WindowResizeListener> implements
- ResizeHandler {
+ public static class WindowResize extends
+ ListenerWrapper<WindowResizeListener> implements ResizeHandler {
@Deprecated
public static void add(WindowResizeListener listener) {
Window.addResizeHandler(new WindowResize(listener));
@@ -110,8 +148,8 @@
}
}
- public static class WindowScroll extends ListenerWrapper<WindowScrollListener> implements
- Window.ScrollHandler {
+ public static class WindowScroll extends
+ ListenerWrapper<WindowScrollListener> implements Window.ScrollHandler {
@Deprecated
public static void add(WindowScrollListener listener) {
Window.addWindowScrollHandler(new WindowScroll(listener));
@@ -131,7 +169,6 @@
}
}
-
// This is an internal helper method with the current formulation, we have
// lost the info needed to make it safe by this point.
@SuppressWarnings("unchecked")
@@ -153,7 +190,7 @@
}
}
}
-
+
/**
* Listener being wrapped.
*/
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImpl.java b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
index 30da21c..09ffb25 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -157,9 +157,6 @@
public abstract void insertChild(Element parent, Element child, int index);
- /**
- * Initialize the event system if it has not already been initialized.
- */
public void maybeInitializeEventSystem() {
if (!eventSystemIsInitialized) {
initEventSystem();
diff --git a/user/src/com/google/gwt/user/client/ui/PopupPanel.java b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
index 707fc0b..981d9cb 100644
--- a/user/src/com/google/gwt/user/client/ui/PopupPanel.java
+++ b/user/src/com/google/gwt/user/client/ui/PopupPanel.java
@@ -829,7 +829,7 @@
@SuppressWarnings("deprecation")
protected void onPreviewNativeEvent(NativePreviewEvent event) {
// Cancel the event based on the deprecated onEventPreview() method
- if (!event.isCanceled() && !onEventPreview(event.getNativeEvent())) {
+ if (event.isFirstHandler() && !onEventPreview(event.getNativeEvent())) {
event.cancel();
}
}
diff --git a/user/test/com/google/gwt/user/client/EventTest.java b/user/test/com/google/gwt/user/client/EventTest.java
index a894e85..42c7516 100644
--- a/user/test/com/google/gwt/user/client/EventTest.java
+++ b/user/test/com/google/gwt/user/client/EventTest.java
@@ -132,17 +132,20 @@
/**
* Test that {@link Event#fireNativePreviewEvent(Event)} fires handlers in
- * reverse order. Also verify that the legacy EventPreview fires last.
+ * reverse order, and that the legacy EventPreview fires only if it is at the
+ * top of the stack.
*/
@SuppressWarnings("deprecation")
public void testFireNativePreviewEventReverseOrder() {
- final TestEventPreview preview = new TestEventPreview(false);
+ final TestEventPreview preview0 = new TestEventPreview(false);
+ final TestEventPreview preview1 = new TestEventPreview(false);
final TestNativePreviewHandler handler0 = new TestNativePreviewHandler(
false, false) {
@Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
super.onPreviewNativeEvent(event);
- preview.assertIsFired(false);
+ preview0.assertIsFired(false);
+ preview1.assertIsFired(true);
}
};
final TestNativePreviewHandler handler1 = new TestNativePreviewHandler(
@@ -151,7 +154,8 @@
public void onPreviewNativeEvent(NativePreviewEvent event) {
super.onPreviewNativeEvent(event);
handler0.assertIsFired(false);
- preview.assertIsFired(false);
+ preview0.assertIsFired(false);
+ preview1.assertIsFired(true);
}
};
final TestNativePreviewHandler handler2 = new TestNativePreviewHandler(
@@ -161,7 +165,8 @@
super.onPreviewNativeEvent(event);
handler0.assertIsFired(false);
handler1.assertIsFired(false);
- preview.assertIsFired(false);
+ preview0.assertIsFired(false);
+ preview1.assertIsFired(true);
}
};
final TestNativePreviewHandler handler3 = new TestNativePreviewHandler(
@@ -172,25 +177,29 @@
handler0.assertIsFired(false);
handler1.assertIsFired(false);
handler2.assertIsFired(false);
- preview.assertIsFired(false);
+ preview0.assertIsFired(false);
+ preview1.assertIsFired(true);
}
};
+ DOM.addEventPreview(preview0);
HandlerRegistration reg0 = Event.addNativePreviewHandler(handler0);
HandlerRegistration reg1 = Event.addNativePreviewHandler(handler1);
HandlerRegistration reg2 = Event.addNativePreviewHandler(handler2);
HandlerRegistration reg3 = Event.addNativePreviewHandler(handler3);
- DOM.addEventPreview(preview);
+ DOM.addEventPreview(preview1);
assertTrue(DOM.previewEvent(null));
handler0.assertIsFired(true);
handler1.assertIsFired(true);
handler2.assertIsFired(true);
handler3.assertIsFired(true);
- preview.assertIsFired(true);
+ preview0.assertIsFired(false);
+ preview1.assertIsFired(true);
reg0.removeHandler();
reg1.removeHandler();
reg2.removeHandler();
reg3.removeHandler();
- DOM.removeEventPreview(preview);
+ DOM.removeEventPreview(preview0);
+ DOM.removeEventPreview(preview1);
}
/**