Add GSS support in UiBinder.

Third patch concerning the support of GSS in GWT. This patch
introduces a new attribute named useGss that you can use
in order to enable GSS in the inline style of UiBinder

Change-Id: I7e6f907c3ffa4a4c8f642786506de2f9c4107e85
diff --git a/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java b/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java
new file mode 100644
index 0000000..610ef26
--- /dev/null
+++ b/user/src/com/google/gwt/resources/gss/ClassNamesCollector.java
@@ -0,0 +1,73 @@
+/*
+ * 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.gss;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.resources.rg.GssResourceGenerator;
+import com.google.gwt.thirdparty.common.css.compiler.ast.CssClassSelectorNode;
+import com.google.gwt.thirdparty.common.css.compiler.ast.CssTree;
+import com.google.gwt.thirdparty.common.css.compiler.ast.DefaultTreeVisitor;
+import com.google.gwt.thirdparty.guava.common.base.Preconditions;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * Collect all CSS class names in a stylesheet.
+ */
+public class ClassNamesCollector extends DefaultTreeVisitor {
+  private Set<String>  classNames;
+  private SortedSet<String> excludedPrefixes;
+
+  /**
+   * Extract all CSS class names in the provided stylesheet, modulo those
+   * imported from another context.
+   */
+  public Set<String> getClassNames(CssTree tree, Set<JClassType> imports) {
+    Preconditions.checkNotNull(tree, "tree cannot be null");
+    Preconditions.checkNotNull(imports, "imports set cannot be null");
+
+    classNames = new HashSet<String>();
+    excludedPrefixes = new TreeSet<String>();
+
+    for (JClassType importedType : imports) {
+      excludedPrefixes.add(GssResourceGenerator.getImportPrefix(importedType));
+    }
+
+    tree.getVisitController().startVisit(this);
+
+    return classNames;
+  }
+
+  @Override
+  public boolean enterClassSelector(CssClassSelectorNode classSelector) {
+    String className = classSelector.getRefinerName();
+
+    if (!isImportedClass(className)) {
+      classNames.add(className);
+    }
+
+    return false;
+  }
+
+  private boolean isImportedClass(String className) {
+    SortedSet<String> lowerPrefixes = excludedPrefixes.headSet(className);
+
+    return !lowerPrefixes.isEmpty() && className.startsWith(lowerPrefixes.last());
+  }
+}
diff --git a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
index 8206f98..ecf6c3c 100644
--- a/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/CssResourceGenerator.java
@@ -539,6 +539,9 @@
       throw new UnableToCompleteException();
     }
 
+    // At this point, gss is not enabled so we shouldn't try to compile a gss file.
+    ensureNoGssFile(resources, logger);
+
     // Create the AST and do a quick scan for requirements
     CssStylesheet sheet = GenerateCssAst.exec(logger, resources);
     checkSheet(logger, sheet);
@@ -546,6 +549,19 @@
     (new RequirementsCollector(logger, context.getRequirements())).accept(sheet);
   }
 
+  private void ensureNoGssFile(URL[] resources, TreeLogger logger)
+      throws UnableToCompleteException {
+
+    for (URL stylesheet : resources) {
+      if (stylesheet.getFile().endsWith(".gss")) {
+        logger.log(Type.ERROR, "GSS is not enabled. Add the following line to your gwt.xml file " +
+            "to enable it: " +
+            "<set-configuration-property name=\"CssResource.enableGss\" value=\"true\" />");
+        throw new UnableToCompleteException();
+      }
+    }
+  }
+
   protected void checkSheet(TreeLogger logger, CssStylesheet stylesheet)
   throws UnableToCompleteException {
     // Do nothing
diff --git a/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java b/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java
index 8093068..5c9f088 100644
--- a/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java
+++ b/user/src/com/google/gwt/resources/rg/GssResourceGenerator.java
@@ -243,6 +243,19 @@
       'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', '0', '1',
       '2', '3', '4', '5', '6'};
 
+  /**
+   * Returns the import prefix for a type, including the trailing hyphen.
+   */
+  public static String getImportPrefix(JClassType importType) {
+    String prefix = importType.getSimpleSourceName();
+    ImportedWithPrefix exp = importType.getAnnotation(ImportedWithPrefix.class);
+    if (exp != null) {
+      prefix = exp.value();
+    }
+
+    return prefix + "-";
+  }
+
   private static String encode(long id) {
     assert id >= 0;
 
@@ -1106,7 +1119,7 @@
       // This substitution map will prefix each renamed class with the resource prefix and use a
       // MinimalSubstitutionMap for computing the obfuscated name.
       SubstitutionMap prefixingSubstitutionMap = new PrefixingSubstitutionMap(
-          new MinimalSubstitutionMap(), obfuscationPrefix + resourcePrefix + "_");
+          new MinimalSubstitutionMap(), obfuscationPrefix + resourcePrefix + "-");
 
       for (JMethod method : cssResource.getOverridableMethods()) {
         if (method == getNameMethod || method == getTextMethod || method == ensuredInjectedMethod) {
@@ -1139,17 +1152,4 @@
       replacementsForSharedMethods.put(method, obfuscatedClassName);
     }
   }
-
-  /**
-   * Returns the import prefix for a type, including the trailing hyphen.
-   */
-  private String getImportPrefix(JClassType importType) {
-    String prefix = importType.getSimpleSourceName();
-    ImportedWithPrefix exp = importType.getAnnotation(ImportedWithPrefix.class);
-    if (exp != null) {
-      prefix = exp.value();
-    }
-
-    return prefix + "-";
-  }
 }
diff --git a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
index c1cd78b..b7e16ca 100644
--- a/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
+++ b/user/src/com/google/gwt/uibinder/rebind/UiBinderParser.java
@@ -92,6 +92,7 @@
   private static final String REPEAT_STYLE_ATTRIBUTE = "repeatStyle";
   private static final String SOURCE_ATTRIBUTE = "src";
   private static final String TYPE_ATTRIBUTE = "type";
+  private static final String GSS_ATTRIBUTE = "gss";
 
   // TODO(rjrjr) Make all the ElementParsers receive their dependencies via
   // constructor like this one does, and make this an ElementParser. I want
@@ -445,8 +446,10 @@
       importTypes.add(findCssResourceType(elem, type));
     }
 
+    Boolean gss = elem.consumeBooleanConstantAttribute(GSS_ATTRIBUTE);
+
     ImplicitCssResource cssMethod = bundleClass.createCssResource(name, source,
-        publicType, body, importTypes, resourceOracle);
+        publicType, body, importTypes, gss, resourceOracle);
 
     FieldWriter field = fieldManager.registerFieldForGeneratedCssResource(cssMethod);
     field.setInitializer(String.format("%s.%s()",
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java
index 3e98c33..e1afec8 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitClientBundle.java
@@ -63,14 +63,15 @@
    * @param body the inline css text
    * @param importTypes for the {@literal @}Import annotation, if any. LinkedHashSet
    *          to enforce deterministic order across recompiles
+   * @param gss indicates that GSS is used or not
    * @param resourceOracle from which to load resources
    * @return the newly-created CssResource
    */
   public ImplicitCssResource createCssResource(String name, String[] source,
       JClassType extendedInterface, String body, LinkedHashSet<JClassType> importTypes,
-      ResourceOracle resourceOracle) {
+      Boolean gss, ResourceOracle resourceOracle) {
     ImplicitCssResource css = new ImplicitCssResource(packageName, cssBaseName + name, name, source,
-        extendedInterface, body, logger, importTypes, resourceOracle);
+        extendedInterface, body, logger, importTypes, gss, resourceOracle);
     cssMethods.add(css);
     return css;
   }
diff --git a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
index e3012ec..df4b742 100644
--- a/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
+++ b/user/src/com/google/gwt/uibinder/rebind/model/ImplicitCssResource.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.uibinder.rebind.model;
 
+import com.google.gwt.core.ext.TreeLogger;
 import com.google.gwt.core.ext.UnableToCompleteException;
 import com.google.gwt.core.ext.impl.ResourceLocatorImpl;
 import com.google.gwt.core.ext.typeinfo.JClassType;
@@ -23,6 +24,11 @@
 import com.google.gwt.resources.css.GenerateCssAst;
 import com.google.gwt.resources.css.ast.CssStylesheet;
 import com.google.gwt.resources.ext.ResourceGeneratorUtil;
+import com.google.gwt.resources.gss.ClassNamesCollector;
+import com.google.gwt.thirdparty.common.css.SourceCode;
+import com.google.gwt.thirdparty.common.css.compiler.ast.CssTree;
+import com.google.gwt.thirdparty.common.css.compiler.ast.GssParser;
+import com.google.gwt.thirdparty.common.css.compiler.ast.GssParserException;
 import com.google.gwt.uibinder.attributeparsers.CssNameConverter;
 import com.google.gwt.uibinder.rebind.MortalLogger;
 
@@ -55,6 +61,7 @@
   private final MortalLogger logger;
   private final Set<JClassType> imports;
   private final ResourceOracle resourceOracle;
+  private final boolean gss;
 
   private File generatedFile;
   private Set<String> cssClassNames;
@@ -65,7 +72,8 @@
    */
   public ImplicitCssResource(String packageName, String className, String name,
       String[] source, JClassType extendedInterface, String body,
-      MortalLogger logger, Set<JClassType> importTypes, ResourceOracle resourceOracle) {
+      MortalLogger logger, Set<JClassType> importTypes, Boolean gss,
+      ResourceOracle resourceOracle) {
     this.packageName = packageName;
     this.className = className;
     this.name = name;
@@ -74,6 +82,7 @@
     this.logger = logger;
     this.imports = Collections.unmodifiableSet(importTypes);
     this.resourceOracle = resourceOracle;
+    this.gss = Boolean.TRUE.equals(gss);
     sources = Arrays.asList(source);
   }
 
@@ -85,9 +94,9 @@
   }
 
   /**
-   * Returns the set of CSS classnames in the underlying .css files.
+   * Returns the set of CSS classnames in the underlying css or gss files.
    *
-   * @throws UnableToCompleteException if the user has called for a .css file we
+   * @throws UnableToCompleteException if the user has called for a css/gss file we
    *           can't find.
    */
   public Set<String> getCssClassNames() throws UnableToCompleteException {
@@ -103,10 +112,23 @@
       }
       assert urls.size() > 0;
 
-      CssStylesheet sheet = GenerateCssAst.exec(logger.getTreeLogger(),
-          urls.toArray(new URL[urls.size()]));
-      cssClassNames = ExtractClassNamesVisitor.exec(sheet,
-          imports.toArray(new JClassType[imports.size()]));
+      if (gss) {
+        SourceCode sourceCode = new SourceCode(bodyFile.getName(), body);
+        CssTree tree;
+        try {
+          tree = new GssParser(sourceCode).parse();
+        } catch (GssParserException e) {
+          logger.getTreeLogger().log(TreeLogger.ERROR, "Unable to parse CSS", e);
+          throw new UnableToCompleteException();
+        }
+        cssClassNames = new ClassNamesCollector().getClassNames(tree, imports);
+
+      } else {
+        CssStylesheet sheet = GenerateCssAst.exec(logger.getTreeLogger(),
+            urls.toArray(new URL[urls.size()]));
+        cssClassNames = ExtractClassNamesVisitor.exec(sheet,
+            imports.toArray(new JClassType[imports.size()]));
+      }
     }
     return cssClassNames;
   }
@@ -168,7 +190,7 @@
   }
 
   /**
-   * Returns the name of the .css file(s), separate by white space.
+   * Returns the name of the css or gss file(s), separate by white space.
    */
   public Collection<String> getSource() {
     if (body.length() == 0) {
@@ -181,7 +203,8 @@
   }
 
   private String getBodyFileName() {
-    String bodyFileName = String.format("uibinder.%s.%s.css", packageName, className);
+    String bodyFileName = String.format("uibinder.%s.%s.%s", packageName, className,
+        getCssFileExtension());
     // To verify that the resulting file can be retrieved out of zip files using a URL reference.
     assert isValidUrl("file:/" + bodyFileName);
     return bodyFileName;
@@ -225,8 +248,8 @@
 
     if (generatedFile == null) {
       try {
-        File f = File.createTempFile(String.format("uiBinder_%s_%s",
-            packageName, className), ".css");
+        File f = File.createTempFile(String.format("uiBinder_%s_%s", packageName, className),
+            "." + getCssFileExtension());
         f.deleteOnExit();
 
         BufferedWriter out = new BufferedWriter(new FileWriter(f));
@@ -251,4 +274,8 @@
     }
     return true;
   }
+
+  private String getCssFileExtension() {
+    return gss ? "gss" : "css";
+  }
 }
diff --git a/user/test/com/google/gwt/resources/ResourcesJreSuite.java b/user/test/com/google/gwt/resources/ResourcesJreSuite.java
index 3cd9dac..0fef2d5 100644
--- a/user/test/com/google/gwt/resources/ResourcesJreSuite.java
+++ b/user/test/com/google/gwt/resources/ResourcesJreSuite.java
@@ -28,6 +28,7 @@
 import com.google.gwt.resources.css.ExtractClassNamesVisitorTest;
 import com.google.gwt.resources.css.UnknownAtRuleTest;
 import com.google.gwt.resources.ext.ResourceGeneratorUtilTest;
+import com.google.gwt.resources.gss.ClassNamesCollectorTest;
 import com.google.gwt.resources.gss.CssPrinterTest;
 import com.google.gwt.resources.gss.ExtendedEliminateConditionalNodesTest;
 import com.google.gwt.resources.gss.ExternalClassesCollectorTest;
@@ -64,6 +65,8 @@
     // GSS tests
     suite.addTestSuite(ExternalClassesCollectorTest.class);
     suite.addTestSuite(RenamingSubstitutionMapTest.class);
+    suite.addTestSuite(ImageSpriteCreatorTest.class);
+    suite.addTestSuite(ClassNamesCollectorTest.class);
     suite.addTestSuite(CssPrinterTest.class);
     suite.addTestSuite(PermutationsCollectorTest.class);
     suite.addTestSuite(RecordingBidiFlipperTest.class);
@@ -74,7 +77,6 @@
     suite.addTestSuite(RuntimeConditionalBlockCollectorTest.class);
     suite.addTestSuite(ValidateRuntimeConditionalNodeTest.class);
     suite.addTestSuite(ValueFunctionTest.class);
-    suite.addTestSuite(ImageSpriteCreatorTest.class);
 
     // CSS to GSS converter tests
     suite.addTestSuite(Css2GssTest.class);
diff --git a/user/test/com/google/gwt/resources/gss/ClassNamesCollectorTest.java b/user/test/com/google/gwt/resources/gss/ClassNamesCollectorTest.java
new file mode 100644
index 0000000..ca24695
--- /dev/null
+++ b/user/test/com/google/gwt/resources/gss/ClassNamesCollectorTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.gss;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.gwt.core.ext.typeinfo.JClassType;
+import com.google.gwt.resources.client.CssResource.ImportedWithPrefix;
+import com.google.gwt.thirdparty.common.css.compiler.ast.CssClassSelectorNode;
+import com.google.gwt.thirdparty.common.css.compiler.ast.CssTree;
+import com.google.gwt.thirdparty.common.css.compiler.ast.VisitController;
+
+import junit.framework.TestCase;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test for {@link ClassNamesCollector}.
+ */
+public class ClassNamesCollectorTest extends TestCase {
+  private CssClassSelectorNode cssClassSelectorNode;
+  private CssTree cssTree;
+  private VisitController visitController;
+  private ClassNamesCollector classNamesCollector;
+
+  @Override
+  protected void setUp() {
+    classNamesCollector = new ClassNamesCollector();
+    cssClassSelectorNode = mock(CssClassSelectorNode.class);
+    cssTree = mock(CssTree.class);
+    visitController = mock(VisitController.class);
+
+    when(cssTree.getVisitController()).thenReturn(visitController);
+  }
+
+  public void testGetClassNames_noImport_collectAllClasses() {
+    // Given
+    List<CssClassSelectorNode> classSelectorNodes = createClassSelectorNodes("class1", "class2",
+        "class3");
+    whenVisitorControllerVisitTreeThenVisitAllNodes(classSelectorNodes);
+
+    // When
+    Set<String> classNames = classNamesCollector.getClassNames(cssTree, new HashSet<JClassType>());
+
+    // Then
+    assertEquals(3, classNames.size());
+    assertTrue(classNames.contains("class1"));
+    assertTrue(classNames.contains("class2"));
+    assertTrue(classNames.contains("class3"));
+  }
+
+  public void testGetClassNames_withImport_dontCollectImportedClasses() {
+    // Given
+    List<CssClassSelectorNode> classSelectorNodes = createClassSelectorNodes("class1", "class2",
+        "class3", "Imported-class", "ImportedFakeClass", "otherImport-class");
+    whenVisitorControllerVisitTreeThenVisitAllNodes(classSelectorNodes);
+
+    Set<JClassType> importedType = new HashSet<JClassType>();
+
+    // mock a class without @ImportedWithPrefix annotation
+    JClassType importedClassType = mock(JClassType.class);
+    when(importedClassType.getSimpleSourceName()).thenReturn("Imported");
+    importedType.add(importedClassType);
+
+    // mock a class with a @ImportedWithPrefix annotation
+    JClassType importedWithPrefixClassType = mock(JClassType.class);
+    when(importedWithPrefixClassType.getSimpleSourceName()).thenReturn("ImportedWithPrefix");
+    ImportedWithPrefix importedWithPrefixAnnotation = mock(ImportedWithPrefix.class);
+    when(importedWithPrefixAnnotation.value()).thenReturn("otherImport");
+    when(importedWithPrefixClassType.getAnnotation(ImportedWithPrefix.class))
+        .thenReturn(importedWithPrefixAnnotation);
+    importedType.add(importedWithPrefixClassType);
+
+    // When
+    Set<String> classNames = classNamesCollector.getClassNames(cssTree, importedType);
+
+    // Then
+    assertEquals(4, classNames.size());
+    assertTrue(classNames.contains("class1"));
+    assertTrue(classNames.contains("class2"));
+    assertTrue(classNames.contains("class3"));
+    assertTrue(classNames.contains("ImportedFakeClass"));
+    assertFalse(classNames.contains("otherImport-class"));
+    assertFalse(classNames.contains("Imported-class"));
+  }
+
+  private List<CssClassSelectorNode> createClassSelectorNodes(String... classNames) {
+    List<CssClassSelectorNode> classSelectorNodes = new ArrayList<CssClassSelectorNode>(classNames
+        .length);
+
+    for (String className : classNames) {
+      CssClassSelectorNode node = mock(CssClassSelectorNode.class);
+      when(node.getRefinerName()).thenReturn(className);
+      classSelectorNodes.add(node);
+    }
+
+    return classSelectorNodes;
+  }
+
+  private void whenVisitorControllerVisitTreeThenVisitAllNodes(final List<CssClassSelectorNode>
+      nodes) {
+    doAnswer(new Answer() {
+      @Override
+      public Object answer(InvocationOnMock invocation) throws Throwable {
+        for (CssClassSelectorNode node : nodes) {
+          classNamesCollector.enterClassSelector(node);
+        }
+        return null;
+      }
+    }).when(visitController).startVisit(classNamesCollector);
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/UiBinderSuite.java b/user/test/com/google/gwt/uibinder/UiBinderSuite.java
index 0657bc1..110ed3c 100644
--- a/user/test/com/google/gwt/uibinder/UiBinderSuite.java
+++ b/user/test/com/google/gwt/uibinder/UiBinderSuite.java
@@ -23,6 +23,7 @@
 import com.google.gwt.uibinder.test.client.UiBinderTest;
 import com.google.gwt.uibinder.test.client.UiChildTest;
 import com.google.gwt.uibinder.test.client.UiHandlerTest;
+import com.google.gwt.uibinder.test.client.gss.UiBinderWithGssTest;
 
 import junit.framework.Test;
 
@@ -41,6 +42,7 @@
     suite.addTestSuite(UiBinderUtilTest.class);
     suite.addTestSuite(UiChildTest.class);
     suite.addTestSuite(UiHandlerTest.class);
+    suite.addTestSuite(UiBinderWithGssTest.class);
 
     return suite;
   }
diff --git a/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java
index eae9c83..a76923f 100644
--- a/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java
+++ b/user/test/com/google/gwt/uibinder/rebind/FieldWriterOfGeneratedCssResourceTest.java
@@ -58,7 +58,8 @@
 
     ImplicitCssResource css = new ImplicitCssResource("package", "ClassName",
         "fieldName", new String[] {}, cssResourceType, ".able-baker {}",
-        MortalLogger.NULL, Collections.<JClassType> emptySet(), new MockResourceOracle());
+        MortalLogger.NULL, Collections.<JClassType> emptySet(), false,
+        new MockResourceOracle());
 
     FieldWriterOfGeneratedCssResource f = new FieldWriterOfGeneratedCssResource(
         null, stringType, css, MortalLogger.NULL);
@@ -74,7 +75,7 @@
 
     ImplicitCssResource css = new ImplicitCssResource("package", "ClassName",
         "fieldName", new String[] {}, cssResourceType, ".ableBaker {}",
-        MortalLogger.NULL, Collections.<JClassType> emptySet(), new MockResourceOracle());
+        MortalLogger.NULL, Collections.<JClassType> emptySet(), false, new MockResourceOracle());
 
     FieldWriterOfGeneratedCssResource f = new FieldWriterOfGeneratedCssResource(
         null, stringType, css, MortalLogger.NULL);
diff --git a/user/test/com/google/gwt/uibinder/test/UiBinderWithGss.gwt.xml b/user/test/com/google/gwt/uibinder/test/UiBinderWithGss.gwt.xml
new file mode 100644
index 0000000..f7ccadf
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/UiBinderWithGss.gwt.xml
@@ -0,0 +1,8 @@
+<module type="fileset">
+  <inherits name="com.google.gwt.user.User" />
+  <inherits name="com.google.gwt.debug.Debug"/>
+  <inherits name="com.google.gwt.junit.JUnit"/>
+
+  <set-configuration-property name="CssResource.enableGss" value="true" />
+  <set-configuration-property name="CssResource.legacy" value="true" />
+</module>
diff --git a/user/test/com/google/gwt/uibinder/test/client/gss/UiBinderWithGssTest.java b/user/test/com/google/gwt/uibinder/test/client/gss/UiBinderWithGssTest.java
new file mode 100644
index 0000000..3b2f39f
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/gss/UiBinderWithGssTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.uibinder.test.client.gss;
+
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.uibinder.test.client.gss.WidgetUsingCss.CssStyle;
+import com.google.gwt.uibinder.test.client.gss.WidgetUsingGss.GssStyle;
+import com.google.gwt.user.client.ui.RootPanel;
+
+/**
+ * Test case for GSS integration in UiBinder.
+ */
+public class UiBinderWithGssTest extends GWTTestCase {
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.uibinder.test.UiBinderWithGss";
+  }
+
+  public void testGssIntegration() {
+    WidgetUsingGss widgetUsingGss = new WidgetUsingGss();
+    GssStyle style = widgetUsingGss.style;
+    RootPanel.get().add(widgetUsingGss);
+
+    assertEquals(style.main(), widgetUsingGss.getStyleName());
+    String expectedCss = "." + style.main() + "{background-color:black;color:white;width:200px;" +
+        "height:300px}";
+    assertEquals(expectedCss, widgetUsingGss.style.getText());
+  }
+
+  public void testCssConversion() {
+    WidgetUsingCss widgetUsingCss = new WidgetUsingCss();
+    CssStyle style = widgetUsingCss.style;
+    RootPanel.get().add(widgetUsingCss);
+
+    assertEquals(style.main(), widgetUsingCss.getStyleName());
+    String expectedCss = "." + style.main() + "{background-color:black;color:white}";
+    assertEquals(expectedCss, widgetUsingCss.style.getText());
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingCss.java b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingCss.java
new file mode 100644
index 0000000..52d5691
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingCss.java
@@ -0,0 +1,46 @@
+/*
+ * 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.uibinder.test.client.gss;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Widget not using GSS syntax in the inline style of its UiBinder template.
+ */
+public class WidgetUsingCss extends Composite {
+  interface WidgetUsingCssUiBinder extends UiBinder<Widget, WidgetUsingCss> {
+  }
+
+  interface CssStyle extends CssResource {
+    String main();
+  }
+
+  static final String WHITE = "white";
+
+  private static WidgetUsingCssUiBinder uiBinder = GWT.create(WidgetUsingCssUiBinder.class);
+
+  @UiField
+  CssStyle style;
+
+  public WidgetUsingCss() {
+    initWidget(uiBinder.createAndBindUi(this));
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingCss.ui.xml b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingCss.ui.xml
new file mode 100644
index 0000000..a3c973d
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingCss.ui.xml
@@ -0,0 +1,13 @@
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+             xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+  <ui:style type="com.google.gwt.uibinder.test.client.gss.WidgetUsingCss.CssStyle">
+    @def my_black black;
+    @eval my_white com.google.gwt.uibinder.test.client.gss.WidgetUsingCss.WHITE;
+
+    .main {
+      background-color: my_black;
+      color: my_white;
+    }
+  </ui:style>
+  <g:SimplePanel styleName="{style.main}"/>
+</ui:UiBinder>
diff --git a/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingGss.java b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingGss.java
new file mode 100644
index 0000000..35e941f
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingGss.java
@@ -0,0 +1,44 @@
+/*
+ * 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.uibinder.test.client.gss;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.CssResource;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Widget using GSS syntax in the inline style of its UiBinder template.
+ */
+public class WidgetUsingGss extends Composite {
+  interface WidgetUsingGssUiBinder extends UiBinder<Widget, WidgetUsingGss> {
+  }
+
+  private static WidgetUsingGssUiBinder uiBinder = GWT.create(WidgetUsingGssUiBinder.class);
+
+  interface GssStyle extends CssResource {
+    String main();
+  }
+
+  @UiField
+  GssStyle style;
+
+  public WidgetUsingGss() {
+    initWidget(uiBinder.createAndBindUi(this));
+  }
+}
diff --git a/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingGss.ui.xml b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingGss.ui.xml
new file mode 100644
index 0000000..bfe056a
--- /dev/null
+++ b/user/test/com/google/gwt/uibinder/test/client/gss/WidgetUsingGss.ui.xml
@@ -0,0 +1,18 @@
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+             xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+  <ui:style gss="true" type="com.google.gwt.uibinder.test.client.gss.WidgetUsingGss.GssStyle">
+    @def BLACK black;
+
+    @defmixin size(WIDTH, HEIGHT) {
+      width: WIDTH;
+      height: HEIGHT;
+    }
+
+    .main {
+      background-color: BLACK;
+      color: white;
+      @mixin size(200px, 300px);
+    }
+  </ui:style>
+  <g:SimplePanel styleName="{style.main}"/>
+</ui:UiBinder>