Ensures that onload events are fired for images even if the browser fires the onload event synchronously while the image is not attached.
Patch by: jlabanca
Review by: jgw (Partial TBR)
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7092 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java b/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java
index 5fe9b26..5b02b1c 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplOpera.java
@@ -59,7 +59,7 @@
elem.onscroll = (bits & 0x04000) ?
@com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
elem.onload = (bits & 0x08000) ?
- @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
+ @com.google.gwt.user.client.impl.DOMImplStandard::dispatchUnhandledEvent : null;
elem.onerror = (bits & 0x10000) ?
@com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
elem.onmousewheel = (bits & 0x20000) ?
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 4160aab..414e4c3 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
@@ -38,6 +38,9 @@
@SuppressWarnings("unused")
private static JavaScriptObject dispatchEvent;
+ @SuppressWarnings("unused")
+ private static JavaScriptObject dispatchUnhandledEvent;
+
@Override
public Element eventGetFromElement(Event evt) {
if (evt.getType().equals("mouseover")) {
@@ -165,6 +168,11 @@
return true;
});
+ @com.google.gwt.user.client.impl.DOMImplStandard::dispatchUnhandledEvent = $entry(function(evt) {
+ this.__gwtLastUnhandledEvent = evt.type;
+ @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent.call(this, evt);
+ });
+
@com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent = $entry(function(evt) {
var listener, curElem = this;
while (curElem && !(listener = curElem.__listener)) {
@@ -231,7 +239,7 @@
if (chMask & 0x04000) elem.onscroll = (bits & 0x04000) ?
@com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
if (chMask & 0x08000) elem.onload = (bits & 0x08000) ?
- @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
+ @com.google.gwt.user.client.impl.DOMImplStandard::dispatchUnhandledEvent : null;
if (chMask & 0x10000) elem.onerror = (bits & 0x10000) ?
@com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent : null;
if (chMask & 0x20000) elem.onmousewheel = (bits & 0x20000) ?
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 19ea536..8f90472 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,9 @@
@SuppressWarnings("unused")
private static JavaScriptObject dispatchDblClickEvent;
+ @SuppressWarnings("unused")
+ private static JavaScriptObject dispatchUnhandledEvent;
+
/**
* 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.
@@ -151,6 +154,11 @@
}
});
+ @com.google.gwt.user.client.impl.DOMImplTrident::dispatchUnhandledEvent = $entry(function() {
+ this.__gwtLastUnhandledEvent = $wnd.event.type;
+ @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent.call(this);
+ });
+
// We need to create these delegate functions to fix up the 'this' context.
// Normally, 'this' is the firing element, but this is only true for
// 'onclick = ...' event handlers, not for handlers setup via attachEvent().
@@ -243,7 +251,7 @@
if (chMask & 0x04000) elem.onscroll = (bits & 0x04000) ?
@com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
if (chMask & 0x08000) elem.onload = (bits & 0x08000) ?
- @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+ @com.google.gwt.user.client.impl.DOMImplTrident::dispatchUnhandledEvent : null;
if (chMask & 0x10000) elem.onerror = (bits & 0x10000) ?
@com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
if (chMask & 0x20000) elem.onmousewheel = (bits & 0x20000) ?
diff --git a/user/src/com/google/gwt/user/client/ui/Image.java b/user/src/com/google/gwt/user/client/ui/Image.java
index dd95468..0035d6a 100644
--- a/user/src/com/google/gwt/user/client/ui/Image.java
+++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 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
@@ -19,6 +19,7 @@
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
+import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.ErrorEvent;
@@ -43,6 +44,8 @@
import com.google.gwt.event.dom.client.MouseWheelHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.impl.ClippedImageImpl;
@@ -57,16 +60,20 @@
* image is constructed, and how it is transformed after construction. Methods
* will operate differently depending on the mode that the image is in. These
* differences are detailed in the documentation for each method.
- *
+ *
* <p>
* If an image transitions between clipped mode and unclipped mode, any
* {@link Element}-specific attributes added by the user (including style
* attributes, style names, and style modifiers), except for event listeners,
* will be lost.
* </p>
- *
- * <h3>CSS Style Rules</h3> <ul class="css"> <li>.gwt-Image { }</li> </ul>
- *
+ *
+ * <h3>CSS Style Rules</h3>
+ * <dl>
+ * <dt>.gwt-Image</dt>
+ * </dd>The outer element</dd>
+ * </dl>
+ *
* Tranformations between clipped and unclipped state will result in a loss of
* any style names that were set/added; the only style names that are preserved
* are those that are mentioned in the static CSS style rules. Due to
@@ -75,7 +82,7 @@
* expected when an image is in clipped mode. These limitations can usually be
* easily worked around by encapsulating the image in a container widget that
* can itself be styled.
- *
+ *
* <p>
* <h3>Example</h3>
* {@example com.google.gwt.examples.ImageExample}
@@ -87,6 +94,12 @@
HasAllMouseHandlers, SourcesMouseEvents {
/**
+ * The attribute that is set when an image fires a native load or error event
+ * before it is attached.
+ */
+ private static final String UNHANDLED_EVENT_ATTR = "__gwtLastUnhandledEvent";
+
+ /**
* Implementation of behaviors associated with the clipped state of an image.
*/
private static class ClippedState extends State {
@@ -119,6 +132,11 @@
}
@Override
+ public ImageElement getImageElement(Image image) {
+ return impl.getImgElement(image).cast();
+ }
+
+ @Override
public int getOriginLeft() {
return left;
}
@@ -140,7 +158,10 @@
@Override
public void setUrl(Image image, String url) {
- image.changeState(new UnclippedState(image, url));
+ image.changeState(new UnclippedState(image));
+ // Need to make sure we change the state before an onload event can fire,
+ // or handlers will be fired while we are in the old state.
+ image.setUrl(url);
}
@Override
@@ -156,7 +177,7 @@
this.height = height;
impl.adjust(image.getElement(), url, left, top, width, height);
- impl.fireSyntheticLoadEvent(image);
+ fireSyntheticLoadEvent(image);
}
}
@@ -177,7 +198,7 @@
this.height = height;
impl.adjust(image.getElement(), url, left, top, width, height);
- impl.fireSyntheticLoadEvent(image);
+ fireSyntheticLoadEvent(image);
}
}
@@ -196,6 +217,8 @@
public abstract int getHeight(Image image);
+ public abstract ImageElement getImageElement(Image image);
+
public abstract int getOriginLeft();
public abstract int getOriginTop();
@@ -204,6 +227,16 @@
public abstract int getWidth(Image image);
+ public void onLoad(Image image) {
+ // If an onload event fired while the image wasn't attached, we need to
+ // synthesize one now.
+ String unhandledEvent = getImageElement(image).getPropertyString(
+ UNHANDLED_EVENT_ATTR);
+ if ("load".equals(unhandledEvent)) {
+ fireSyntheticLoadEvent(image);
+ }
+ }
+
public abstract void setUrl(Image image, String url);
public abstract void setUrlAndVisibleRect(Image image, String url,
@@ -212,6 +245,27 @@
public abstract void setVisibleRect(Image image, int left, int top,
int width, int height);
+ /**
+ * We need to synthesize a load event in case the image loads synchronously,
+ * before our handlers can be attached.
+ *
+ * @param image the image on which to dispatch the event
+ */
+ protected void fireSyntheticLoadEvent(final Image image) {
+ /*
+ * We use a deferred command here to simulate the native version of the
+ * event as closely as possible. In the native event case, it is unlikely
+ * that a second load event would occur while you are in the load event
+ * handler.
+ */
+ DeferredCommand.addCommand(new Command() {
+ public void execute() {
+ NativeEvent evt = Document.get().createLoadEvent();
+ getImageElement(image).dispatchEvent(evt);
+ }
+ });
+ }
+
// This method is used only by unit tests.
protected abstract String getStateName();
}
@@ -248,7 +302,12 @@
@Override
public int getHeight(Image image) {
- return image.getImageElement().getHeight();
+ return getImageElement(image).getHeight();
+ }
+
+ @Override
+ public ImageElement getImageElement(Image image) {
+ return image.getElement().cast();
}
@Override
@@ -263,17 +322,18 @@
@Override
public String getUrl(Image image) {
- return image.getImageElement().getSrc();
+ return getImageElement(image).getSrc();
}
@Override
public int getWidth(Image image) {
- return image.getImageElement().getWidth();
+ return getImageElement(image).getWidth();
}
@Override
public void setUrl(Image image, String url) {
- image.getImageElement().setSrc(url);
+ image.clearUnhandledEvent();
+ getImageElement(image).setSrc(url);
}
@Override
@@ -305,7 +365,7 @@
/**
* Causes the browser to pre-fetch the image at a given URL.
- *
+ *
* @param url the URL of the image to be prefetched
*/
public static void prefetch(String url) {
@@ -316,11 +376,11 @@
/**
* Creates a Image widget that wraps an existing <img> element.
- *
+ *
* This element must already be attached to the document. If the element is
* removed from the document, you must call
* {@link RootPanel#detachNow(Widget)}.
- *
+ *
* @param element the element to be wrapped
*/
public static Image wrap(Element element) {
@@ -349,18 +409,18 @@
/**
* Creates an image whose size and content are defined by an ImageResource.
- *
+ *
* @param resource the ImageResource to be displayed
*/
public Image(ImageResource resource) {
- this(resource.getURL(), resource.getLeft(),
- resource.getTop(), resource.getWidth(), resource.getHeight());
+ this(resource.getURL(), resource.getLeft(), resource.getTop(),
+ resource.getWidth(), resource.getHeight());
}
/**
* Creates an image with a specified URL. The load event will be fired once
* the image at the given URL has been retrieved by the browser.
- *
+ *
* @param url the URL of the image to be displayed
*/
public Image(String url) {
@@ -377,7 +437,7 @@
* the width and height are specified explicitly by the user, this behavior
* will not cause problems with retrieving the width and height of a clipped
* image in a load event handler.
- *
+ *
* @param url the URL of the image to be displayed
* @param left the horizontal co-ordinate of the upper-left vertex of the
* visibility rectangle
@@ -394,7 +454,7 @@
/**
* This constructor may be used by subclasses to explicitly use an existing
* element. This element must be an <img> element.
- *
+ *
* @param element the element to be used
*/
protected Image(Element element) {
@@ -436,9 +496,9 @@
}
/**
- * @deprecated Use {@link #addMouseOverHandler} {@link
- * #addMouseMoveHandler}, {@link #addMouseDownHandler}, {@link
- * #addMouseUpHandler} and {@link #addMouseOutHandler} instead
+ * @deprecated Use {@link #addMouseOverHandler} {@link #addMouseMoveHandler},
+ * {@link #addMouseDownHandler}, {@link #addMouseUpHandler} and
+ * {@link #addMouseOutHandler} instead
*/
@Deprecated
public void addMouseListener(MouseListener listener) {
@@ -477,7 +537,7 @@
* Gets the height of the image. When the image is in the unclipped state, the
* height of the image is not known until the image has been loaded (i.e. load
* event has been fired for the image).
- *
+ *
* @return the height of the image, or 0 if the height is unknown
*/
public int getHeight() {
@@ -489,7 +549,7 @@
* visibility rectangle. If the image is in the unclipped state, then the
* visibility rectangle is assumed to be the rectangle which encompasses the
* entire image, which has an upper-left vertex of (0,0).
- *
+ *
* @return the horizontal co-ordinate of the upper-left vertex of the image's
* visibility rectangle
*/
@@ -502,7 +562,7 @@
* visibility rectangle. If the image is in the unclipped state, then the
* visibility rectangle is assumed to be the rectangle which encompasses the
* entire image, which has an upper-left vertex of (0,0).
- *
+ *
* @return the vertical co-ordinate of the upper-left vertex of the image's
* visibility rectangle
*/
@@ -514,7 +574,7 @@
* Gets the URL of the image. The URL that is returned is not necessarily the
* URL that was passed in by the user. It may have been transformed to an
* absolute URL.
- *
+ *
* @return the image URL
*/
public String getUrl() {
@@ -525,16 +585,27 @@
* Gets the width of the image. When the image is in the unclipped state, the
* width of the image is not known until the image has been loaded (i.e. load
* event has been fired for the image).
- *
+ *
* @return the width of the image, or 0 if the width is unknown
*/
public int getWidth() {
return state.getWidth(this);
}
+ @Override
+ public void onBrowserEvent(Event event) {
+ // We have to clear the unhandled event before firing handlers because the
+ // handlers could trigger onLoad, which would refire the event.
+ if (event.getTypeInt() == Event.ONLOAD) {
+ clearUnhandledEvent();
+ }
+
+ super.onBrowserEvent(event);
+ }
+
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler} method on
- * the object returned by {@link #addClickHandler} instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #addClickHandler} instead
*/
@Deprecated
public void removeClickListener(ClickListener listener) {
@@ -542,8 +613,8 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by an add*Handler method instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by an add*Handler method instead
*/
@Deprecated
public void removeLoadListener(LoadListener listener) {
@@ -551,8 +622,8 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by an add*Handler method instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by an add*Handler method instead
*/
@Deprecated
public void removeMouseListener(MouseListener listener) {
@@ -560,9 +631,8 @@
}
/**
- * @deprecated Use the {@link HandlerRegistration#removeHandler}
- * method on the object returned by {@link #addMouseWheelHandler}
- * instead
+ * @deprecated Use the {@link HandlerRegistration#removeHandler} method on the
+ * object returned by {@link #addMouseWheelHandler} instead
*/
@Deprecated
public void removeMouseWheelListener(MouseWheelListener listener) {
@@ -576,7 +646,7 @@
* image's current url or current visibility rectangle co-ordinates. If the
* image is currently in the unclipped state, a call to this method will cause
* a transition to the clipped state.
- *
+ *
* @param resource the ImageResource to display
*/
public void setResource(ImageResource resource) {
@@ -589,7 +659,7 @@
* state, a call to this method will cause a transition of the image to the
* unclipped state. Regardless of whether or not the image is in the clipped
* or unclipped state, a load event will be fired.
- *
+ *
* @param url the image URL
*/
public void setUrl(String url) {
@@ -603,7 +673,7 @@
* visibility rectangle co-ordinates. If the image is currently in the
* unclipped state, a call to this method will cause a transition to the
* clipped state.
- *
+ *
* @param url the image URL
* @param left the horizontal coordinate of the upper-left vertex of the
* visibility rectangle
@@ -626,7 +696,7 @@
* is in the unclipped state, a call to this method will cause a transition of
* the image to the clipped state. This transition will cause a load event to
* fire.
- *
+ *
* @param left the horizontal coordinate of the upper-left vertex of the
* visibility rectangle
* @param top the vertical coordinate of the upper-left vertex of the
@@ -638,11 +708,25 @@
state.setVisibleRect(this, left, top, width, height);
}
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+
+ // Issue 863: the state may need to fire a synthetic event if the native
+ // onload event fired while the image was detached.
+ state.onLoad(this);
+ }
+
private void changeState(State newState) {
state = newState;
}
- private ImageElement getImageElement() {
- return getElement().cast();
+ /**
+ * Clear the unhandled event.
+ */
+ private void clearUnhandledEvent() {
+ if (state != null) {
+ state.getImageElement(this).setPropertyString(UNHANDLED_EVENT_ATTR, "");
+ }
}
}
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
index 4e78056..9d112af 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImpl.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 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
@@ -18,22 +18,19 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.ui.Image;
/**
* Uses a combination of a clear image and a background image to clip all except
* a desired portion of an underlying image.
- *
+ *
* Do not use this class - it is used for implementation only, and its methods
* may change in the future.
*/
public class ClippedImageImpl {
public void adjust(Element img, String url, int left, int top, int width,
- int height) {
+ int height) {
String style = "url(" + url + ") no-repeat " + (-left + "px ")
+ (-top + "px");
img.getStyle().setProperty("background", style);
@@ -42,7 +39,7 @@
}
public Element createStructure(String url, int left, int top, int width,
- int height) {
+ int height) {
Element tmp = Document.get().createSpanElement();
tmp.setInnerHTML(getHTML(url, left, top, width, height));
return tmp.getFirstChildElement();
@@ -53,31 +50,15 @@
+ "px; background: url(" + url + ") no-repeat " + (-left + "px ")
+ (-top + "px");
- String clippedImgHtml = "<img src='" + GWT.getModuleBaseURL() +
- "clear.cache.gif' style='" + style + "' border='0'>";
+ String clippedImgHtml = "<img "
+ + "onload='this.__gwtLastUnhandledEvent=\"load\";' src='"
+ + GWT.getModuleBaseURL() + "clear.cache.gif' style='" + style
+ + "' border='0'>";
return clippedImgHtml;
}
- public void fireSyntheticLoadEvent(final Image image) {
- /*
- * We need to synthesize a load event, because the native events that are
- * fired would correspond to the loading of clear.cache.gif, which is
- * incorrect. A native event would not even fire in Internet Explorer,
- * because the root element is a wrapper element around the <img> element.
- * Since we are synthesizing a load event, we do not need to sink the
- * onload event.
- *
- * We use a deferred command here to simulate the native version of the
- * load event as closely as possible. In the native event case, it is
- * unlikely that a second load event would occur while you are in the load
- * event handler.
- */
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- NativeEvent evt = Document.get().createLoadEvent();
- image.getElement().dispatchEvent(evt);
- }
- });
+ public Element getImgElement(Image image) {
+ return image.getElement();
}
}
diff --git a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java
index a06a5ce..9341f34 100644
--- a/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java
+++ b/user/src/com/google/gwt/user/client/ui/impl/ClippedImageImplIE6.java
@@ -16,11 +16,7 @@
package com.google.gwt.user.client.ui.impl;
import com.google.gwt.core.client.GWT;
-import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
-import com.google.gwt.dom.client.NativeEvent;
-import com.google.gwt.user.client.Command;
-import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Image;
@@ -113,25 +109,6 @@
return clipper;
}
- public void fireSyntheticLoadEvent(final Image image) {
- if (!isIE6) {
- super.fireSyntheticLoadEvent(image);
- return;
- }
-
- // This is the same as the superclass' implementation, except that it
- // explicitly checks for the 'clipper' element, and dispatches the event
- // on the img (you can't dispatch events on the clipper).
- DeferredCommand.addCommand(new Command() {
- public void execute() {
- NativeEvent evt = Document.get().createLoadEvent();
- Element clipper = image.getElement();
- Element img = clipper.getFirstChildElement();
- img.dispatchEvent(evt);
- }
- });
- }
-
@Override
public String getHTML(String url, int left, int top, int width, int height) {
if (!isIE6) {
@@ -158,7 +135,7 @@
*/
String clippedImgHtml = "<gwt:clipper style=\""
+ clipperStyle
- + "\"><img src='"
+ + "\"><img onload='this.__gwtLastUnhandledEvent=\"load\";' src='"
+ moduleBaseUrlProtocol
+ "' onerror='if(window.__gwt_transparentImgHandler)window.__gwt_transparentImgHandler(this);else this.src=\"" + GWT.getModuleBaseURL() + "clear.cache.gif\"' style=\""
+ imgStyle + "\" width=" + (left + width) + " height=" + (top + height)
@@ -166,4 +143,12 @@
return clippedImgHtml;
}
+
+ @Override
+ public Element getImgElement(Image image) {
+ if (!isIE6) {
+ return super.getImgElement(image);
+ }
+ return image.getElement().getFirstChildElement();
+ }
}
diff --git a/user/test/com/google/gwt/resources/client/ImageResourceTest.java b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
index 9da158a..1f4e3c8 100644
--- a/user/test/com/google/gwt/resources/client/ImageResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
@@ -92,13 +92,7 @@
assertFalse(a.getURL().equals(r.i16x16().getURL()));
}
- /**
- * Issue 863: Image.onload event does not fire on Internet Explorer when image
- * is in cache.
- *
- * TODO(jlabanca): Reenable this test after fixing the issue.
- */
- public void disabledTestDedup() {
+ public void testDedup() {
Resources r = GWT.create(Resources.class);
ImageResource a = r.i64x64();
diff --git a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
index 623a29b..6ff73ff 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -41,6 +41,7 @@
/**
* Functional test of UiBinder.
*/
+@DoNotRunWith({Platform.HtmlUnit})
public class UiBinderTest extends GWTTestCase {
private WidgetBasedUi widgetUi;
private DomBasedUi domUi;
diff --git a/user/test/com/google/gwt/user/client/ui/ImageTest.java b/user/test/com/google/gwt/user/client/ui/ImageTest.java
index 42ac527..f3e05fe 100644
--- a/user/test/com/google/gwt/user/client/ui/ImageTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ImageTest.java
@@ -1,12 +1,12 @@
/*
* Copyright 2008 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
@@ -27,12 +27,13 @@
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.user.client.Timer;
/**
* Tests for the Image widget. Images in both clipped mode and unclipped mode
* are tested, along with the transitions between the two modes.
*/
-@SuppressWarnings("deprecation")
+@DoNotRunWith({Platform.HtmlUnit})
public class ImageTest extends GWTTestCase {
interface Bundle extends ClientBundle {
ImageResource prettyPiccy();
@@ -50,6 +51,24 @@
}
}
+ private abstract static class TestLoadHandler implements LoadHandler {
+ private boolean finished = false;
+
+ /**
+ * Mark the test as finished.
+ */
+ public void finish() {
+ finished = true;
+ }
+
+ /**
+ * @return true if the test has finished
+ */
+ public boolean isFinished() {
+ return finished;
+ }
+ }
+
@Deprecated
private abstract static class TestLoadListener implements LoadListener {
private boolean finished = false;
@@ -82,7 +101,7 @@
* Helper method that allows us to 'peek' at the private <code>state</code>
* field in the Image object, and call the <code>state.getStateName()</code>
* method.
- *
+ *
* @param image The image instance
* @return "unclipped" if image is in the unclipped state, or "clipped" if the
* image is in the clipped state
@@ -96,25 +115,24 @@
private int firedLoad;
+ @Override
+ public String getModuleName() {
+ return "com.google.gwt.user.UserTest";
+ }
+
/**
* Tests the transition from the clipped state to the unclipped state.
- *
- * Disabled because of issue #863 & #864. It fails intermittently in linux
- * hosted mode tests.
*/
- public void disabledTestChangeClippedImageToUnclipped() {
+ public void testChangeClippedImageToUnclipped() {
final Image image = new Image("counting-forwards.png", 12, 13, 8, 8);
assertEquals("clipped", getCurrentImageStateName(image));
delayTestFinish(5000);
- image.addLoadListener(new LoadListener() {
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(new LoadHandler() {
private int onLoadEventCount = 0;
- public void onError(Widget sender) {
- fail("The image " + ((Image) sender).getUrl() + " failed to load.");
- }
-
- public void onLoad(Widget sender) {
+ public void onLoad(LoadEvent event) {
++onLoadEventCount;
if (onLoadEventCount == 1) { // Set the url after the first image loads
image.setUrl("counting-forwards.png");
@@ -134,22 +152,17 @@
/**
* Tests the transition from the unclipped state to the clipped state.
- *
- * Disabled because of issue #863.
*/
- public void disabledTestChangeImageToClipped() {
+ public void testChangeImageToClipped() {
final Image image = new Image("counting-forwards.png");
assertEquals("unclipped", getCurrentImageStateName(image));
delayTestFinish(5000);
- image.addLoadListener(new LoadListener() {
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(new LoadHandler() {
private int onLoadEventCount = 0;
- public void onError(Widget sender) {
- fail("The image " + ((Image) sender).getUrl() + " failed to load.");
- }
-
- public void onLoad(Widget sender) {
+ public void onLoad(LoadEvent event) {
if (getCurrentImageStateName(image).equals("unclipped")) {
image.setVisibleRect(12, 13, 8, 8);
}
@@ -170,21 +183,16 @@
/**
* Tests the creation of an image in unclipped mode.
- *
- * Disabled because of issue #863 & #864.
*/
- public void disabledTestCreateImage() {
+ public void testCreateImage() {
final Image image = new Image("counting-forwards.png");
delayTestFinish(5000);
- image.addLoadListener(new LoadListener() {
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(new LoadHandler() {
private int onLoadEventCount = 0;
- public void onError(Widget sender) {
- fail("The image " + ((Image) sender).getUrl() + " failed to load.");
- }
-
- public void onLoad(Widget sender) {
+ public void onLoad(LoadEvent event) {
if (++onLoadEventCount == 1) {
assertEquals(32, image.getWidth());
assertEquals(32, image.getHeight());
@@ -200,24 +208,40 @@
}
/**
+ * Tests the creation of an image that does not exist.
+ */
+ public void testCreateImageWithError() {
+ final Image image = new Image("imageDoesNotExist.png");
+
+ delayTestFinish(5000);
+ image.addErrorHandler(new ErrorHandler() {
+ public void onError(ErrorEvent event) {
+ finishTest();
+ }
+ });
+ image.addLoadHandler(new LoadHandler() {
+ public void onLoad(LoadEvent event) {
+ fail("The image " + image.getUrl() + " should have failed to load.");
+ }
+ });
+
+ RootPanel.get().add(image);
+ }
+
+ /**
* Tests the firing of onload events when
* {@link com.google.gwt.user.client.ui.Image#setUrl(String)} is called on an
* unclipped image.
- *
- * Disabled because of issue #863
*/
- public void disabledTestSetUrlAndLoadEventsOnUnclippedImage() {
+ public void testSetUrlAndLoadEventsOnUnclippedImage() {
final Image image = new Image();
delayTestFinish(5000);
- image.addLoadListener(new LoadListener() {
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(new LoadHandler() {
private int onLoadEventCount = 0;
- public void onError(Widget sender) {
- fail("The image " + ((Image) sender).getUrl() + " failed to load.");
- }
-
- public void onLoad(Widget sender) {
+ public void onLoad(LoadEvent event) {
if (++onLoadEventCount == 2) {
finishTest();
} else {
@@ -232,23 +256,19 @@
/**
* Tests the behavior of
- * <code>setUrlAndVisibleRect(String, int, int, int, int)</code> method on
- * an unclipped image, which causes a state transition to the clipped state.
- *
- * Disabled because of issue #863.
+ * <code>setUrlAndVisibleRect(String, int, int, int, int)</code> method on an
+ * unclipped image, which causes a state transition to the clipped state.
*/
- public void disabledTestSetUrlAndVisibleRectOnUnclippedImage() {
+ public void testSetUrlAndVisibleRectOnUnclippedImage() {
final Image image = new Image("counting-backwards.png");
+ assertEquals("unclipped", getCurrentImageStateName(image));
delayTestFinish(5000);
- image.addLoadListener(new LoadListener() {
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(new LoadHandler() {
private int onLoadEventCount = 0;
- public void onError(Widget sender) {
- fail("The image " + ((Image) sender).getUrl() + " failed to load.");
- }
-
- public void onLoad(Widget sender) {
+ public void onLoad(LoadEvent event) {
if (getCurrentImageStateName(image).equals("unclipped")) {
image.setUrlAndVisibleRect("counting-forwards.png", 0, 16, 16, 16);
}
@@ -265,19 +285,13 @@
});
RootPanel.get().add(image);
- assertEquals("unclipped", getCurrentImageStateName(image));
- }
-
- @Override
- public String getModuleName() {
- return "com.google.gwt.user.UserTest";
}
/**
* Tests the creation of an image in clipped mode.
*/
- @DoNotRunWith({Platform.HtmlUnit})
- public void disabledTestCreateClippedImage() {
+ @SuppressWarnings("deprecation")
+ public void testCreateClippedImage() {
final Image image = new Image("counting-forwards.png", 16, 16, 16, 16);
delayTestFinish(5000);
@@ -343,6 +357,83 @@
assertEquals(1, firedError);
}
+ /**
+ * Verify that detaching and reattaching an image in a handler does not fire a
+ * second onload event.
+ */
+ public void testNoEventOnReattachInHandler() {
+ final Image image = new Image("counting-forwards.png");
+
+ delayTestFinish(5000);
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(new LoadHandler() {
+ private int onLoadEventCount = 0;
+
+ public void onLoad(LoadEvent event) {
+ if (++onLoadEventCount == 1) {
+ RootPanel.get().remove(image);
+ RootPanel.get().add(image);
+ // The extra onLoad would will fire synchronously before finishTest().
+ finishTest();
+ } else {
+ fail("onLoad fired on reattach.");
+ }
+ }
+ });
+
+ RootPanel.get().add(image);
+ }
+
+ public void testOneEventOnly() {
+ final Image image = new Image("counting-forwards.png");
+
+ final TestLoadHandler loadHandler = new TestLoadHandler() {
+ public void onLoad(LoadEvent event) {
+ if (isFinished()) {
+ fail("LoadHandler fired multiple times.");
+ }
+ finish();
+ }
+ };
+ delayTestFinish(6000);
+ new Timer() {
+ @Override
+ public void run() {
+ assertTrue(loadHandler.isFinished());
+ finishTest();
+ }
+ }.schedule(5000);
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(loadHandler);
+
+ RootPanel.get().add(image);
+ }
+
+ public void testOneEventOnlyClippedImage() {
+ final Image image = new Image("counting-forwards.png", 12, 13, 8, 8);
+
+ final TestLoadHandler loadHandler = new TestLoadHandler() {
+ public void onLoad(LoadEvent event) {
+ if (isFinished()) {
+ fail("LoadHandler fired multiple times.");
+ }
+ finish();
+ }
+ };
+ delayTestFinish(6000);
+ new Timer() {
+ @Override
+ public void run() {
+ assertTrue(loadHandler.isFinished());
+ finishTest();
+ }
+ }.schedule(5000);
+ image.addErrorHandler(new TestErrorHandler(image));
+ image.addLoadHandler(loadHandler);
+
+ RootPanel.get().add(image);
+ }
+
public void testResourceConstructor() {
Bundle b = GWT.create(Bundle.class);
Image image = new Image(b.prettyPiccy());
@@ -361,8 +452,8 @@
* {@link com.google.gwt.user.client.ui.Image#setUrlAndVisibleRect(String,int,int,int,int)}
* on a clipped image.
*/
- @DoNotRunWith({Platform.HtmlUnit})
- public void disabledTestSetUrlAndVisibleRectOnClippedImage() {
+ @SuppressWarnings("deprecation")
+ public void testSetUrlAndVisibleRectOnClippedImage() {
final Image image = new Image("counting-backwards.png", 12, 12, 12, 12);
delayTestFinish(5000);
@@ -412,8 +503,8 @@
* {@link com.google.gwt.user.client.ui.Image#setVisibleRect(int,int,int,int)}
* on a clipped image.
*/
- @DoNotRunWith({Platform.HtmlUnit})
- public void disabledTestSetVisibleRectAndLoadEventsOnClippedImage() {
+ @SuppressWarnings("deprecation")
+ public void testSetVisibleRectAndLoadEventsOnClippedImage() {
final Image image = new Image("counting-backwards.png", 16, 16, 16, 16);
delayTestFinish(5000);
diff --git a/user/test/com/google/gwt/user/client/ui/TreeItemTest.java b/user/test/com/google/gwt/user/client/ui/TreeItemTest.java
index 8e037d8..679f4c2 100644
--- a/user/test/com/google/gwt/user/client/ui/TreeItemTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TreeItemTest.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.user.client.ui;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
/**
@@ -39,6 +41,7 @@
assertEquals("Test", widget.getText());
}
+ @DoNotRunWith({Platform.HtmlUnit})
public void testSetWidgetNullWithError() {
// Create a widget that will throw an exception onUnload.
BadWidget badWidget = new BadWidget();
diff --git a/user/test/com/google/gwt/user/client/ui/TreeTest.java b/user/test/com/google/gwt/user/client/ui/TreeTest.java
index b581cf9..f6a520f 100644
--- a/user/test/com/google/gwt/user/client/ui/TreeTest.java
+++ b/user/test/com/google/gwt/user/client/ui/TreeTest.java
@@ -15,6 +15,8 @@
*/
package com.google.gwt.user.client.ui;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
@@ -37,6 +39,7 @@
return "com.google.gwt.user.DebugTest";
}
+ @DoNotRunWith({Platform.HtmlUnit})
public void testAttachDetachOrder() {
HasWidgetsTester.testAll(new Tree(), new Adder(), true);
}