Adds isStandalone to ImageResource so Image can used an UnclippedState.
isStandalone should be true for "data:" URLs and non-inlined images
(preventInlining=true or repeatStyle=Both). In other words, it should
actually rarely be false (IE6/7).
Fixes issue 7403
Change-Id: I832fd71e17e447e642de134e7a556befa1a7f0f1
diff --git a/tools/api-checker/config/gwt25_26userApi.conf b/tools/api-checker/config/gwt25_26userApi.conf
index c151c41..82d5a92 100644
--- a/tools/api-checker/config/gwt25_26userApi.conf
+++ b/tools/api-checker/config/gwt25_26userApi.conf
@@ -138,6 +138,7 @@
:com.google.gwt.benchmarks.client.impl\
:com.google.gwt.user.client.ui.impl\
:com.google.gwt.i18n.client.impl\
+:com.google.gwt.resources.client.impl\
##############################################
#Api whitelist
diff --git a/user/src/com/google/gwt/resources/client/ImageResource.java b/user/src/com/google/gwt/resources/client/ImageResource.java
index 5f72fb8..f1885ed 100644
--- a/user/src/com/google/gwt/resources/client/ImageResource.java
+++ b/user/src/com/google/gwt/resources/client/ImageResource.java
@@ -147,4 +147,10 @@
* Return <code>true</code> if the image contains multiple frames.
*/
boolean isAnimated();
+
+ /**
+ * Returns <code>true</code> if the image is standalone, <code>false</code>
+ * if it's a region of a composite image.
+ */
+ boolean isStandalone();
}
diff --git a/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java b/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java
index 7a422d4..ffabd21 100644
--- a/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java
+++ b/user/src/com/google/gwt/resources/client/impl/ImageResourcePrototype.java
@@ -26,6 +26,7 @@
private final boolean animated;
private final boolean lossy;
+ private final boolean standalone;
private final String name;
private final SafeUri url;
private final int left;
@@ -37,7 +38,7 @@
* Only called by generated code.
*/
public ImageResourcePrototype(String name, SafeUri url, int left, int top, int width, int height,
- boolean animated, boolean lossy) {
+ boolean animated, boolean lossy, boolean standalone) {
this.name = name;
this.left = left;
this.top = top;
@@ -46,6 +47,7 @@
this.url = url;
this.animated = animated;
this.lossy = lossy;
+ this.standalone = standalone;
}
/**
@@ -95,4 +97,9 @@
public boolean isLossy() {
return lossy;
}
+
+ @Override
+ public boolean isStandalone() {
+ return standalone;
+ }
}
diff --git a/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java b/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java
index 4509439..9e25e69 100644
--- a/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/ImageResourceGenerator.java
@@ -71,6 +71,11 @@
localizedByImageResource = Maps.create();
}
+ @Override
+ public boolean isStandalone() {
+ return false;
+ }
+
public LocalizedImage addImage(TreeLogger logger, ResourceContext context,
ImageResourceDeclaration image) throws UnableToCompleteException,
CannotBundleImageException {
@@ -192,6 +197,7 @@
if (isExternal) {
return "External: " + image.get();
}
+ // test mirrored in prepare(), make sure to keep them in sync
if (image.isPreventInlining() || image.getRepeatStyle() == RepeatStyle.Both) {
return "Unbundled: " + image.get();
}
@@ -253,6 +259,8 @@
protected String normalContentsFieldName;
protected String rtlContentsFieldName;
+ public abstract boolean isStandalone();
+
public abstract ImageRect getImageRect(ImageResourceDeclaration image);
/**
@@ -296,6 +304,11 @@
}
@Override
+ public boolean isStandalone() {
+ return true;
+ }
+
+ @Override
public ImageRect getImageRect(ImageResourceDeclaration image) {
return this.image.equals(image) ? rect : null;
}
@@ -487,7 +500,8 @@
+ urlExpressions[1] + " : " + urlExpressions[0] + "),");
}
sw.println(rect.getLeft() + ", " + rect.getTop() + ", " + rect.getWidth() + ", "
- + rect.getHeight() + ", " + rect.isAnimated() + ", " + rect.isLossy());
+ + rect.getHeight() + ", " + rect.isAnimated() + ", " + rect.isLossy() + ", "
+ + bundle.isStandalone());
sw.outdent();
sw.print(")");
@@ -543,7 +557,8 @@
localizedImage = bundledImage.addImage(logger, context, image);
rect = bundledImage.getImageRect(image);
displayed = bundledImage;
- if (image.isPreventInlining()) {
+ // mirrors the "unbundled" case in BundleKey.
+ if (image.isPreventInlining() || image.getRepeatStyle() == RepeatStyle.Both) {
cannotBundle = true;
}
} catch (CannotBundleImageException e) {
diff --git a/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java b/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java
index b40d913..a122b3b 100644
--- a/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java
+++ b/user/src/com/google/gwt/user/client/ui/AbstractImagePrototype.java
@@ -71,6 +71,8 @@
* ImageResource
*/
public static AbstractImagePrototype create(ImageResource resource) {
+ // for backwards compatilibity, and to keep things simple, we treat standalone resources the
+ // same as composite ones (which means using a clear gif with the image as the CSS background)
return new ClippedImagePrototype(resource.getSafeUri(), resource.getLeft(), resource.getTop(),
resource.getWidth(), resource.getHeight());
}
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 d616f41..8a59e27 100644
--- a/user/src/com/google/gwt/user/client/ui/Image.java
+++ b/user/src/com/google/gwt/user/client/ui/Image.java
@@ -202,10 +202,12 @@
@Override
public void setUrl(Image image, SafeUri 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);
+ image.changeState(new UnclippedState(image, url));
+ }
+
+ @Override
+ public void setUrl(Image image, SafeUri url, int width, int height) {
+ image.changeState(new UnclippedState(image, url, width, height));
}
@Override
@@ -299,6 +301,8 @@
public abstract void setUrl(Image image, SafeUri url);
+ public abstract void setUrl(Image image, SafeUri url, int width, int height);
+
public abstract void setUrlAndVisibleRect(Image image, SafeUri url, int left, int top,
int width, int height);
@@ -380,6 +384,12 @@
setUrl(image, url);
}
+ UnclippedState(Image image, SafeUri url, int width, int height) {
+ this(image, url);
+ getImageElement(image).setWidth(width);
+ getImageElement(image).setHeight(height);
+ }
+
@Override
public int getHeight(Image image) {
return getImageElement(image).getHeight();
@@ -417,6 +427,13 @@
}
@Override
+ public void setUrl(Image image, SafeUri url, int width, int height) {
+ setUrl(image, url);
+ getImageElement(image).setWidth(width);
+ getImageElement(image).setHeight(height);
+ }
+
+ @Override
public void setUrlAndVisibleRect(Image image, SafeUri url, int left, int top, int width,
int height) {
image.changeState(new ClippedState(image, url, left, top, width, height));
@@ -499,8 +516,14 @@
* @param resource the ImageResource to be displayed
*/
public Image(ImageResource resource) {
- this(resource.getURL(), resource.getLeft(), resource.getTop(), resource.getWidth(),
- resource.getHeight());
+ if (resource.isStandalone()) {
+ changeState(new UnclippedState(this, resource.getSafeUri(), resource.getWidth(),
+ resource.getHeight()));
+ } else {
+ changeState(new ClippedState(this, resource.getSafeUri(), resource.getLeft(),
+ resource.getTop(), resource.getWidth(), resource.getHeight()));
+ }
+ setStyleName("gwt-Image");
}
/**
@@ -849,8 +872,12 @@
* @param resource the ImageResource to display
*/
public void setResource(ImageResource resource) {
- setUrlAndVisibleRect(resource.getSafeUri(), resource.getLeft(), resource.getTop(),
- resource.getWidth(), resource.getHeight());
+ if (resource.isStandalone()) {
+ state.setUrl(this, resource.getSafeUri(), resource.getWidth(), resource.getHeight());
+ } else {
+ state.setUrlAndVisibleRect(this, resource.getSafeUri(), resource.getLeft(),
+ resource.getTop(), resource.getWidth(), resource.getHeight());
+ }
}
/**
diff --git a/user/src/com/google/gwt/user/client/ui/ImageResourceRenderer.java b/user/src/com/google/gwt/user/client/ui/ImageResourceRenderer.java
index 962e04f..c78a5ec 100644
--- a/user/src/com/google/gwt/user/client/ui/ImageResourceRenderer.java
+++ b/user/src/com/google/gwt/user/client/ui/ImageResourceRenderer.java
@@ -15,16 +15,31 @@
*/
package com.google.gwt.user.client.ui;
+import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.text.shared.AbstractSafeHtmlRenderer;
/**
- * Given an {@link ImageResource}, renders a span element to show it.
+ * Given an {@link ImageResource}, renders an element to show it.
*/
public class ImageResourceRenderer extends AbstractSafeHtmlRenderer<ImageResource> {
+
+ interface Template extends SafeHtmlTemplates {
+ @SafeHtmlTemplates.Template("<img src='{0}' border='0' width='{1}' height='{2}'>")
+ SafeHtml image(SafeUri imageUri, int width, int height);
+ }
+
+ private static final Template TEMPLATE = GWT.create(Template.class);
+
@Override
public SafeHtml render(ImageResource image) {
- return AbstractImagePrototype.create(image).getSafeHtml();
+ if (image.isStandalone()) {
+ return TEMPLATE.image(image.getSafeUri(), image.getWidth(), image.getHeight());
+ } else {
+ return AbstractImagePrototype.create(image).getSafeHtml();
+ }
}
}
diff --git a/user/test/com/google/gwt/cell/client/ImageResourceCellTest.java b/user/test/com/google/gwt/cell/client/ImageResourceCellTest.java
index 9f3ab8b..0142fce 100644
--- a/user/test/com/google/gwt/cell/client/ImageResourceCellTest.java
+++ b/user/test/com/google/gwt/cell/client/ImageResourceCellTest.java
@@ -18,7 +18,7 @@
import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
-import com.google.gwt.user.client.ui.AbstractImagePrototype;
+import com.google.gwt.user.client.ui.ImageResourceRenderer;
/**
* Tests for {@link ImageResourceCell}.
@@ -56,7 +56,7 @@
@Override
protected String getExpectedInnerHtml() {
- return AbstractImagePrototype.create(getImages().prettyPiccy()).getHTML();
+ return new ImageResourceRenderer().render(getImages().prettyPiccy()).asString();
}
@Override
diff --git a/user/test/com/google/gwt/resources/client/ImageResourceTest.java b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
index 55141d6..225634a 100644
--- a/user/test/com/google/gwt/resources/client/ImageResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/ImageResourceTest.java
@@ -38,7 +38,15 @@
@ImageOptions(preventInlining = true)
@Source("32x32.png")
- ImageResource i32x32();
+ ImageResource i32x32();
+
+ @ImageOptions(repeatStyle = RepeatStyle.Both)
+ @Source("16x16.png")
+ ImageResource i16x16RepeatBoth();
+
+ @ImageOptions(repeatStyle = RepeatStyle.Both)
+ @Source("32x32.png")
+ ImageResource i32x32RepeatBoth();
}
interface Resources extends ClientBundle {
@@ -163,6 +171,7 @@
// Make sure that the large, lossy image isn't bundled with the rest
assertTrue(((ImageResourcePrototype) lossy).isLossy());
assertTrue(!i64.getSafeUri().equals(lossy.getSafeUri()));
+ assertTrue(lossy.isStandalone());
assertEquals(16, r.i16x16Vertical().getWidth());
assertEquals(16, r.i16x16Vertical().getHeight());
@@ -191,10 +200,32 @@
// No image packing
assertEquals(0, a.getTop());
assertEquals(0, a.getLeft());
+ assertTrue(a.isStandalone());
assertEquals(0, b.getTop());
assertEquals(0, b.getLeft());
+ assertTrue(b.isStandalone());
}
-
+
+ /**
+ * Tests that RepeatStyle.Both creates standalone images.
+ */
+ public void testRepeatStyleBoth() {
+ ExternalResources r = GWT.create(ExternalResources.class);
+ ImageResource a = r.i16x16RepeatBoth();
+ ImageResource b = r.i32x32RepeatBoth();
+
+ // Should be fetched from different URLs
+ assertFalse(a.getURL().equals(b.getURL()));
+
+ // No image packing
+ assertEquals(0, a.getTop());
+ assertEquals(0, a.getLeft());
+ assertTrue(a.isStandalone());
+ assertEquals(0, b.getTop());
+ assertEquals(0, b.getLeft());
+ assertTrue(b.isStandalone());
+ }
+
@SuppressWarnings("deprecation")
public void testSafeUri() {
Resources r = GWT.create(Resources.class);
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 758bb69..b232e15 100644
--- a/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
+++ b/user/test/com/google/gwt/uibinder/test/client/UiBinderTest.java
@@ -20,6 +20,8 @@
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ParagraphElement;
import com.google.gwt.dom.client.SpanElement;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource.NotStrict;
@@ -544,6 +546,12 @@
assertEquals("funny characters \\ \" ' ' & < > > { }", t);
}
+ /**
+ * Fails in all modes due to an HtmlUnit bug: offsetWidth always returns 1256.
+ * TODO(t.broyer): file a new HtmlUnit bug.
+ * Similar to http://sourceforge.net/p/htmlunit/bugs/1447/
+ */
+ @DoNotRunWith(Platform.HtmlUnitBug)
public void testCustomImageClass() {
ImageResource resource = widgetUi.prettyImage;
Image widget = widgetUi.fooImage;
@@ -553,6 +561,12 @@
assertEquals(resource.getLeft(), widget.getOriginLeft());
}
+ /**
+ * Fails in all modes due to an HtmlUnit bug: offsetWidth always returns 1256.
+ * TODO(t.broyer): file a new HtmlUnit bug.
+ * Similar to http://sourceforge.net/p/htmlunit/bugs/1447/
+ */
+ @DoNotRunWith(Platform.HtmlUnitBug)
public void testImageResourceInImageWidget() {
ImageResource resource = widgetUi.prettyImage;
Image widget = widgetUi.babyWidget;
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 5fa9bf1..4845f30 100644
--- a/user/test/com/google/gwt/user/client/ui/ImageTest.java
+++ b/user/test/com/google/gwt/user/client/ui/ImageTest.java
@@ -26,8 +26,11 @@
import com.google.gwt.junit.DoNotRunWith;
import com.google.gwt.junit.Platform;
import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.junit.client.WithProperties;
+import com.google.gwt.junit.client.WithProperties.Property;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.ImageResource;
+import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.user.client.Timer;
/**
@@ -37,6 +40,10 @@
public class ImageTest extends GWTTestCase {
interface Bundle extends ClientBundle {
ImageResource prettyPiccy();
+
+ @Source("prettyPiccy.png")
+ @ImageOptions(preventInlining = true)
+ ImageResource prettyPiccyStandalone();
}
private static class TestErrorHandler implements ErrorHandler {
@@ -609,17 +616,48 @@
}.schedule(LOAD_EVENT_TIMEOUT);
}
+ @WithProperties({
+ @Property(name = "ClientBundle.enableInlining", value = "false")
+ })
public void testResourceConstructor() {
Bundle b = GWT.create(Bundle.class);
Image image = new Image(b.prettyPiccy());
assertResourceWorked(image, b.prettyPiccy());
+
+ assertFalse(b.prettyPiccy().isStandalone());
+ assertEquals("clipped", getCurrentImageStateName(image));
}
+ @WithProperties({
+ @Property(name = "ClientBundle.enableInlining", value = "false")
+ })
public void testSetResource() {
Bundle b = GWT.create(Bundle.class);
Image image = new Image();
image.setResource(b.prettyPiccy());
assertResourceWorked(image, b.prettyPiccy());
+
+ assertFalse(b.prettyPiccy().isStandalone());
+ assertEquals("clipped", getCurrentImageStateName(image));
+ }
+
+ public void testStandaloneResourceConstructor() {
+ Bundle b = GWT.create(Bundle.class);
+ Image image = new Image(b.prettyPiccyStandalone());
+ assertResourceWorked(image, b.prettyPiccyStandalone());
+
+ assertTrue(b.prettyPiccyStandalone().isStandalone());
+ assertEquals("unclipped", getCurrentImageStateName(image));
+ }
+
+ public void testStandaloneSetResource() {
+ Bundle b = GWT.create(Bundle.class);
+ Image image = new Image();
+ image.setResource(b.prettyPiccyStandalone());
+ assertResourceWorked(image, b.prettyPiccyStandalone());
+
+ assertTrue(b.prettyPiccyStandalone().isStandalone());
+ assertEquals("unclipped", getCurrentImageStateName(image));
}
/**