As you may have noticed, we have recently upgraded the rich-text editor in Rally to use Google’s Closure editor.  This is the same editor used in Gmail and Google Docs.

As we were testing the Closure editor within the Rally tool, we noticed that the toolbar icons are actually images coming from Google’s server.  This isn’t really a problem, except that some of our customers are on-premise customers with no internet access.  Therefore, if we didn’t fix this, all of these customers would have blank images for all of their toolbar icons.  Not good.

The fix was easy, as you might have guessed (we simply hosted the icons in Rally).  Perhaps more importantly though, we wanted to make sure that we’d never have to worry about this same problem in the future.  So we wrote a test to make sure that none of the CSS that Rally uses fetches resources from a third-party server.  I’ll walk you through the test.  We made heavy use of the CSS Parser library in our test.

First, we got a list of the paths to all of our CSS files (method implementation not shown):


List<String> cssFiles = getCssFiles();

We then built up a HashMap (called selectorPropertiesMap) where the keys are all the CSS selectors that we use in our app, and the values are HashMaps.  These “inner” HashMaps’ keys are the CSS properties (padding, margin, background, etc.) declared for a given CSS selector and the values are the CSS value for each property for the given selector.

The use of the “inner” HashMap is important because it means that for a given selector S, if CSS property P is declared with value V in CSS file F, but P is later overriden using the same selector S in a later file F1 with a new value V1, then this HashMap will also override the value of P with V1 just as a browser would do when it determines CSS specificity (when everything else is a tie, the last declared value wins).


HashMap<String, HashMap<String, String>> selectorPropertiesMap = newHashMap();
for (String cssFile : cssFiles) {
  MockServletContext servletContext = new MockServletContext("file:" + System.getProperty("slm.dir", "."));
  InputStream stream = servletContext.getResourceAsStream(cssFile);

  InputSource source = new InputSource(new InputStreamReader(stream));
  CSSOMParser parser = new CSSOMParser();
  CSSStyleSheet stylesheet = parser.parseStyleSheet(source, null, null);

  CSSRuleListImpl ruleList = (CSSRuleListImpl) stylesheet.getCssRules();
  for (CSSRule rule : ruleList.getRules()) {
    CSSStyleRuleImpl cssRule = (CSSStyleRuleImpl) rule;

    List<Selector> selectorList = ((SelectorListImpl) cssRule.getSelectors()).getSelectors();
    List<Property> properties = ((CSSStyleDeclarationImpl) cssRule.getStyle()).getProperties();
    for (Selector selector : selectorList) {
      for (Property property : properties) {
        String propertyName = property.getName();
        String propertyValue = property.getValue().toString();

        HashMap<String, String> propertiesMap = selectorPropertiesMap.get(selector.toString());
        if (propertiesMap == null) {
          HashMap<String, String> newPropertiesMap = newHashMap();
          newPropertiesMap.put(propertyName, propertyValue);
          selectorPropertiesMap.put(selector.toString(), newPropertiesMap);
        } else {
          propertiesMap.put(propertyName, propertyValue);
        }
      }
    }
  }

Finally, we iterate over the value of every CSS property in our app and ensure that it is not pointing to a third-party resource:

for (HashMap<String, String> propertiesMap : selectorPropertiesMap.values()) {
  for (String propertyValue : propertiesMap.values()) {
    assertFalse(
      propertyValue.matches(".*https?://.*"),
      "The CSS rules may not contain static links. Violation found for " + propertyValue
    );
  }
}

So, there you have it. A fairly simple test that ensures that none of your CSS properties are pointing to third-party resources.