Strokes and Fills in Flex CSS through ActionScript injection

Flex CSS, while a fine idea, is riddled with limitations. Most notable is probably its lack of proper cascading, but equally unfortunate is that it’s a system defined by programmatic ActionScript APIs. There is a textual way of defining styles, but the Flex compiler, mxmlc, is used to turn this style sheet language into ActionScript, which is in turn compiled into a style module you can load at runtime.

The textual stylesheet language is the black sheep of the family, supporting only a limited set of the features available with runtime Flex styles. For example, take the LineSeries from the data visualization package that comes with FlexBuilder Pro. It defines a number of styles in terms of IFill or IStroke instances. How do you specify IFill or IStroker instances in Flex CSS, so that you can configure look and feel separately from application logic, or alter styles without redeploying the entire application? You don’t. You curse the mxmlc designers for not allowing richer CSS compilation. You can only specify numbers; Strings; embedded images, sounds, and fonts; and references to classes through the ClassReference directive. No instances of any other objects.

Well, what if you’re mad as hell and you won’t take it anymore?

I’m working on a visualization framework with some dynamic styling, and being able to specify “default” styles externally through standard Flex CSS is really, really important. It makes the model much simpler. I was banging my head against the Flex CSS wall and then the scales fell from my eyes:

  1. Mxmlc compiles Flex CSS files to ActionScript.
  2. That ActionScript must simply be building standard mx.styles.CSSStyleDeclaration classes.
  3. Mxmlc has a keep-generated-actionscript attribute.

We take global.css:

AxisRenderer {
    labelAlign: "center";

compile with keep-generated-actionscript=true, and we get:

Looking at these, only is interesting. It has a lot of boilerplate code, but then something familiar:

        var style:CSSStyleDeclaration;
        var keys:Array;
        var selector:String;
        var effects:Array;
        var addedEffects:Array;

        selector = "AxisRenderer";

        style = StyleManager.getStyleDeclaration(selector);

        if (!style)
            style = new CSSStyleDeclaration();
            StyleManager.setStyleDeclaration(selector, style, false);

        keys = overrideMap[selector];

        if (keys == null)
            keys = [];
            overrideMap[selector] = keys;

        style.mx_internal::setStyle('labelAlign', "center");

This seems fairly routine. It builds a CSSStyleDeclaration programmatically, and then adds values according to our source CSS file. And the style values seem to be just passed along. If only we could somehow “unquote” that style value, we could inject arbitrary ActionScript expressions that would be evaluated before being assigned as style values.

“Unquoting” has a venerable history in programming, so it’s not too crazy an idea. Bash does it (echo "hello $user"), PHP does it (though it’s been a while since my PHP days and I don’t remember the syntax–something along the lines of bash if memory serves), and of course Lisp does it ((let ((user 'maciek)) `(hello ,user)). The tricky part is the implementation.

Fortunately, it’s not really that tricky. With the keep-generated-actionscript flag, mxmlc does most of the work for us. All we need to do is a little post-processing, and re-build the ActionScript into a swf, ignoring the intermediate output swf. Now, everybody stand back. I know regular expressions (not well, mind you, but I get by).

Since Flex has ant task wrappers for its compilers, the easiest way to approach this was to actually build a custom ant filter. Filters can be applied when moving or copying files and they can perform line-by-line transformations on a file. Looking at the generated ActionScript above, we try to find lines matching the calls to setStyle, look for an arbitary special escape sequence that we introduce in String style values (I chose a leading comma, in homage to Lisp), and strip quotes from values matching that escape sequence, to let them execute as arbitrary ActionScript code.


public class StyleFilter implements TokenFilter.Filter {
    public String filter(String token) {
        return token.
            replaceAll("(?<=style\\.mx_internal::setStyle\\('[^']{1,100}', )\",([^\"]+)\"(?=\\);)",
                "$1").replaceAll("^import mx\\.core\\.mx_internal;$",
                "import mx.core.mx_internal;\nimport*;");

The regular expression is a little daunting. It can probably be optimized for both performance and correctness (I know I miss edge cases), but it works for now. Briefly,

  1. "(?<=style\\.mx_internal::setStyle\\('[^']{1,100}', )\",([^\"]+)\"(?=\\);)": positive lookbehind for the setStyle() call we see above–everything up to and including the comma after the style name. Note that lookbehind won’t let us use an unbounded number of characters due to regex mechanics, so I arbitrarily cap it at 100. I’m sure there are better ways around this, but you’ll seldom see 100-character style names in the wild. Note also that we need to escape the period and parentheses, and escape the backslashes escaping them, since these are Java Strings.
  2. "(?<=style\\.mx_internal::setStyle\\('[^']{1,100}', )\",([^\"]+)\"(?=\\);)": the actual quoted value. This expression won’t work on escaped embedded quotes, but since ActionScript supports both single and double quotes, you can typically use single quotes instead. Note that we match the whole quoted value, with the leading comma that signifies the value is to be “de-quoted”, but we only capture the unquoted value, sans leading comma.
  3. "(?<=style\\.mx_internal::setStyle\\('[^']{1,100}', )\",([^\"]+)\"(?=\\);)": We make sure the line ends as we expect with positive lookahead. Not really necessary, but a nice sanity check

We replace this whole thing with the first captured group.

Note also the second replace. That’s there to add an import statement for the package, where Stroke and SolidFill, the classes I am most interested in live. Unfortunately, Flex requires import statements even for fully qualified class names (whereas in, e.g., Java, you can say something like System.out.println instead of having to import it explicitly). With this, a style like:

AxisRenderer {
    labelAlign: center;
    axisStroke: ',new Stroke(0x0000ff, 2)';

will look like this before the processing step:

        style.mx_internal::setStyle('labelAlign', "center");
        style.mx_internal::setStyle('axisStroke', ",new Stroke(0x0000ff, 2)");

and this after processing:

        style.mx_internal::setStyle('labelAlign', "center");
        style.mx_internal::setStyle('axisStroke', new Stroke(0x0000ff, 2));

This means you can put arbitrary ActionScript expressions in your style values in CSS. Compiling this yields a standard Flex style module that can be loaded like any other.

Does the special treatment for the package mean that you need to extend the filter with more imports if you need another package? Not necessarily. ActionScript has anonymous functions and imports can be done locally, so you can have CSS values that would give any self-respecting designer a heart attack:

AxisRenderer {
    axisStroke: ',function():Object { import com.example.stroke.CustomStroke; return new CustomStroke(0x0000ff, 2); }()';

Fun with xmlns in Flex

So, one of the more interesting things about Flex is that, in addition to Perl-like regular expression literals, it also gives us full-on XML literals. Maybe ‘perverse’ is more accurate than ‘interesting’–the things this must do to the grammar (and the number of issues in Adobe JIRA seems to confirm this). Anyway, it looks like this:

var myXML:XML = <foo><bar baz="quux"><xyzzy/></foo>;

This can be pretty handy to test things when developing something that will eventually consume XML produced by the server. Also, since a number of Flex components (e.g., Menu) consume XML, it’s easy to define it inline.

I am currently experimenting with Degrafa, and using some SVG files as input. Given Flex’s fancy XML support, I decided to just copy and paste the entire text of the SVG file. Everything seems to build fine, but when I launch—no dice. Nothing shows up.

It took me a couple of hours of debugging to track down the problem to this:

var myXML:XML = <foo xmlns=""><bar baz="quux"><xyzzy/></foo>;

The XML namespace declaration for the XML literal seems to confuse either Flex or Degrafa (I’m not quite sure which), and my Geometry was not being updated. As soon as I took out the xmlns declaration, things are hunky dory.

Update: It seems the problem is in Flex itself. With the following code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="" layout="vertical"
	horizontalAlign="center" verticalAlign="middle">
            private const withNamespace:XML =
                <foo xmlns=""><bar>namespaced</bar><baz/></foo>;
            private const withoutNamespace:XML =
                <foo><bar>not namespaced</bar><baz/></foo>;
    <mx:Label text="{}"/>
    <mx:Label text="{}"/>

only ‘not namespaced’ shows.