Allows multiple GWT apps to preview native events in IE.
Patch by: jlabanca
Review by: jgw
Issue: 3892
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@6163 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/eclipse/reference/code-museum/Issue3892.launch b/eclipse/reference/code-museum/Issue3892.launch
new file mode 100644
index 0000000..3766ff0
--- /dev/null
+++ b/eclipse/reference/code-museum/Issue3892.launch
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/code-museum"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="4"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.eclipse.jdt.launching.JRE_CONTAINER" javaProject="code-museum" path="1" type="4"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/code-museum/src" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/src" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-dev-linux/core/src" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/super" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-dev-linux/core/super" path="3" type="2"/> "/>
+<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry id="org.eclipse.jdt.launching.classpathentry.defaultClasspath"> <memento exportedEntriesOnly="false" project="code-museum"/> </runtimeClasspathEntry> "/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.HostedMode"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-startupUrl Issue3892.html com.google.gwt.museum.Issue3892Module1 com.google.gwt.museum.Issue3892Module2 com.google.gwt.museum.Issue3892Module3"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="code-museum"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Xmx256M -Dgwt.devjar=${gwt_devjar}""/>
+</launchConfiguration>
diff --git a/reference/code-museum/src/com/google/gwt/museum/Issue3892Module1.gwt.xml b/reference/code-museum/src/com/google/gwt/museum/Issue3892Module1.gwt.xml
new file mode 100644
index 0000000..d83fadc
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/Issue3892Module1.gwt.xml
@@ -0,0 +1,28 @@
+<!-- -->
+<!-- Copyright 2009 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 rename-to="issue3892Module1">
+
+ <!-- Inherit the core Web Toolkit stuff. -->
+ <inherits name='com.google.gwt.user.User' />
+ <inherits name='com.google.gwt.museum.Museum' />
+ <inherits name="com.google.gwt.user.theme.standard.StandardResources" />
+ <inherits name="com.google.gwt.user.theme.chrome.ChromeResources" />
+ <inherits name="com.google.gwt.user.theme.dark.DarkResources" />
+
+ <!-- Specify the app entry point class. -->
+ <entry-point class='com.google.gwt.museum.client.defaultmuseum.Issue3892EntryPoint1' />
+ <source path="client/common" />
+ <source path="client/defaultmuseum" />
+ <source path="client/viewer" />
+</module>
diff --git a/reference/code-museum/src/com/google/gwt/museum/Issue3892Module2.gwt.xml b/reference/code-museum/src/com/google/gwt/museum/Issue3892Module2.gwt.xml
new file mode 100644
index 0000000..47fb0eb
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/Issue3892Module2.gwt.xml
@@ -0,0 +1,28 @@
+<!-- -->
+<!-- Copyright 2009 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 rename-to="issue3892Module2">
+
+ <!-- Inherit the core Web Toolkit stuff. -->
+ <inherits name='com.google.gwt.user.User' />
+ <inherits name='com.google.gwt.museum.Museum' />
+ <inherits name="com.google.gwt.user.theme.standard.StandardResources" />
+ <inherits name="com.google.gwt.user.theme.chrome.ChromeResources" />
+ <inherits name="com.google.gwt.user.theme.dark.DarkResources" />
+
+ <!-- Specify the app entry point class. -->
+ <entry-point class="com.google.gwt.museum.client.defaultmuseum.Issue3892EntryPoint2" />
+ <source path="client/common" />
+ <source path="client/defaultmuseum" />
+ <source path="client/viewer" />
+</module>
diff --git a/reference/code-museum/src/com/google/gwt/museum/Issue3892Module3.gwt.xml b/reference/code-museum/src/com/google/gwt/museum/Issue3892Module3.gwt.xml
new file mode 100644
index 0000000..0b42303
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/Issue3892Module3.gwt.xml
@@ -0,0 +1,28 @@
+<!-- -->
+<!-- Copyright 2009 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 rename-to="issue3892Module3">
+
+ <!-- Inherit the core Web Toolkit stuff. -->
+ <inherits name='com.google.gwt.user.User' />
+ <inherits name='com.google.gwt.museum.Museum' />
+ <inherits name="com.google.gwt.user.theme.standard.StandardResources" />
+ <inherits name="com.google.gwt.user.theme.chrome.ChromeResources" />
+ <inherits name="com.google.gwt.user.theme.dark.DarkResources" />
+
+ <!-- Specify the app entry point class. -->
+ <entry-point class="com.google.gwt.museum.client.defaultmuseum.Issue3892EntryPoint3" />
+ <source path="client/common" />
+ <source path="client/defaultmuseum" />
+ <source path="client/viewer" />
+</module>
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint1.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint1.java
new file mode 100644
index 0000000..dd3b799
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint1.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2009 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.museum.client.defaultmuseum;
+
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.museum.client.common.AbstractIssue;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.Grid;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Only a single GWT application can preview native events.
+ */
+public class Issue3892EntryPoint1 extends AbstractIssue {
+
+ public static final String BUTTON_1_ID = "Issue3892Button1";
+ public static final String BUTTON_2_ID = "Issue3892Button2";
+ public static final String BUTTON_3_ID = "Issue3892Button3";
+
+ /**
+ * The main grid used for layout.
+ */
+ private Grid grid = new Grid(1, 3);
+
+ @Override
+ public Widget createIssue() {
+ Window.alert("Module 1 loaded");
+
+ // Setup the grid.
+ grid.setHTML(0, 0, "<b>Test<b>");
+ grid.setHTML(0, 1, "<b>Description<b>");
+ grid.setHTML(0, 2, "<b>Expected Results<b>");
+ addTest(BUTTON_1_ID, "Event is not cancelled by any module.",
+ "The event will fire in the button.", false);
+ addTest(BUTTON_2_ID, "Module 1 cancels event.",
+ "The event will not fire in the button.", true);
+ addTest(BUTTON_3_ID, "Module 2 cancels event.",
+ "The event will not fire in the button.", true);
+
+ // Add the event preview.
+ Event.addNativePreviewHandler(new NativePreviewHandler() {
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ if (event.getTypeInt() == Event.ONCLICK) {
+ Element target = event.getNativeEvent().getEventTarget().cast();
+ if (BUTTON_2_ID.equals(target.getId())) {
+ event.cancel();
+ Window.alert("Click handled by module 1 and cancelled");
+ } else {
+ Window.alert("Click handled by module 1");
+ }
+ }
+ }
+ });
+
+ return grid;
+ }
+
+ @Override
+ public String getInstructions() {
+ return "After all three modules have loaded (indicated by alert boxes), "
+ + "click the buttons and verify that you see the expected results. "
+ + "For each test, all three modules should preview the event (even if "
+ + "one of the modules cancels the event).";
+ }
+
+ @Override
+ public String getSummary() {
+ return "Only a single GWT application can preview native events";
+ }
+
+ @Override
+ public boolean hasCSS() {
+ return false;
+ }
+
+ /**
+ * Add a test button to the grid.
+ *
+ * @param buttonId the ID of the button
+ * @param description the test description
+ * @param results the expected result of the test
+ * @param isCancelled true if one of the modules will cancel the event
+ */
+ private void addTest(String buttonId, String description, String results,
+ final boolean isCancelled) {
+ int row = grid.getRowCount();
+ grid.resizeRows(row + 1);
+
+ // Add the test button.
+ Button button = new Button("Run Test", new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ if (isCancelled) {
+ Window.alert("[Error] Event should have been cancelled");
+ } else {
+ Window.alert("[Success] Event successfully fired");
+ }
+ }
+ });
+ button.getElement().setId(buttonId);
+ grid.setWidget(row, 0, button);
+
+ // Add the description and expected results.
+ grid.setText(row, 1, description);
+ grid.setText(row, 2, results);
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint2.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint2.java
new file mode 100644
index 0000000..1231698
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint2.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2009 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.museum.client.defaultmuseum;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.dom.client.Element;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
+
+/**
+ * Only a single GWT application can preview native events.
+ */
+public class Issue3892EntryPoint2 implements EntryPoint {
+ public void onModuleLoad() {
+ Window.alert("Module 2 loaded");
+ Event.addNativePreviewHandler(new NativePreviewHandler() {
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ if (event.getTypeInt() == Event.ONCLICK) {
+ Element target = event.getNativeEvent().getEventTarget().cast();
+ if (Issue3892EntryPoint1.BUTTON_3_ID.equals(target.getId())) {
+ event.cancel();
+ Window.alert("Click handled by module 2 and cancelled");
+ } else {
+ Window.alert("Click handled by module 2");
+ }
+ }
+ }
+ });
+ }
+}
diff --git a/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint3.java b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint3.java
new file mode 100644
index 0000000..4f52d40
--- /dev/null
+++ b/reference/code-museum/src/com/google/gwt/museum/client/defaultmuseum/Issue3892EntryPoint3.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2009 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.museum.client.defaultmuseum;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
+
+/**
+ * Only a single GWT application can preview native events.
+ */
+public class Issue3892EntryPoint3 implements EntryPoint {
+ public void onModuleLoad() {
+ Window.alert("Module 3 loaded");
+ Event.addNativePreviewHandler(new NativePreviewHandler() {
+ public void onPreviewNativeEvent(NativePreviewEvent event) {
+ if (event.getTypeInt() == Event.ONCLICK) {
+ Window.alert("Click handled by module 3");
+ }
+ }
+ });
+ }
+}
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java b/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
index 09509e6..af01b54 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
@@ -31,6 +31,23 @@
@SuppressWarnings("unused")
private static JavaScriptObject dispatchDblClickEvent;
+ /**
+ * Let every GWT app on the page preview the current event. If any app cancels
+ * the event, the event will be canceled for all apps.
+ *
+ * @return <code>false</code> to cancel the event
+ */
+ @SuppressWarnings("unused")
+ private static native boolean previewEventImpl() /*-{
+ var isCancelled = false;
+ for (var i = 0; i < $wnd.__gwt_globalEventArray.length; i++) {
+ if (!$wnd.__gwt_globalEventArray[i]()) {
+ isCancelled = true;
+ }
+ }
+ return !isCancelled;
+ }-*/;
+
@Override
public native Element eventGetFromElement(Event evt) /*-{
// Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
@@ -68,6 +85,24 @@
@Override
public native void initEventSystem() /*-{
+ // All GWT apps on the page register themselves with the globelEventArray
+ // so that the first app to handle an event can allow all apps on the page
+ // to preview it. See issue 3892 for more details.
+ //
+ // Apps cannot just mark the event as they visit it for a few reasons.
+ // First, window level event handlers fire last in IE, so the first app to
+ // cancel the event will be the last to see it. Second, window events do
+ // not support arbitrary attributes, and the only writable field is the
+ // returnValue, which has another use. Finally, window events are not
+ // comparable (ex. a=event; b=event; a!=b), so we cannot keep a list of
+ // events that have already been previewed by the current app.
+ if ($wnd.__gwt_globalEventArray == null) {
+ $wnd.__gwt_globalEventArray = new Array();
+ }
+ $wnd.__gwt_globalEventArray[$wnd.__gwt_globalEventArray.length] = function() {
+ return @com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event);
+ }
+
@com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent = function() {
// IE doesn't define event.currentTarget, so we squirrel it away here. It
// also seems that IE won't allow you to add expandos to the event object,
@@ -76,9 +111,12 @@
var oldEventTarget = @com.google.gwt.dom.client.DOMImplTrident::currentEventTarget;
@com.google.gwt.dom.client.DOMImplTrident::currentEventTarget = this;
+ // The first GWT app on the page to handle the event allows all apps to
+ // preview it before continuing or cancelling, which is consistent with
+ // other browsers.
if ($wnd.event.returnValue == null) {
$wnd.event.returnValue = true;
- if (!@com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event)) {
+ if (!@com.google.gwt.user.client.impl.DOMImplTrident::previewEventImpl()()) {
@com.google.gwt.dom.client.DOMImplTrident::currentEventTarget = oldEventTarget;
return;
}
@@ -109,7 +147,7 @@
} else if ($wnd.event.returnValue == null) {
// Ensure that we preview the event even if we aren't handling it.
$wnd.event.returnValue = true;
- @com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event);
+ @com.google.gwt.user.client.impl.DOMImplTrident::previewEventImpl()();
}
};