Adds event dispatch extension methods to DOMImplStandard
Fixes issue 8379
Change-Id: I7df2678ab26e703b3db437c81ccc8d768b44917e
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
index 6bd383e..f4fb29a 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
@@ -15,6 +15,7 @@
*/
package com.google.gwt.user.client.impl;
+import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
@@ -26,13 +27,79 @@
* by those browsers that come a bit closer to supporting a common standard (ie,
* not legacy IEs).
*/
-abstract class DOMImplStandard extends DOMImpl {
+public abstract class DOMImplStandard extends DOMImpl {
+
+ /**
+ * Adds custom bitless event dispatchers to GWT. If no specific event dispatcher supplied for an
+ * event, the default dispatcher is used.
+ * <p> Example usage:
+ * <pre>
+ * static {
+ * DOMImplStandard.addBitlessEventDispatchers(getMyCustomDispatchers());
+ * }
+ *
+ * private static native JavaScriptObject getMyCustomDispatchers() /*-{
+ * return {
+ * click: @com.xxx.YYY::myCustomDispatcher(*),
+ * ...
+ * };
+ * }-* /;
+ * </pre>
+ *
+ * <p> Note that although this method is public for extensions, it is subject to change in
+ * different releases.
+ *
+ * @param eventMap an object that provides dispatching methods keyed with the name of the event
+ */
+ public static void addBitlessEventDispatchers(JavaScriptObject eventMap) {
+ ensureInit();
+ bitlessEventDispatchers.merge(eventMap);
+ }
+
+ /**
+ * Adds custom capture event dispatchers to GWT.
+ * <p> Example usage:
+ * <pre>
+ * static {
+ * if (isIE10Plus())) {
+ * DOMImplStandard.addCaptureEventDispatchers(getMsPointerCaptureDispatchers());
+ * }
+ * }
+ *
+ * private static native JavaScriptObject getMsPointerCaptureDispatchers() /*-{
+ * return {
+ * MSPointerDown: @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent(*),
+ * MSPointerUp: @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent(*),
+ * ...
+ * };
+ * }-* /;
+ * </pre>
+ *
+ * <p> Note that although this method is public for extensions, it is subject to change in
+ * different releases.
+ *
+ * @param eventMap an object that provides dispatching methods keyed with the name of the event
+ */
+ public static void addCaptureEventDispatchers(JavaScriptObject eventMap) {
+ ensureInit();
+ captureEventDispatchers.merge(eventMap);
+ }
+
+ private static void ensureInit() {
+ if (eventSystemIsInitialized) {
+ throw new IllegalStateException("Event system already initialized");
+ }
+
+ // Ensure that any default extensions for the browser is registered via
+ // static initializers in deferred binding of DOMImpl:
+ GWT.create(DOMImpl.class);
+ }
private static Element captureElem;
- private static JavaScriptObject bitlessEventDispatchers = getBitlessEventDispatchers();
+ private static EventMap bitlessEventDispatchers = getBitlessEventDispatchers();
- private static JavaScriptObject captureEventDispatchers = getCaptureEventDispatchers();
+ private static EventMap captureEventDispatchers = getCaptureEventDispatchers();
@Deprecated // We no longer want any external JSNI dependencies
private static JavaScriptObject dispatchEvent;
@@ -159,7 +226,7 @@
@com.google.gwt.user.client.impl.DOMImplStandard::dispatchUnhandledEvent =
$entry(@com.google.gwt.user.client.impl.DOMImplStandard::dispatchUnhandledEvent(*));
- var foreach = @com.google.gwt.user.client.impl.DOMImplStandard::foreach(*);
+ var foreach = @com.google.gwt.user.client.impl.EventMap::foreach(*);
// Ensure $entry for bitless event dispatchers
var bitlessEvents = @com.google.gwt.user.client.impl.DOMImplStandard::bitlessEventDispatchers;
@@ -175,7 +242,7 @@
@Override
protected native void disposeEventSystem() /*-{
- var foreach = @com.google.gwt.user.client.impl.DOMImplStandard::foreach(*);
+ var foreach = @com.google.gwt.user.client.impl.EventMap::foreach(*);
// Remove capture event listeners
foreach(captureEvents, function(e, fn) { $wnd.removeEventListener(e, fn, true); });
@@ -290,7 +357,7 @@
}
}
- private static native JavaScriptObject getBitlessEventDispatchers() /*-{
+ private static native EventMap getBitlessEventDispatchers() /*-{
return {
_default_: @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent(*),
dragenter: @com.google.gwt.user.client.impl.DOMImplStandard::dispatchDragEvent(*),
@@ -298,7 +365,7 @@
};
}-*/;
- private static native JavaScriptObject getCaptureEventDispatchers() /*-{
+ private static native EventMap getCaptureEventDispatchers() /*-{
return {
// Mouse events
click: @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent(*),
@@ -325,12 +392,4 @@
gesturechange:@com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent(*),
};
}-*/;
-
- private static native void foreach(JavaScriptObject map, JavaScriptObject fn) /*-{
- for (var e in map) {
- if (map.hasOwnProperty(e)) {
- fn(e, map[e]);
- }
- }
- }-*/;
}
diff --git a/user/src/com/google/gwt/user/client/impl/EventMap.java b/user/src/com/google/gwt/user/client/impl/EventMap.java
new file mode 100644
index 0000000..a32ae65
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/EventMap.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client.impl;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * A simple helper class to abstract event maps.
+ */
+class EventMap extends JavaScriptObject {
+
+ protected EventMap() { /* Mandatory constructor for JSO */}
+
+ /**
+ * Merges the key-value pairs from the parameter into this object. If a key already exists, then
+ * it will be overridden by the new value.
+ */
+ public final void merge(JavaScriptObject eventMap) {
+ foreach(eventMap, copyTo(this));
+ }
+
+ /**
+ * Returns a function that copies the passed key-value to target object.
+ */
+ private static native JavaScriptObject copyTo(EventMap target) /*-{
+ return function(key, value) { target[key] = value; };
+ }-*/;
+
+ /**
+ * Executes a provided function over the key-value pairs of the map object.
+ */
+ static native void foreach(JavaScriptObject map, JavaScriptObject fn) /*-{
+ for (var e in map) {
+ if (map.hasOwnProperty(e)) {
+ fn(e, map[e]);
+ }
+ }
+ }-*/;
+}
diff --git a/user/test/com/google/gwt/user/CustomEventsTest.gwt.xml b/user/test/com/google/gwt/user/CustomEventsTest.gwt.xml
new file mode 100644
index 0000000..7ed3833
--- /dev/null
+++ b/user/test/com/google/gwt/user/CustomEventsTest.gwt.xml
@@ -0,0 +1,17 @@
+<!-- -->
+<!-- Copyright 2013 Google Inc. -->
+<!-- Licensed under the Apache License, Version 2.0 (the "License"); you -->
+<!-- may not use this file except in compliance with the License. You may -->
+<!-- may obtain a copy of the License at -->
+<!-- -->
+<!-- http://www.apache.org/licenses/LICENSE-2.0 -->
+<!-- -->
+<!-- Unless required by applicable law or agreed to in writing, software -->
+<!-- distributed under the License is distributed on an "AS IS" BASIS, -->
+<!-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or -->
+<!-- implied. License for the specific language governing permissions and -->
+<!-- limitations under the License. -->
+
+<module type="fileset">
+ <inherits name="com.google.gwt.user.User"/>
+</module>
diff --git a/user/test/com/google/gwt/user/UiPart1Suite.java b/user/test/com/google/gwt/user/UiPart1Suite.java
index dba4009..9052013 100644
--- a/user/test/com/google/gwt/user/UiPart1Suite.java
+++ b/user/test/com/google/gwt/user/UiPart1Suite.java
@@ -16,6 +16,7 @@
package com.google.gwt.user;
import com.google.gwt.junit.tools.GWTTestSuite;
+import com.google.gwt.user.client.CustomEventsTest;
import com.google.gwt.user.client.ui.AbsolutePanelTest;
import com.google.gwt.user.client.ui.AnchorTest;
import com.google.gwt.user.client.ui.ButtonTest;
@@ -74,6 +75,7 @@
suite.addTestSuite(CompositeTest.class);
suite.addTestSuite(CreateEventTest.class);
suite.addTestSuite(CustomButtonTest.class);
+ suite.addTestSuite(CustomEventsTest.class);
suite.addTestSuite(CustomScrollPanelTest.class);
suite.addTestSuite(DateBoxTest.class);
suite.addTestSuite(DatePickerTest.class);
diff --git a/user/test/com/google/gwt/user/client/CustomEventsTest.java b/user/test/com/google/gwt/user/client/CustomEventsTest.java
new file mode 100644
index 0000000..9e6f26b
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/CustomEventsTest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.user.client;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.shared.GWT;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.dom.client.NativeEvent;
+import com.google.gwt.event.dom.client.DomEvent;
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.user.client.impl.DOMImpl;
+import com.google.gwt.user.client.impl.DOMImplStandard;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Tests standard DOM operations in the {@link DOM} class.
+ * <p>
+ * The test has its own module so that it will always be the first test executed in the page. By
+ * this way, we can make sure that the custom event registration always takes place before the event
+ * system gets initialized.
+ */
+public class CustomEventsTest extends GWTTestCase {
+
+ private static class CustomHandler implements EventHandler {
+ void on(DomEvent<?> customEvent) {
+ Element el = customEvent.getRelativeElement();
+ el.setTitle(el.getTitle() + "-dispatched");
+ }
+ }
+
+ private static class CustomEvent1 extends DomEvent<CustomHandler> {
+ static final Type<CustomHandler> TYPE = new Type<CustomHandler>("c1", new CustomEvent1());
+
+ @Override
+ public Type<CustomHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(CustomHandler handler) {
+ handler.on(this);
+ }
+ }
+
+ private static class CustomEvent2 extends DomEvent<CustomHandler> {
+ static final Type<CustomHandler> TYPE = new Type<CustomHandler>("c2", new CustomEvent2());
+
+ @Override
+ public Type<CustomHandler> getAssociatedType() {
+ return TYPE;
+ }
+
+ @Override
+ protected void dispatch(CustomHandler handler) {
+ handler.on(this);
+ }
+ }
+
+ private Button button;
+
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.user.CustomEventsTest";
+ }
+
+ @Override
+ protected void gwtSetUp() throws Exception {
+ ensureCustomEventDispatch();
+
+ button = new Button();
+ button.setTitle("event");
+ button.addBitlessDomHandler(new CustomHandler(), CustomEvent1.TYPE);
+ button.addBitlessDomHandler(new CustomHandler(), CustomEvent2.TYPE);
+
+ RootPanel.get().add(button);
+ }
+
+ @Override
+ protected void gwtTearDown() throws Exception {
+ RootPanel.get().remove(button);
+ }
+
+ public void testCustomEvent() {
+ if (!isStandard()) {
+ return; // Custom event support is for standard browsers (i.e. no IE 6/7/8)
+ }
+
+ dispatchEvent("c1");
+ assertEquals("event-dispatched", button.getTitle());
+ }
+
+ public void testCustomDispatchEvent() {
+ if (!isStandard()) {
+ return; // Custom event support is for standard browsers (i.e. no IE 6/7/8)
+ }
+
+ dispatchEvent("c2");
+ assertEquals("event-dispatched-custom_method", button.getTitle());
+ }
+
+ private void dispatchEvent(String evt) {
+ button.getElement().dispatchEvent(createCustomEvent(evt));
+ }
+
+ private static native NativeEvent createCustomEvent(String evt) /*-{
+ var e = $doc.createEvent("Event");
+ e.initEvent(evt, true, false);
+ return e;
+ }-*/;
+
+ private static native JavaScriptObject getBitlessCustomDisptachers() /*-{
+ return {
+ c2: function(evt) {
+ @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent(*)(evt);
+ evt.target.title += "-custom_method";
+ }
+ };
+ }-*/;
+
+ private static boolean initCustomEventDispatch = true;
+
+ private static void ensureCustomEventDispatch() {
+ if (initCustomEventDispatch) {
+ DOMImplStandard.addBitlessEventDispatchers(getBitlessCustomDisptachers());
+ initCustomEventDispatch = false;
+ }
+ }
+
+ private static boolean isStandard() {
+ return GWT.create(DOMImpl.class) instanceof DOMImplStandard;
+ }
+}