001    // Copyright (c) 2013, Mike Samuel
002    // All rights reserved.
003    //
004    // Redistribution and use in source and binary forms, with or without
005    // modification, are permitted provided that the following conditions
006    // are met:
007    //
008    // Redistributions of source code must retain the above copyright
009    // notice, this list of conditions and the following disclaimer.
010    // Redistributions in binary form must reproduce the above copyright
011    // notice, this list of conditions and the following disclaimer in the
012    // documentation and/or other materials provided with the distribution.
013    // Neither the name of the OWASP nor the names of its contributors may
014    // be used to endorse or promote products derived from this software
015    // without specific prior written permission.
016    // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
017    // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
018    // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
019    // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
020    // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
021    // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
022    // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023    // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
024    // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
025    // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
026    // ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
027    // POSSIBILITY OF SUCH DAMAGE.
028    
029    package org.owasp.html;
030    
031    import java.util.Map;
032    import java.util.Set;
033    import java.util.SortedSet;
034    
035    import javax.annotation.Nullable;
036    
037    import com.google.common.collect.ImmutableMap;
038    import com.google.common.collect.ImmutableSet;
039    import com.google.common.collect.Maps;
040    import com.google.common.collect.Sets;
041    
042    /** Describes the kinds of tokens a CSS property's value can safely contain. */
043    @TCB
044    public final class CssSchema {
045    
046      static final class Property {
047        /** A bitfield of BIT_* constants describing groups of allowed tokens. */
048        final int bits;
049        /** Specific allowed values. */
050        final ImmutableSet<String> literals;
051        /**
052         * Maps lower-case function tokens to the schema key for their parameters.
053         */
054        final ImmutableMap<String, String> fnKeys;
055    
056        private Property(
057            int bits, ImmutableSet<String> literals,
058            ImmutableMap<String, String> fnKeys) {
059          this.bits = bits;
060          this.literals = literals;
061          this.fnKeys = fnKeys;
062        }
063      }
064    
065      static final int BIT_QUANTITY = 1;
066      static final int BIT_HASH_VALUE = 2;
067      static final int BIT_NEGATIVE = 4;
068      static final int BIT_STRING = 8;
069      static final int BIT_URL = 16;
070      static final int BIT_UNRESERVED_WORD = 64;
071      static final int BIT_UNICODE_RANGE = 128;
072    
073      static final Property DISALLOWED = new Property(
074          0, ImmutableSet.<String>of(), ImmutableMap.<String, String>of());
075    
076      private final ImmutableMap<String, Property> properties;
077    
078      private CssSchema(ImmutableMap<String, Property> properties) {
079        if (properties == null) { throw new NullPointerException(); }
080        this.properties = properties;
081      }
082    
083      /**
084       * A schema that includes all and only the named properties.
085       *
086       * @param propertyNames a series of lower-case CSS property names that appear
087       *    in the built-in CSS definitions.  It is an error to mention an unknown
088       *    property name.  This class's {@code main} method will dump a list of
089       *    known property names when run with zero arguments.
090       */
091      public static CssSchema withProperties(
092          Iterable<? extends String> propertyNames) {
093        ImmutableMap.Builder<String, Property> propertiesBuilder =
094            ImmutableMap.builder();
095        for (String propertyName : propertyNames) {
096          Property prop = DEFINITIONS.get(propertyName);
097          if (prop == null) { throw new IllegalArgumentException(propertyName); }
098          propertiesBuilder.put(propertyName, prop);
099        }
100        return new CssSchema(propertiesBuilder.build());
101      }
102    
103      /**
104       * A schema that represents the union of the input schemas.
105       *
106       * @return A schema that allows all and only CSS properties that are allowed
107       *    by at least one of the inputs.
108       */
109      public static CssSchema union(CssSchema... cssSchemas) {
110        if (cssSchemas.length == 1) { return cssSchemas[0]; }
111        Map<String, Property> properties = Maps.newLinkedHashMap();
112        for (CssSchema cssSchema : cssSchemas) {
113          properties.putAll(cssSchema.properties);
114        }
115        return new CssSchema(ImmutableMap.copyOf(properties));
116      }
117    
118      /**
119       * The set of CSS properties allowed by this schema.
120       *
121       * @return an immutable set.
122       */
123      public Set<String> allowedProperties() {
124        return properties.keySet();
125      }
126    
127      /** The schema for the named property or function key. */
128      Property forKey(String propertyName) {
129        propertyName = Strings.toLowerCase(propertyName);
130        Property property = properties.get(propertyName);
131        if (property != null) { return property; }
132        int n = propertyName.length();
133        if (n != 0 && propertyName.charAt(0) == '-') {
134          String barePropertyName = stripVendorPrefix(propertyName);
135          property = properties.get(barePropertyName);
136          if (property != null) { return property; }
137        }
138        return DISALLOWED;
139      }
140    
141      /** {@code "-moz-foo"} &rarr; {@code "foo"}. */
142      private static @Nullable String stripVendorPrefix(String cssKeyword) {
143        int prefixLen = 0;
144        switch (cssKeyword.charAt(1)) {
145          case 'm':
146            if (cssKeyword.startsWith("-ms-")) {
147              prefixLen = 4;
148            } else if (cssKeyword.startsWith("-moz-")) {
149              prefixLen = 5;
150            }
151            break;
152          case 'o':
153            if (cssKeyword.startsWith("-o-")) { prefixLen = 3; }
154            break;
155          case 'w':
156            if (cssKeyword.startsWith("-webkit-")) { prefixLen = 8; }
157            break;
158          default: break;
159        }
160        return prefixLen == 0 ? null : cssKeyword.substring(prefixLen);
161      }
162    
163      /** Maps lower-cased CSS property names to information about them. */
164      static final ImmutableMap<String, Property> DEFINITIONS;
165      static {
166        ImmutableMap<String, String> zeroFns = ImmutableMap.of();
167        ImmutableMap.Builder<String, Property> builder
168            = ImmutableMap.builder();
169        ImmutableSet<String> mozBorderRadiusLiterals0 = ImmutableSet.of("/");
170        ImmutableSet<String> mozOpacityLiterals0 = ImmutableSet.of("inherit");
171        ImmutableSet<String> mozOutlineLiterals0 = ImmutableSet.of(
172            "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige",
173            "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown",
174            "burlywood", "cadetblue", "chartreuse", "chocolate", "coral",
175            "cornflowerblue", "cornsilk", "crimson", "cyan", "darkblue", "darkcyan",
176            "darkgoldenrod", "darkgray", "darkgreen", "darkkhaki", "darkmagenta",
177            "darkolivegreen", "darkorange", "darkorchid", "darkred", "darksalmon",
178            "darkseagreen", "darkslateblue", "darkslategray", "darkturquoise",
179            "darkviolet", "deeppink", "deepskyblue", "dimgray", "dodgerblue",
180            "firebrick", "floralwhite", "forestgreen", "fuchsia", "gainsboro",
181            "ghostwhite", "gold", "goldenrod", "gray", "green", "greenyellow",
182            "honeydew", "hotpink", "indianred", "indigo", "ivory", "khaki",
183            "lavender", "lavenderblush", "lawngreen", "lemonchiffon", "lightblue",
184            "lightcoral", "lightcyan", "lightgoldenrodyellow", "lightgreen",
185            "lightgrey", "lightpink", "lightsalmon", "lightseagreen",
186            "lightskyblue", "lightslategray", "lightsteelblue", "lightyellow",
187            "lime", "limegreen", "linen", "magenta", "maroon", "mediumaquamarine",
188            "mediumblue", "mediumorchid", "mediumpurple", "mediumseagreen",
189            "mediumslateblue", "mediumspringgreen", "mediumturquoise",
190            "mediumvioletred", "midnightblue", "mintcream", "mistyrose",
191            "moccasin", "navajowhite", "navy", "oldlace", "olive", "olivedrab",
192            "orange", "orangered", "orchid", "palegoldenrod", "palegreen",
193            "paleturquoise", "palevioletred", "papayawhip", "peachpuff", "peru",
194            "pink", "plum", "powderblue", "purple", "red", "rosybrown", "royalblue",
195            "saddlebrown", "salmon", "sandybrown", "seagreen", "seashell", "sienna",
196            "silver", "skyblue", "slateblue", "slategray", "snow", "springgreen",
197            "steelblue", "tan", "teal", "thistle", "tomato", "turquoise", "violet",
198            "wheat", "white", "whitesmoke", "yellow", "yellowgreen");
199        ImmutableSet<String> mozOutlineLiterals1 = ImmutableSet.of(
200            "dashed", "dotted", "double", "groove", "outset", "ridge", "solid");
201        ImmutableSet<String> mozOutlineLiterals2 = ImmutableSet.of("thick", "thin");
202        ImmutableSet<String> mozOutlineLiterals3 = ImmutableSet.of(
203            "hidden", "inherit", "inset", "invert", "medium", "none");
204        ImmutableMap<String, String> mozOutlineFunctions =
205          ImmutableMap.<String, String>of("rgb(", "rgb()", "rgba(", "rgba()");
206        ImmutableSet<String> mozOutlineColorLiterals0 =
207          ImmutableSet.of("inherit", "invert");
208        ImmutableSet<String> mozOutlineStyleLiterals0 =
209          ImmutableSet.of("hidden", "inherit", "inset", "none");
210        ImmutableSet<String> mozOutlineWidthLiterals0 =
211          ImmutableSet.of("inherit", "medium");
212        ImmutableSet<String> oTextOverflowLiterals0 =
213          ImmutableSet.of("clip", "ellipsis");
214        ImmutableSet<String> azimuthLiterals0 = ImmutableSet.of(
215            "behind", "center-left", "center-right", "far-left", "far-right",
216            "left-side", "leftwards", "right-side", "rightwards");
217        ImmutableSet<String> azimuthLiterals1 = ImmutableSet.of("left", "right");
218        ImmutableSet<String> azimuthLiterals2 =
219          ImmutableSet.of("center", "inherit");
220        ImmutableSet<String> backgroundLiterals0 = ImmutableSet.of(
221            "border-box", "contain", "content-box", "cover", "padding-box");
222        ImmutableSet<String> backgroundLiterals1 =
223          ImmutableSet.of("no-repeat", "repeat-x", "repeat-y", "round", "space");
224        ImmutableSet<String> backgroundLiterals2 = ImmutableSet.of("bottom", "top");
225        ImmutableSet<String> backgroundLiterals3 = ImmutableSet.of(
226            ",", "/", "auto", "center", "fixed", "inherit", "local", "none",
227            "repeat", "scroll", "transparent");
228        ImmutableMap<String, String> backgroundFunctions =
229          ImmutableMap.<String, String>builder()
230          .put("image(", "image()")
231          .put("linear-gradient(", "linear-gradient()")
232          .put("radial-gradient(", "radial-gradient()")
233          .put("repeating-linear-gradient(", "repeating-linear-gradient()")
234          .put("repeating-radial-gradient(", "repeating-radial-gradient()")
235          .put("rgb(", "rgb()").put("rgba(", "rgba()")
236          .build();
237        ImmutableSet<String> backgroundAttachmentLiterals0 =
238          ImmutableSet.of(",", "fixed", "local", "scroll");
239        ImmutableSet<String> backgroundColorLiterals0 =
240          ImmutableSet.of("inherit", "transparent");
241        ImmutableSet<String> backgroundImageLiterals0 =
242          ImmutableSet.of(",", "none");
243        ImmutableMap<String, String> backgroundImageFunctions =
244          ImmutableMap.<String, String>of(
245              "image(", "image()",
246              "linear-gradient(", "linear-gradient()",
247              "radial-gradient(", "radial-gradient()",
248              "repeating-linear-gradient(", "repeating-linear-gradient()",
249              "repeating-radial-gradient(", "repeating-radial-gradient()");
250        ImmutableSet<String> backgroundPositionLiterals0 = ImmutableSet.of(
251            ",", "center");
252        ImmutableSet<String> backgroundRepeatLiterals0 = ImmutableSet.of(
253            ",", "repeat");
254        ImmutableSet<String> borderLiterals0 = ImmutableSet.of(
255            "hidden", "inherit", "inset", "medium", "none", "transparent");
256        ImmutableSet<String> borderCollapseLiterals0 = ImmutableSet.of(
257            "collapse", "inherit", "separate");
258        ImmutableSet<String> bottomLiterals0 = ImmutableSet.of("auto", "inherit");
259        ImmutableSet<String> boxShadowLiterals0 = ImmutableSet.of(
260            ",", "inset", "none");
261        ImmutableSet<String> clearLiterals0 = ImmutableSet.of(
262            "both", "inherit", "none");
263        ImmutableMap<String, String> clipFunctions =
264            ImmutableMap.<String, String>of("rect(", "rect()");
265        ImmutableSet<String> contentLiterals0 = ImmutableSet.of("none", "normal");
266        ImmutableSet<String> cueLiterals0 = ImmutableSet.of("inherit", "none");
267        ImmutableSet<String> cursorLiterals0 = ImmutableSet.of(
268            "all-scroll", "col-resize", "crosshair", "default", "e-resize",
269            "hand", "help", "move", "n-resize", "ne-resize", "no-drop",
270            "not-allowed", "nw-resize", "pointer", "progress", "row-resize",
271            "s-resize", "se-resize", "sw-resize", "text", "vertical-text",
272            "w-resize", "wait");
273        ImmutableSet<String> cursorLiterals1 = ImmutableSet.of(
274            ",", "auto", "inherit");
275        ImmutableSet<String> directionLiterals0 = ImmutableSet.of("ltr", "rtl");
276        ImmutableSet<String> displayLiterals0 = ImmutableSet.of(
277            "-moz-inline-box", "-moz-inline-stack", "block", "inline",
278            "inline-block", "inline-table", "list-item", "run-in", "table",
279            "table-caption", "table-cell", "table-column", "table-column-group",
280            "table-footer-group", "table-header-group", "table-row",
281            "table-row-group");
282        ImmutableSet<String> elevationLiterals0 = ImmutableSet.of(
283            "above", "below", "higher", "level", "lower");
284        ImmutableSet<String> emptyCellsLiterals0 = ImmutableSet.of("hide", "show");
285        //ImmutableMap<String, String> filterFunctions =
286        //  ImmutableMap.<String, String>of("alpha(", "alpha()");
287        ImmutableSet<String> fontLiterals0 = ImmutableSet.of(
288            "100", "200", "300", "400", "500", "600", "700", "800", "900", "bold",
289            "bolder", "lighter");
290        ImmutableSet<String> fontLiterals1 = ImmutableSet.of(
291            "large", "larger", "small", "smaller", "x-large", "x-small",
292            "xx-large", "xx-small");
293        ImmutableSet<String> fontLiterals2 = ImmutableSet.of(
294            "caption", "icon", "menu", "message-box", "small-caption",
295            "status-bar");
296        ImmutableSet<String> fontLiterals3 = ImmutableSet.of(
297            "cursive", "fantasy", "monospace", "sans-serif", "serif");
298        ImmutableSet<String> fontLiterals4 = ImmutableSet.of("italic", "oblique");
299        ImmutableSet<String> fontLiterals5 = ImmutableSet.of(
300            ",", "/", "inherit", "medium", "normal", "small-caps");
301        ImmutableSet<String> fontFamilyLiterals0 = ImmutableSet.of(",", "inherit");
302        ImmutableSet<String> fontStretchLiterals0 = ImmutableSet.of(
303            "condensed", "expanded", "extra-condensed", "extra-expanded",
304            "narrower", "semi-condensed", "semi-expanded", "ultra-condensed",
305            "ultra-expanded", "wider");
306        ImmutableSet<String> fontStretchLiterals1 = ImmutableSet.of("normal");
307        ImmutableSet<String> fontStyleLiterals0 = ImmutableSet.of(
308            "inherit", "normal");
309        ImmutableSet<String> fontVariantLiterals0 = ImmutableSet.of(
310            "inherit", "normal", "small-caps");
311        ImmutableSet<String> listStyleLiterals0 = ImmutableSet.of(
312            "armenian", "cjk-decimal", "decimal", "decimal-leading-zero", "disc",
313            "disclosure-closed", "disclosure-open", "ethiopic-numeric", "georgian",
314            "hebrew", "hiragana", "hiragana-iroha", "japanese-formal",
315            "japanese-informal", "katakana", "katakana-iroha",
316            "korean-hangul-formal", "korean-hanja-formal",
317            "korean-hanja-informal", "lower-alpha", "lower-greek", "lower-latin",
318            "lower-roman", "simp-chinese-formal", "simp-chinese-informal",
319            "square", "trad-chinese-formal", "trad-chinese-informal",
320            "upper-alpha", "upper-latin", "upper-roman");
321        ImmutableSet<String> listStyleLiterals1 = ImmutableSet.of(
322            "inside", "outside");
323        ImmutableSet<String> listStyleLiterals2 = ImmutableSet.of(
324            "circle", "inherit", "none");
325        ImmutableSet<String> maxHeightLiterals0 = ImmutableSet.of(
326            "auto", "inherit", "none");
327        ImmutableSet<String> overflowLiterals0 = ImmutableSet.of(
328            "auto", "hidden", "inherit", "scroll", "visible");
329        ImmutableSet<String> overflowXLiterals0 = ImmutableSet.of(
330            "no-content", "no-display");
331        ImmutableSet<String> overflowXLiterals1 = ImmutableSet.of(
332            "auto", "hidden", "scroll", "visible");
333        ImmutableSet<String> pageBreakAfterLiterals0 = ImmutableSet.of(
334            "always", "auto", "avoid", "inherit");
335        ImmutableSet<String> pageBreakInsideLiterals0 = ImmutableSet.of(
336            "auto", "avoid", "inherit");
337        ImmutableSet<String> pitchLiterals0 = ImmutableSet.of(
338            "high", "low", "x-high", "x-low");
339        ImmutableSet<String> playDuringLiterals0 = ImmutableSet.of(
340            "auto", "inherit", "mix", "none", "repeat");
341        ImmutableSet<String> positionLiterals0 = ImmutableSet.of(
342            "absolute", "relative", "static");
343        ImmutableSet<String> speakLiterals0 = ImmutableSet.of(
344            "inherit", "none", "normal", "spell-out");
345        ImmutableSet<String> speakHeaderLiterals0 = ImmutableSet.of(
346            "always", "inherit", "once");
347        ImmutableSet<String> speakNumeralLiterals0 = ImmutableSet.of(
348            "continuous", "digits");
349        ImmutableSet<String> speakPunctuationLiterals0 = ImmutableSet.of(
350            "code", "inherit", "none");
351        ImmutableSet<String> speechRateLiterals0 = ImmutableSet.of(
352            "fast", "faster", "slow", "slower", "x-fast", "x-slow");
353        ImmutableSet<String> tableLayoutLiterals0 = ImmutableSet.of(
354            "auto", "fixed", "inherit");
355        ImmutableSet<String> textAlignLiterals0 = ImmutableSet.of(
356            "center", "inherit", "justify");
357        ImmutableSet<String> textDecorationLiterals0 = ImmutableSet.of(
358            "blink", "line-through", "overline", "underline");
359        ImmutableSet<String> textTransformLiterals0 = ImmutableSet.of(
360            "capitalize", "lowercase", "uppercase");
361        ImmutableSet<String> textWrapLiterals0 = ImmutableSet.of(
362            "suppress", "unrestricted");
363        ImmutableSet<String> unicodeBidiLiterals0 = ImmutableSet.of(
364            "bidi-override", "embed");
365        ImmutableSet<String> verticalAlignLiterals0 = ImmutableSet.of(
366            "baseline", "middle", "sub", "super", "text-bottom", "text-top");
367        ImmutableSet<String> visibilityLiterals0 = ImmutableSet.of(
368            "collapse", "hidden", "inherit", "visible");
369        ImmutableSet<String> voiceFamilyLiterals0 = ImmutableSet.of(
370            "child", "female", "male");
371        ImmutableSet<String> volumeLiterals0 = ImmutableSet.of(
372            "loud", "silent", "soft", "x-loud", "x-soft");
373        ImmutableSet<String> whiteSpaceLiterals0 = ImmutableSet.of(
374            "-moz-pre-wrap", "-o-pre-wrap", "-pre-wrap", "nowrap", "pre",
375            "pre-line", "pre-wrap");
376        ImmutableSet<String> wordWrapLiterals0 = ImmutableSet.of(
377            "break-word", "normal");
378        ImmutableSet<String> rgb$FunLiterals0 = ImmutableSet.of(",");
379        ImmutableSet<String> linearGradient$FunLiterals0 = ImmutableSet.of(
380            ",", "to");
381        ImmutableSet<String> radialGradient$FunLiterals0 = ImmutableSet.of(
382            "at", "closest-corner", "closest-side", "ellipse", "farthest-corner",
383            "farthest-side");
384        ImmutableSet<String> radialGradient$FunLiterals1 = ImmutableSet.of(
385            ",", "center", "circle");
386        ImmutableSet<String> rect$FunLiterals0 = ImmutableSet.of(",", "auto");
387        //ImmutableSet<String> alpha$FunLiterals0 = ImmutableSet.of("=", "opacity");
388        Property mozBorderRadius =
389           new Property(5, mozBorderRadiusLiterals0, zeroFns);
390        builder.put("-moz-border-radius", mozBorderRadius);
391        Property mozBorderRadiusBottomleft =
392           new Property(5, ImmutableSet.<String>of(), zeroFns);
393        builder.put("-moz-border-radius-bottomleft", mozBorderRadiusBottomleft);
394        Property mozOpacity = new Property(1, mozOpacityLiterals0, zeroFns);
395        builder.put("-moz-opacity", mozOpacity);
396        @SuppressWarnings("unchecked")
397        Property mozOutline = new Property(
398            7,
399            union(mozOutlineLiterals0, mozOutlineLiterals1, mozOutlineLiterals2,
400                  mozOutlineLiterals3),
401            mozOutlineFunctions);
402        builder.put("-moz-outline", mozOutline);
403        @SuppressWarnings("unchecked")
404        Property mozOutlineColor = new Property(
405            2, union(mozOutlineColorLiterals0, mozOutlineLiterals0),
406            mozOutlineFunctions);
407        builder.put("-moz-outline-color", mozOutlineColor);
408        @SuppressWarnings("unchecked")
409        Property mozOutlineStyle = new Property(
410            0, union(mozOutlineLiterals1, mozOutlineStyleLiterals0), zeroFns);
411        builder.put("-moz-outline-style", mozOutlineStyle);
412        @SuppressWarnings("unchecked")
413        Property mozOutlineWidth = new Property(
414            5, union(mozOutlineLiterals2, mozOutlineWidthLiterals0), zeroFns);
415        builder.put("-moz-outline-width", mozOutlineWidth);
416        Property oTextOverflow = new Property(0, oTextOverflowLiterals0, zeroFns);
417        builder.put("-o-text-overflow", oTextOverflow);
418        @SuppressWarnings("unchecked")
419        Property azimuth = new Property(
420            5, union(azimuthLiterals0, azimuthLiterals1, azimuthLiterals2),
421            zeroFns);
422        builder.put("azimuth", azimuth);
423        @SuppressWarnings("unchecked")
424        Property background = new Property(
425            23,
426            union(azimuthLiterals1, backgroundLiterals0, backgroundLiterals1,
427                  backgroundLiterals2, backgroundLiterals3, mozOutlineLiterals0),
428            backgroundFunctions);
429        builder.put("background", background);
430        builder.put("background-attachment",
431                    new Property(0, backgroundAttachmentLiterals0, zeroFns));
432        @SuppressWarnings("unchecked")
433        Property backgroundColor = new Property(
434            258, union(backgroundColorLiterals0, mozOutlineLiterals0),
435            mozOutlineFunctions);
436        builder.put("background-color", backgroundColor);
437        builder.put("background-image",
438                    new Property(16, backgroundImageLiterals0,
439                                 backgroundImageFunctions));
440        @SuppressWarnings("unchecked")
441        Property backgroundPosition = new Property(
442            5,
443            union(azimuthLiterals1, backgroundLiterals2,
444                  backgroundPositionLiterals0),
445            zeroFns);
446        builder.put("background-position", backgroundPosition);
447        @SuppressWarnings("unchecked")
448        Property backgroundRepeat = new Property(
449            0, union(backgroundLiterals1, backgroundRepeatLiterals0), zeroFns);
450        builder.put("background-repeat", backgroundRepeat);
451        @SuppressWarnings("unchecked")
452        Property border = new Property(
453            7,
454            union(borderLiterals0, mozOutlineLiterals0, mozOutlineLiterals1,
455                  mozOutlineLiterals2),
456            mozOutlineFunctions);
457        builder.put("border", border);
458        @SuppressWarnings("unchecked")
459        Property borderBottomColor = new Property(
460            2, union(backgroundColorLiterals0, mozOutlineLiterals0),
461            mozOutlineFunctions);
462        builder.put("border-bottom-color", borderBottomColor);
463        builder.put("border-collapse",
464                    new Property(0, borderCollapseLiterals0, zeroFns));
465        Property borderSpacing = new Property(5, mozOpacityLiterals0, zeroFns);
466        builder.put("border-spacing", borderSpacing);
467        Property bottom = new Property(5, bottomLiterals0, zeroFns);
468        builder.put("bottom", bottom);
469        @SuppressWarnings("unchecked")
470        Property boxShadow = new Property(
471            7, union(boxShadowLiterals0, mozOutlineLiterals0), mozOutlineFunctions);
472        builder.put("box-shadow", boxShadow);
473        @SuppressWarnings("unchecked")
474        Property captionSide = new Property(
475            0, union(backgroundLiterals2, mozOpacityLiterals0), zeroFns);
476        builder.put("caption-side", captionSide);
477        @SuppressWarnings("unchecked")
478        Property clear = new Property(
479            0, union(azimuthLiterals1, clearLiterals0), zeroFns);
480        builder.put("clear", clear);
481        builder.put("clip", new Property(0, bottomLiterals0, clipFunctions));
482        @SuppressWarnings("unchecked")
483        Property color = new Property(
484            258, union(mozOpacityLiterals0, mozOutlineLiterals0),
485            mozOutlineFunctions);
486        builder.put("color", color);
487        builder.put("content", new Property(8, contentLiterals0, zeroFns));
488        Property cue = new Property(16, cueLiterals0, zeroFns);
489        builder.put("cue", cue);
490        @SuppressWarnings("unchecked")
491        Property cursor = new Property(
492            272, union(cursorLiterals0, cursorLiterals1), zeroFns);
493        builder.put("cursor", cursor);
494        @SuppressWarnings("unchecked")
495        Property direction = new Property(
496            0, union(directionLiterals0, mozOpacityLiterals0), zeroFns);
497        builder.put("direction", direction);
498        @SuppressWarnings("unchecked")
499        Property display = new Property(
500            0, union(cueLiterals0, displayLiterals0), zeroFns);
501        builder.put("display", display);
502        @SuppressWarnings("unchecked")
503        Property elevation = new Property(
504            5, union(elevationLiterals0, mozOpacityLiterals0), zeroFns);
505        builder.put("elevation", elevation);
506        @SuppressWarnings("unchecked")
507        Property emptyCells = new Property(
508            0, union(emptyCellsLiterals0, mozOpacityLiterals0), zeroFns);
509        builder.put("empty-cells", emptyCells);
510        //builder.put("filter",
511        //            new Property(0, ImmutableSet.<String>of(), filterFunctions));
512        @SuppressWarnings("unchecked")
513        Property cssFloat = new Property(
514            0, union(azimuthLiterals1, cueLiterals0), zeroFns);
515        builder.put("float", cssFloat);
516        @SuppressWarnings("unchecked")
517        Property font = new Property(
518            73,
519            union(fontLiterals0, fontLiterals1, fontLiterals2, fontLiterals3,
520                  fontLiterals4, fontLiterals5),
521            zeroFns);
522        builder.put("font", font);
523        @SuppressWarnings("unchecked")
524        Property fontFamily = new Property(
525            72, union(fontFamilyLiterals0, fontLiterals3), zeroFns);
526        builder.put("font-family", fontFamily);
527        @SuppressWarnings("unchecked")
528        Property fontSize = new Property(
529            1, union(fontLiterals1, mozOutlineWidthLiterals0), zeroFns);
530        builder.put("font-size", fontSize);
531        @SuppressWarnings("unchecked")
532        Property fontStretch = new Property(
533            0, union(fontStretchLiterals0, fontStretchLiterals1), zeroFns);
534        builder.put("font-stretch", fontStretch);
535        @SuppressWarnings("unchecked")
536        Property fontStyle = new Property(
537            0, union(fontLiterals4, fontStyleLiterals0), zeroFns);
538        builder.put("font-style", fontStyle);
539        builder.put("font-variant", new Property(
540            0, fontVariantLiterals0, zeroFns));
541        @SuppressWarnings("unchecked")
542        Property fontWeight = new Property(
543            0, union(fontLiterals0, fontStyleLiterals0), zeroFns);
544        builder.put("font-weight", fontWeight);
545        Property height = new Property(5, bottomLiterals0, zeroFns);
546        builder.put("height", height);
547        Property letterSpacing = new Property(5, fontStyleLiterals0, zeroFns);
548        builder.put("letter-spacing", letterSpacing);
549        builder.put("line-height", new Property(1, fontStyleLiterals0, zeroFns));
550        @SuppressWarnings("unchecked")
551        Property listStyle = new Property(
552            16,
553            union(listStyleLiterals0, listStyleLiterals1, listStyleLiterals2),
554            backgroundImageFunctions);
555        builder.put("list-style", listStyle);
556        builder.put("list-style-image", new Property(
557            16, cueLiterals0, backgroundImageFunctions));
558        @SuppressWarnings("unchecked")
559        Property listStylePosition = new Property(
560            0, union(listStyleLiterals1, mozOpacityLiterals0), zeroFns);
561        builder.put("list-style-position", listStylePosition);
562        @SuppressWarnings("unchecked")
563        Property listStyleType = new Property(
564            0, union(listStyleLiterals0, listStyleLiterals2), zeroFns);
565        builder.put("list-style-type", listStyleType);
566        Property margin = new Property(1, bottomLiterals0, zeroFns);
567        builder.put("margin", margin);
568        Property maxHeight = new Property(1, maxHeightLiterals0, zeroFns);
569        builder.put("max-height", maxHeight);
570        Property opacity = new Property(1, mozOpacityLiterals0, zeroFns);
571        builder.put("opacity", opacity);
572        builder.put("overflow", new Property(0, overflowLiterals0, zeroFns));
573        @SuppressWarnings("unchecked")
574        Property overflowX = new Property(
575            0, union(overflowXLiterals0, overflowXLiterals1), zeroFns);
576        builder.put("overflow-x", overflowX);
577        Property padding = new Property(1, mozOpacityLiterals0, zeroFns);
578        builder.put("padding", padding);
579        @SuppressWarnings("unchecked")
580        Property pageBreakAfter = new Property(
581            0, union(azimuthLiterals1, pageBreakAfterLiterals0), zeroFns);
582        builder.put("page-break-after", pageBreakAfter);
583        builder.put("page-break-inside", new Property(
584            0, pageBreakInsideLiterals0, zeroFns));
585        @SuppressWarnings("unchecked")
586        Property pitch = new Property(
587            5, union(mozOutlineWidthLiterals0, pitchLiterals0), zeroFns);
588        builder.put("pitch", pitch);
589        builder.put("play-during", new Property(
590            16, playDuringLiterals0, zeroFns));
591        @SuppressWarnings("unchecked")
592        Property position = new Property(
593            0, union(mozOpacityLiterals0, positionLiterals0), zeroFns);
594        builder.put("position", position);
595        builder.put("quotes", new Property(8, cueLiterals0, zeroFns));
596        builder.put("speak", new Property(0, speakLiterals0, zeroFns));
597        builder.put("speak-header", new Property(
598            0, speakHeaderLiterals0, zeroFns));
599        @SuppressWarnings("unchecked")
600        Property speakNumeral = new Property(
601            0, union(mozOpacityLiterals0, speakNumeralLiterals0), zeroFns);
602        builder.put("speak-numeral", speakNumeral);
603        builder.put("speak-punctuation", new Property(
604            0, speakPunctuationLiterals0, zeroFns));
605        @SuppressWarnings("unchecked")
606        Property speechRate = new Property(
607            5, union(mozOutlineWidthLiterals0, speechRateLiterals0), zeroFns);
608        builder.put("speech-rate", speechRate);
609        builder.put("table-layout", new Property(
610            0, tableLayoutLiterals0, zeroFns));
611        @SuppressWarnings("unchecked")
612        Property textAlign = new Property(
613            0, union(azimuthLiterals1, textAlignLiterals0), zeroFns);
614        builder.put("text-align", textAlign);
615        @SuppressWarnings("unchecked")
616        Property textDecoration = new Property(
617            0, union(cueLiterals0, textDecorationLiterals0), zeroFns);
618        builder.put("text-decoration", textDecoration);
619        @SuppressWarnings("unchecked")
620        Property textTransform = new Property(
621            0, union(cueLiterals0, textTransformLiterals0), zeroFns);
622        builder.put("text-transform", textTransform);
623        @SuppressWarnings("unchecked")
624        Property textWrap = new Property(
625            0, union(contentLiterals0, textWrapLiterals0), zeroFns);
626        builder.put("text-wrap", textWrap);
627        @SuppressWarnings("unchecked")
628        Property unicodeBidi = new Property(
629            0, union(fontStyleLiterals0, unicodeBidiLiterals0), zeroFns);
630        builder.put("unicode-bidi", unicodeBidi);
631        @SuppressWarnings("unchecked")
632        Property verticalAlign = new Property(
633            5,
634            union(backgroundLiterals2, mozOpacityLiterals0, verticalAlignLiterals0),
635            zeroFns);
636        builder.put("vertical-align", verticalAlign);
637        builder.put("visibility", new Property(0, visibilityLiterals0, zeroFns));
638        @SuppressWarnings("unchecked")
639        Property voiceFamily = new Property(
640            8, union(fontFamilyLiterals0, voiceFamilyLiterals0), zeroFns);
641        builder.put("voice-family", voiceFamily);
642        @SuppressWarnings("unchecked")
643        Property volume = new Property(
644            1, union(mozOutlineWidthLiterals0, volumeLiterals0), zeroFns);
645        builder.put("volume", volume);
646        @SuppressWarnings("unchecked")
647        Property whiteSpace = new Property(
648            0, union(fontStyleLiterals0, whiteSpaceLiterals0), zeroFns);
649        builder.put("white-space", whiteSpace);
650        builder.put("word-wrap", new Property(0, wordWrapLiterals0, zeroFns));
651        builder.put("zoom", new Property(1, fontStretchLiterals1, zeroFns));
652        Property rgb$Fun = new Property(1, rgb$FunLiterals0, zeroFns);
653        builder.put("rgb()", rgb$Fun);
654        @SuppressWarnings("unchecked")
655        Property image$Fun = new Property(
656            18, union(mozOutlineLiterals0, rgb$FunLiterals0), mozOutlineFunctions);
657        builder.put("image()", image$Fun);
658        @SuppressWarnings("unchecked")
659        Property linearGradient$Fun = new Property(
660            7,
661            union(azimuthLiterals1, backgroundLiterals2,
662                  linearGradient$FunLiterals0, mozOutlineLiterals0),
663            mozOutlineFunctions);
664        builder.put("linear-gradient()", linearGradient$Fun);
665        @SuppressWarnings("unchecked")
666        Property radialGradient$Fun = new Property(
667            7,
668            union(azimuthLiterals1, backgroundLiterals2, mozOutlineLiterals0,
669                  radialGradient$FunLiterals0, radialGradient$FunLiterals1),
670            mozOutlineFunctions);
671        builder.put("radial-gradient()", radialGradient$Fun);
672        builder.put("rect()", new Property(5, rect$FunLiterals0, zeroFns));
673        //builder.put("alpha()", new Property(1, alpha$FunLiterals0, zeroFns));
674        builder.put("-moz-border-radius-bottomright", mozBorderRadiusBottomleft);
675        builder.put("-moz-border-radius-topleft", mozBorderRadiusBottomleft);
676        builder.put("-moz-border-radius-topright", mozBorderRadiusBottomleft);
677        builder.put("-moz-box-shadow", boxShadow);
678        builder.put("-webkit-border-bottom-left-radius", mozBorderRadiusBottomleft);
679        builder.put("-webkit-border-bottom-right-radius",
680                    mozBorderRadiusBottomleft);
681        builder.put("-webkit-border-radius", mozBorderRadius);
682        builder.put("-webkit-border-radius-bottom-left", mozBorderRadiusBottomleft);
683        builder.put("-webkit-border-radius-bottom-right",
684                    mozBorderRadiusBottomleft);
685        builder.put("-webkit-border-radius-top-left", mozBorderRadiusBottomleft);
686        builder.put("-webkit-border-radius-top-right", mozBorderRadiusBottomleft);
687        builder.put("-webkit-border-top-left-radius", mozBorderRadiusBottomleft);
688        builder.put("-webkit-border-top-right-radius", mozBorderRadiusBottomleft);
689        builder.put("-webkit-box-shadow", boxShadow);
690        builder.put("border-bottom", border);
691        builder.put("border-bottom-left-radius", mozBorderRadiusBottomleft);
692        builder.put("border-bottom-right-radius", mozBorderRadiusBottomleft);
693        builder.put("border-bottom-style", mozOutlineStyle);
694        builder.put("border-bottom-width", mozOutlineWidth);
695        builder.put("border-color", borderBottomColor);
696        builder.put("border-left", border);
697        builder.put("border-left-color", borderBottomColor);
698        builder.put("border-left-style", mozOutlineStyle);
699        builder.put("border-left-width", mozOutlineWidth);
700        builder.put("border-radius", mozBorderRadius);
701        builder.put("border-right", border);
702        builder.put("border-right-color", borderBottomColor);
703        builder.put("border-right-style", mozOutlineStyle);
704        builder.put("border-right-width", mozOutlineWidth);
705        builder.put("border-style", mozOutlineStyle);
706        builder.put("border-top", border);
707        builder.put("border-top-color", borderBottomColor);
708        builder.put("border-top-left-radius", mozBorderRadiusBottomleft);
709        builder.put("border-top-right-radius", mozBorderRadiusBottomleft);
710        builder.put("border-top-style", mozOutlineStyle);
711        builder.put("border-top-width", mozOutlineWidth);
712        builder.put("border-width", mozOutlineWidth);
713        builder.put("cue-after", cue);
714        builder.put("cue-before", cue);
715        builder.put("left", height);
716        builder.put("margin-bottom", margin);
717        builder.put("margin-left", margin);
718        builder.put("margin-right", margin);
719        builder.put("margin-top", margin);
720        builder.put("max-width", maxHeight);
721        builder.put("min-height", margin);
722        builder.put("min-width", margin);
723        builder.put("outline", mozOutline);
724        builder.put("outline-color", mozOutlineColor);
725        builder.put("outline-style", mozOutlineStyle);
726        builder.put("outline-width", mozOutlineWidth);
727        builder.put("overflow-y", overflowX);
728        builder.put("padding-bottom", padding);
729        builder.put("padding-left", padding);
730        builder.put("padding-right", padding);
731        builder.put("padding-top", padding);
732        builder.put("page-break-before", pageBreakAfter);
733        builder.put("pause", borderSpacing);
734        builder.put("pause-after", borderSpacing);
735        builder.put("pause-before", borderSpacing);
736        builder.put("pitch-range", borderSpacing);
737        builder.put("richness", borderSpacing);
738        builder.put("right", height);
739        builder.put("stress", borderSpacing);
740        builder.put("text-indent", borderSpacing);
741        builder.put("text-overflow", oTextOverflow);
742        builder.put("text-shadow", boxShadow);
743        builder.put("top", height);
744        builder.put("width", margin);
745        builder.put("word-spacing", letterSpacing);
746        builder.put("z-index", bottom);
747        builder.put("rgba()", rgb$Fun);
748        builder.put("repeating-linear-gradient()", linearGradient$Fun);
749        builder.put("repeating-radial-gradient()", radialGradient$Fun);
750        DEFINITIONS = builder.build();
751      }
752    
753      private static <T> ImmutableSet<T> union(ImmutableSet<T>... subsets) {
754        ImmutableSet.Builder<T> all = ImmutableSet.builder();
755        for (ImmutableSet<T> subset : subsets) {
756          all.addAll(subset);
757        }
758        return all.build();
759      }
760    
761      static final ImmutableSet<String> DEFAULT_WHITELIST = ImmutableSet.of(
762          "-moz-border-radius",
763          "-moz-border-radius-bottomleft",
764          "-moz-border-radius-bottomright",
765          "-moz-border-radius-topleft",
766          "-moz-border-radius-topright",
767          "-moz-box-shadow",
768          "-moz-outline",
769          "-moz-outline-color",
770          "-moz-outline-style",
771          "-moz-outline-width",
772          "-o-text-overflow",
773          "-webkit-border-bottom-left-radius",
774          "-webkit-border-bottom-right-radius",
775          "-webkit-border-radius",
776          "-webkit-border-radius-bottom-left",
777          "-webkit-border-radius-bottom-right",
778          "-webkit-border-radius-top-left",
779          "-webkit-border-radius-top-right",
780          "-webkit-border-top-left-radius",
781          "-webkit-border-top-right-radius",
782          "-webkit-box-shadow",
783          "azimuth",
784          "background",
785          "background-attachment",
786          "background-color",
787          "background-image",
788          "background-position",
789          "background-repeat",
790          "border",
791          "border-bottom",
792          "border-bottom-color",
793          "border-bottom-left-radius",
794          "border-bottom-right-radius",
795          "border-bottom-style",
796          "border-bottom-width",
797          "border-collapse",
798          "border-color",
799          "border-left",
800          "border-left-color",
801          "border-left-style",
802          "border-left-width",
803          "border-radius",
804          "border-right",
805          "border-right-color",
806          "border-right-style",
807          "border-right-width",
808          "border-spacing",
809          "border-style",
810          "border-top",
811          "border-top-color",
812          "border-top-left-radius",
813          "border-top-right-radius",
814          "border-top-style",
815          "border-top-width",
816          "border-width",
817          "box-shadow",
818          "caption-side",
819          "color",
820          "cue",
821          "cue-after",
822          "cue-before",
823          "direction",
824          "elevation",
825          "empty-cells",
826          "font",
827          "font-family",
828          "font-size",
829          "font-stretch",
830          "font-style",
831          "font-variant",
832          "font-weight",
833          "height",
834          "image()",
835          "letter-spacing",
836          "line-height",
837          "linear-gradient()",
838          "list-style",
839          "list-style-image",
840          "list-style-position",
841          "list-style-type",
842          "margin",
843          "margin-bottom",
844          "margin-left",
845          "margin-right",
846          "margin-top",
847          "max-height",
848          "max-width",
849          "min-height",
850          "min-width",
851          "outline",
852          "outline-color",
853          "outline-style",
854          "outline-width",
855          "padding",
856          "padding-bottom",
857          "padding-left",
858          "padding-right",
859          "padding-top",
860          "pause",
861          "pause-after",
862          "pause-before",
863          "pitch",
864          "pitch-range",
865          "quotes",
866          "radial-gradient()",
867          "rect()",
868          "repeating-linear-gradient()",
869          "repeating-radial-gradient()",
870          "rgb()",
871          "rgba()",
872          "richness",
873          "speak",
874          "speak-header",
875          "speak-numeral",
876          "speak-punctuation",
877          "speech-rate",
878          "stress",
879          "table-layout",
880          "text-align",
881          "text-decoration",
882          "text-indent",
883          "text-overflow",
884          "text-shadow",
885          "text-transform",
886          "text-wrap",
887          "unicode-bidi",
888          "vertical-align",
889          "voice-family",
890          "volume",
891          "white-space",
892          "width",
893          "word-spacing",
894          "word-wrap"
895      );
896    
897      /**
898       * A schema that includes only those properties on the default schema
899       * white-list.
900       */
901      public static final CssSchema DEFAULT =
902          CssSchema.withProperties(DEFAULT_WHITELIST);
903    
904      /** Dumps key and literal list to stdout for easy examination. */
905      public static void main(String... argv) {
906        SortedSet<String> keys = Sets.newTreeSet();
907        SortedSet<String> literals = Sets.newTreeSet();
908    
909        for (ImmutableMap.Entry<String, Property> e : DEFINITIONS.entrySet()) {
910          keys.add(e.getKey());
911          literals.addAll(e.getValue().literals);
912        }
913    
914        System.out.println(
915            "# Below two blocks of tokens.\n"
916                + "#\n"
917            + "# First are all property names.\n"
918            + "# Those followed by an asterisk (*) are in the default white-list.\n"
919            + "#\n"
920            + "# Second are the literal tokens recognized in any defined property\n"
921            + "# value.\n"
922            );
923        for (String key : keys) {
924          System.out.print(key);
925          if (DEFAULT_WHITELIST.contains(key)) { System.out.print("*"); }
926          System.out.println();
927        }
928        System.out.println();
929        for (String literal : literals) {
930          System.out.println(literal);
931        }
932      }
933    }