Restore support for derived DataResource in @url CSS extensions

GWT supports references to Data and Image resource using the @url
CSS extension. In 2.6.0, a test was introduced to check that the
type of the method referenced in the @url extension is indeed of
type DataResource or ImageResource. This test is too strict though
and does not allow DataResource and ImageResource subclasses, which
were allowed before 2.6.0. This patch makes the test more lenient
to accept derived DataResource and ImageResource.

Bug: issue 8564
Change-Id: If81e5f1250e28a5a3150b26d3ffa290d780a26a0
diff --git a/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java b/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java
index 0fe893f..fdbd901 100644
--- a/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java
+++ b/user/src/com/google/gwt/resources/css/SubstitutionReplacer.java
@@ -109,25 +109,21 @@
           throw new CssCompilerException("Cannot find data method");
         }
 
-        if (!methodType.equals(dataResourceType) &&
-            !methodType.equals(imageResourceType)) {
+        if (!(methodType instanceof JClassType) ||
+            (!dataResourceType.isAssignableFrom((JClassType) methodType) &&
+            !imageResourceType.isAssignableFrom((JClassType) methodType))) {
           String message = "Invalid method type for url substitution: " + methodType + ". " +
               "Only DataResource and ImageResource are supported.";
           logger.log(TreeLogger.ERROR, message);
           throw new CssCompilerException(message);
         }
 
-        String instance = "((" + methodType.getQualifiedSourceName() + ")("
-            + context.getImplementationSimpleSourceName() + ".this."
-            + functionName.getExpression() + "))";
-
         StringBuilder expression = new StringBuilder();
         expression.append("\"url('\" + ");
-        if (methodType.equals(dataResourceType)) {
-          expression.append(instance).append(".getUrl()");
-        } else if (methodType.equals(imageResourceType)) {
-          expression.append(instance).append(".getURL()");
-        }
+        expression.append(context.getImplementationSimpleSourceName());
+        expression.append(".this.");
+        expression.append(functionName.getExpression());
+        expression.append(".getSafeUri().asString()");
         expression.append(" + \"')\"");
         result.add(new ExpressionValue(expression.toString()));
       } else {
diff --git a/user/test/com/google/gwt/resources/client/CSSResourceTest.java b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
index be907ae..d4daa15 100644
--- a/user/test/com/google/gwt/resources/client/CSSResourceTest.java
+++ b/user/test/com/google/gwt/resources/client/CSSResourceTest.java
@@ -21,6 +21,7 @@
 import com.google.gwt.resources.client.CssResource.ImportedWithPrefix;
 import com.google.gwt.resources.client.CssResource.Shared;
 
+
 /**
  * Contains various full-stack tests of the CssResource system.
  */
@@ -186,6 +187,12 @@
     FullTestCss css();
 
     @Source("32x32.png")
+    CustomDataResource customDataMethod();
+
+    @Source("16x16.png")
+    CustomImageResource customSpriteMethod();
+
+    @Source("32x32.png")
     DataResource dataMethod();
 
     // Test default extensions
@@ -310,13 +317,17 @@
 
     // Check data URL expansion
     assertTrue(text.contains("backgroundTopLevel:url('"
-        + Resources.INSTANCE.dataMethod().getUrl() + "')"));
+        + Resources.INSTANCE.dataMethod().getSafeUri().asString() + "')"));
     assertTrue(text.contains("backgroundNested:url('"
-        + Resources.INSTANCE.nested().dataMethod().getUrl() + "')"));
+        + Resources.INSTANCE.nested().dataMethod().getSafeUri().asString() + "')"));
+    assertTrue(text.contains("backgroundCustom:url('"
+        + Resources.INSTANCE.customDataMethod().getSafeUri().asString() + "')"));
     assertTrue(text.contains("backgroundImage:url('"
-        + Resources.INSTANCE.spriteMethod().getURL() + "')"));
+        + Resources.INSTANCE.spriteMethod().getSafeUri().asString() + "')"));
     assertTrue(text.contains("backgroundImageNested:url('"
-        + Resources.INSTANCE.nested().spriteMethod().getURL() + "')"));
+        + Resources.INSTANCE.nested().spriteMethod().getSafeUri().asString() + "')"));
+    assertTrue(text.contains("backgroundImageCustom:url('"
+        + Resources.INSTANCE.customSpriteMethod().getSafeUri().asString() + "')"));
 
     // Check @eval expansion
     assertTrue(text.contains(red() + ";"));
diff --git a/user/test/com/google/gwt/resources/client/CustomDataResource.java b/user/test/com/google/gwt/resources/client/CustomDataResource.java
new file mode 100644
index 0000000..6658a61
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/CustomDataResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 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.resources.client;
+
+import com.google.gwt.resources.ext.ResourceGeneratorType;
+import com.google.gwt.resources.rg.CustomDataResourceGenerator;
+
+/**
+ * Custom subtype of {@link DataResource}, to test {@code @url} in CssResource.
+ */
+@ResourceGeneratorType(CustomDataResourceGenerator.class)
+public interface CustomDataResource extends DataResource {
+}
diff --git a/user/test/com/google/gwt/resources/client/CustomImageResource.java b/user/test/com/google/gwt/resources/client/CustomImageResource.java
new file mode 100644
index 0000000..84e067a
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/CustomImageResource.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014 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.resources.client;
+
+import com.google.gwt.resources.ext.ResourceGeneratorType;
+import com.google.gwt.resources.rg.CustomImageResourceGenerator;
+
+/**
+ * Custom subtype of {@link ImageResource}, to test {@code @url} in CssResource.
+ */
+@ResourceGeneratorType(CustomImageResourceGenerator.class)
+public interface CustomImageResource extends ImageResource {
+}
diff --git a/user/test/com/google/gwt/resources/client/impl/CustomDataResourcePrototype.java b/user/test/com/google/gwt/resources/client/impl/CustomDataResourcePrototype.java
new file mode 100644
index 0000000..f8c65fd
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/impl/CustomDataResourcePrototype.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2014 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.resources.client.impl;
+
+import com.google.gwt.resources.client.CustomDataResource;
+import com.google.gwt.safehtml.shared.SafeUri;
+
+/**
+ * Default implementation of {@link CustomDataResource}.
+ */
+public class CustomDataResourcePrototype extends DataResourcePrototype implements CustomDataResource {
+  public CustomDataResourcePrototype(String name, SafeUri uri) {
+    super(name, uri);
+  }
+}
diff --git a/user/test/com/google/gwt/resources/client/impl/CustomImageResourcePrototype.java b/user/test/com/google/gwt/resources/client/impl/CustomImageResourcePrototype.java
new file mode 100644
index 0000000..9bcbc8e
--- /dev/null
+++ b/user/test/com/google/gwt/resources/client/impl/CustomImageResourcePrototype.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2014 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.resources.client.impl;
+
+import com.google.gwt.resources.client.CustomImageResource;
+import com.google.gwt.safehtml.shared.SafeUri;
+
+/**
+ * Default implementation of {@link CustomImageResource}.
+ */
+public class CustomImageResourcePrototype extends ImageResourcePrototype implements
+    CustomImageResource {
+
+  public CustomImageResourcePrototype(String name, SafeUri url) {
+    super(name, url, 0, 0, 0, 0, false, false);
+  }
+}
diff --git a/user/test/com/google/gwt/resources/client/test.css b/user/test/com/google/gwt/resources/client/test.css
index f93d24c..f5e6c4a 100644
--- a/user/test/com/google/gwt/resources/client/test.css
+++ b/user/test/com/google/gwt/resources/client/test.css
@@ -18,8 +18,10 @@
 @eval RED com.google.gwt.resources.client.CSSResourceTest.red();
 @url BACKGROUND dataMethod;
 @url NESTEDBACKGROUND nested.dataMethod;
+@url CUSTOMBACKGROUND customDataMethod;
 @url BACKGROUNDIMAGE spriteMethod;
 @url NESTEDBACKGROUNDIMAGE nested.spriteMethod;
+@url CUSTOMBACKGROUNDIMAGE customSpriteMethod;
 
 /* Check @def expansion */
 @def SPRITEWIDTH value("spriteMethod.getWidth", "px");
@@ -59,8 +61,10 @@
     direction: DIRECTION;
     backgroundTopLevel: BACKGROUND;
     backgroundNested: NESTEDBACKGROUND;
+    backgroundCustom: CUSTOMBACKGROUND;
     backgroundImage: BACKGROUNDIMAGE;
     backgroundImageNested: NESTEDBACKGROUNDIMAGE;
+    backgroundImageCustom: CUSTOMBACKGROUNDIMAGE;
 }
 
 div[foo="bar"] {
diff --git a/user/test/com/google/gwt/resources/rg/CustomDataResourceGenerator.java b/user/test/com/google/gwt/resources/rg/CustomDataResourceGenerator.java
new file mode 100644
index 0000000..6c33af4
--- /dev/null
+++ b/user/test/com/google/gwt/resources/rg/CustomDataResourceGenerator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014 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.resources.rg;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.resources.client.impl.CustomDataResourcePrototype;
+import com.google.gwt.resources.ext.AbstractResourceGenerator;
+import com.google.gwt.resources.ext.ResourceContext;
+import com.google.gwt.resources.ext.ResourceGeneratorUtil;
+import com.google.gwt.safehtml.shared.UriUtils;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.StringSourceWriter;
+
+import java.net.URL;
+
+/**
+ * Generator for {@link CustomDataResource}.
+ */
+public class CustomDataResourceGenerator extends AbstractResourceGenerator {
+  @Override
+  public String createAssignment(TreeLogger logger, ResourceContext context, JMethod method)
+      throws UnableToCompleteException {
+    URL[] resources = ResourceGeneratorUtil.findResources(logger, context, method);
+
+    if (resources.length != 1) {
+      logger.log(TreeLogger.ERROR, "Exactly one resource must be specified", null);
+      throw new UnableToCompleteException();
+    }
+
+    URL resource = resources[0];
+
+    SourceWriter sw = new StringSourceWriter();
+    sw.println("new " + CustomDataResourcePrototype.class.getName() + "(");
+    sw.indent();
+    sw.println('"' + method.getName() + "\",");
+    // We don't care about it actually working, so just use the resource URL
+    sw.println(UriUtils.class.getName() + ".fromTrustedString(\"" + resource.toExternalForm() + "\")");
+    sw.outdent();
+    sw.print(")");
+
+    return sw.toString();
+  }
+}
diff --git a/user/test/com/google/gwt/resources/rg/CustomImageResourceGenerator.java b/user/test/com/google/gwt/resources/rg/CustomImageResourceGenerator.java
new file mode 100644
index 0000000..357d652
--- /dev/null
+++ b/user/test/com/google/gwt/resources/rg/CustomImageResourceGenerator.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2014 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.resources.rg;
+
+import com.google.gwt.core.ext.TreeLogger;
+import com.google.gwt.core.ext.UnableToCompleteException;
+import com.google.gwt.core.ext.typeinfo.JMethod;
+import com.google.gwt.resources.client.impl.CustomImageResourcePrototype;
+import com.google.gwt.resources.ext.AbstractResourceGenerator;
+import com.google.gwt.resources.ext.ResourceContext;
+import com.google.gwt.resources.ext.ResourceGeneratorUtil;
+import com.google.gwt.safehtml.shared.UriUtils;
+import com.google.gwt.user.rebind.SourceWriter;
+import com.google.gwt.user.rebind.StringSourceWriter;
+
+import java.net.URL;
+
+/**
+ * Generator for {@link CustomImageResource}.
+ */
+public class CustomImageResourceGenerator extends AbstractResourceGenerator {
+
+  @Override
+  public String createAssignment(TreeLogger logger, ResourceContext context, JMethod method)
+      throws UnableToCompleteException {
+    URL[] resources = ResourceGeneratorUtil.findResources(logger, context, method);
+
+    if (resources.length != 1) {
+      logger.log(TreeLogger.ERROR, "Exactly one resource must be specified", null);
+      throw new UnableToCompleteException();
+    }
+
+    URL resource = resources[0];
+
+    SourceWriter sw = new StringSourceWriter();
+    sw.println("new " + CustomImageResourcePrototype.class.getName() + "(");
+    sw.indent();
+    sw.println('"' + method.getName() + "\",");
+    // We don't care about it actually working, so just use the resource URL
+    sw.println(UriUtils.class.getName() + ".fromTrustedString(\"" + resource.toExternalForm() + "\")");
+    sw.outdent();
+    sw.print(")");
+
+    return sw.toString();
+  }
+}