Index: /trunk/scripts/TagInfoExtract.java
===================================================================
--- /trunk/scripts/TagInfoExtract.java	(revision 15987)
+++ /trunk/scripts/TagInfoExtract.java	(revision 15988)
@@ -342,5 +342,6 @@
         private List<TagInfoTag> convertStyleSheet() {
             return styleSource.rules.stream()
-                    .flatMap(rule -> rule.selector.getConditions().stream())
+                    .flatMap(rule -> rule.selectors.stream())
+                    .flatMap(selector -> selector.getConditions().stream())
                     .filter(ConditionFactory.SimpleKeyValueCondition.class::isInstance)
                     .map(ConditionFactory.SimpleKeyValueCondition.class::cast)
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15987)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15988)
@@ -15,5 +15,4 @@
 import java.util.HashSet;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -26,4 +25,5 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Stream;
 
 import org.openstreetmap.josm.command.ChangePropertyCommand;
@@ -47,10 +47,8 @@
 import org.openstreetmap.josm.gui.mappaint.MultiCascade;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
-import org.openstreetmap.josm.gui.mappaint.mapcss.Declaration;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
-import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
@@ -76,59 +74,7 @@
 public class MapCSSTagChecker extends Test.TagTest {
     private MapCSSTagCheckerIndex indexData;
+    final Map<MapCSSRule, MapCSSTagCheckerAndRule> ruleToCheckMap = new HashMap<>();
     private final Set<OsmPrimitive> tested = new HashSet<>();
     private static final Map<IPrimitive, Area> mpAreaCache = new HashMap<>();
-
-    /**
-    * A grouped MapCSSRule with multiple selectors for a single declaration.
-    * @see MapCSSRule
-    */
-    public static class GroupedMapCSSRule {
-        /** MapCSS selectors **/
-        public final List<Selector> selectors;
-        /** MapCSS declaration **/
-        public final Declaration declaration;
-        /** MapCSS source **/
-        public final String source;
-
-        /**
-         * Constructs a new {@code GroupedMapCSSRule} with empty source
-         * @param selectors MapCSS selectors
-         * @param declaration MapCSS declaration
-         */
-        public GroupedMapCSSRule(List<Selector> selectors, Declaration declaration) {
-            this(selectors, declaration, "");
-        }
-
-        /**
-         * Constructs a new {@code GroupedMapCSSRule}.
-         * @param selectors MapCSS selectors
-         * @param declaration MapCSS declaration
-         * @param source the source of the rule
-         */
-        public GroupedMapCSSRule(List<Selector> selectors, Declaration declaration, String source) {
-            this.selectors = selectors;
-            this.declaration = declaration;
-            this.source = source;
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(selectors, declaration);
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) return true;
-            if (obj == null || getClass() != obj.getClass()) return false;
-            GroupedMapCSSRule that = (GroupedMapCSSRule) obj;
-            return Objects.equals(selectors, that.selectors) &&
-                    Objects.equals(declaration, that.declaration);
-        }
-
-        @Override
-        public String toString() {
-            return "GroupedMapCSSRule [selectors=" + selectors + ", declaration=" + declaration + ']';
-        }
-    }
 
     /**
@@ -281,5 +227,5 @@
     public static class TagCheck implements Predicate<OsmPrimitive> {
         /** The selector of this {@code TagCheck} */
-        protected final GroupedMapCSSRule rule;
+        protected final MapCSSRule rule;
         /** Commands to apply in order to fix a matching primitive */
         protected final List<FixCommand> fixCommands;
@@ -296,5 +242,5 @@
         protected String group;
 
-        TagCheck(GroupedMapCSSRule rule) {
+        TagCheck(MapCSSRule rule) {
             this.rule = rule;
             this.fixCommands = new ArrayList<>();
@@ -320,5 +266,5 @@
         private static final String POSSIBLE_THROWS = "throwError/throwWarning/throwOther";
 
-        static TagCheck ofMapCSSRule(final GroupedMapCSSRule rule, AssertionConsumer assertionConsumer) throws IllegalDataException {
+        static TagCheck ofMapCSSRule(final MapCSSRule rule, AssertionConsumer assertionConsumer) throws IllegalDataException {
             final TagCheck check = new TagCheck(rule);
             final Map<String, Boolean> assertions = new HashMap<>();
@@ -394,8 +340,8 @@
 
         static ParseResult readMapCSS(Reader css) throws ParseException {
-            return readMapCSS(css, "", null);
-        }
-
-        static ParseResult readMapCSS(Reader css, String url, AssertionConsumer assertionConsumer) throws ParseException {
+            return readMapCSS(css, null);
+        }
+
+        static ParseResult readMapCSS(Reader css, AssertionConsumer assertionConsumer) throws ParseException {
             CheckParameterUtil.ensureParameterNotNull(css, "css");
 
@@ -407,20 +353,8 @@
             // Ignore "meta" rule(s) from external rules of JOSM wiki
             source.removeMetaRules();
-            // group rules with common declaration block
-            Map<Declaration, List<Selector>> g = new LinkedHashMap<>();
+            List<TagCheck> parseChecks = new ArrayList<>();
             for (MapCSSRule rule : source.rules) {
-                if (!g.containsKey(rule.declaration)) {
-                    List<Selector> sels = new ArrayList<>();
-                    sels.add(rule.selector);
-                    g.put(rule.declaration, sels);
-                } else {
-                    g.get(rule.declaration).add(rule.selector);
-                }
-            }
-            List<TagCheck> parseChecks = new ArrayList<>();
-            for (Map.Entry<Declaration, List<Selector>> map : g.entrySet()) {
                 try {
-                    parseChecks.add(TagCheck.ofMapCSSRule(
-                            new GroupedMapCSSRule(map.getValue(), map.getKey(), url), assertionConsumer));
+                    parseChecks.add(TagCheck.ofMapCSSRule(rule, assertionConsumer));
                 } catch (IllegalDataException e) {
                     Logging.error("Cannot add MapCss rule: "+e.getMessage());
@@ -661,8 +595,18 @@
 
     static class MapCSSTagCheckerAndRule extends MapCSSTagChecker {
-        public final GroupedMapCSSRule rule;
-
-        MapCSSTagCheckerAndRule(GroupedMapCSSRule rule) {
+        public final MapCSSRule rule;
+        private final TagCheck tagCheck;
+        private final String source;
+
+        MapCSSTagCheckerAndRule(MapCSSRule rule) {
             this.rule = rule;
+            this.tagCheck = null;
+            this.source = "";
+        }
+
+        MapCSSTagCheckerAndRule(TagCheck tagCheck, String source) {
+            this.rule = tagCheck.rule;
+            this.tagCheck = tagCheck;
+            this.source = source;
         }
 
@@ -674,6 +618,18 @@
         @Override
         public String getSource() {
-            return tr("URL / File: {0}", rule.source);
-        }
+            return tr("URL / File: {0}", source);
+        }
+    }
+
+    static MapCSSTagCheckerIndex createMapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
+        final MapCSSTagCheckerIndex index = new MapCSSTagCheckerIndex();
+        final Stream<MapCSSRule> ruleStream = checks.values().stream()
+                .flatMap(Collection::stream)
+                // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
+                .filter(c -> includeOtherSeverity || Severity.OTHER != c.getSeverity() || !c.setClassExpressions.isEmpty())
+                .filter(c -> allTests || c.rule.selectors.stream().anyMatch(Selector.ChildOrParentSelector.class::isInstance))
+                .map(c -> c.rule);
+        index.buildIndex(ruleStream);
+        return index;
     }
 
@@ -687,29 +643,33 @@
         final List<TestError> res = new ArrayList<>();
         if (indexData == null) {
-            indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverity, MapCSSTagCheckerIndex.ALL_TESTS);
-        }
-
-        MapCSSRuleIndex matchingRuleIndex = indexData.get(p);
+            indexData = MapCSSTagCheckerIndex.createMapCSSTagCheckerIndex(checks, includeOtherSeverity, MapCSSTagCheckerIndex.ALL_TESTS);
+        }
 
         Environment env = new Environment(p, new MultiCascade(), Environment.DEFAULT_LAYER, null);
         env.mpAreaCache = mpAreaCache;
 
-        // the declaration indices are sorted, so it suffices to save the last used index
-        Declaration lastDeclUsed = null;
-
-        Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(p);
+        Iterator<MapCSSRule> candidates = indexData.getRuleCandidates(p);
         while (candidates.hasNext()) {
             MapCSSRule r = candidates.next();
-            env.clearSelectorMatchingInformation();
-            if (r.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
-                TagCheck check = indexData.getCheck(r);
+            for (Selector selector : r.selectors) {
+                env.clearSelectorMatchingInformation();
+                if (!selector.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
+                    continue;
+                }
+                MapCSSTagCheckerAndRule test = ruleToCheckMap.computeIfAbsent(r, rule -> checks.entrySet().stream()
+                        .map(e -> e.getValue().stream()
+                                // rule.selectors might be different due to MapCSSStyleIndex, however, the declarations are the same object
+                                .filter(c -> c.rule.declaration == rule.declaration)
+                                .findFirst()
+                                .map(c -> new MapCSSTagCheckerAndRule(c, e.getKey()))
+                                .orElse(null))
+                        .filter(Objects::nonNull)
+                        .findFirst()
+                        .orElse(null));
+                TagCheck check = test == null ? null : test.tagCheck;
                 if (check != null) {
-                    if (r.declaration == lastDeclUsed)
-                        continue; // don't apply one declaration more than once
-                    lastDeclUsed = r.declaration;
-
                     r.declaration.execute(env);
                     if (!check.errors.isEmpty()) {
-                        for (TestError e: check.getErrorsForPrimitive(p, r.selector, env, new MapCSSTagCheckerAndRule(check.rule))) {
+                        for (TestError e: check.getErrorsForPrimitive(p, selector, env, test)) {
                             addIfNotSimilar(e, res);
                         }
@@ -831,5 +791,5 @@
             if (zip != null)
                 I18n.addTexts(cache.getFile());
-            result = TagCheck.readMapCSS(reader, url, assertionConsumer);
+            result = TagCheck.readMapCSS(reader, assertionConsumer);
             checks.remove(url);
             checks.putAll(url, result.parseChecks);
@@ -890,5 +850,5 @@
         super.setShowElements(true);
         if (indexData == null) {
-            indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ALL_TESTS);
+            indexData = MapCSSTagCheckerIndex.createMapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ALL_TESTS);
         }
         tested.clear();
@@ -904,5 +864,5 @@
             // rebuild index with a reduced set of rules (those that use ChildOrParentSelector) and thus may have left selectors
             // matching the previously tested elements
-            indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ONLY_SELECTED_TESTS);
+            indexData = MapCSSTagCheckerIndex.createMapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ONLY_SELECTED_TESTS);
 
             Set<OsmPrimitive> surrounding = new HashSet<>();
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerIndex.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerIndex.java	(revision 15987)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerIndex.java	(revision 15988)
@@ -5,21 +5,22 @@
 
 import java.text.MessageFormat;
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
-import java.util.List;
+import java.util.Iterator;
 import java.util.Map;
-import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import org.openstreetmap.josm.data.osm.INode;
+import org.openstreetmap.josm.data.osm.IPrimitive;
 import org.openstreetmap.josm.data.osm.IRelation;
 import org.openstreetmap.josm.data.osm.IWay;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.TagCheck;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Declaration;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource.MapCSSRuleIndex;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
-import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector;
 import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
@@ -31,6 +32,6 @@
  *
  */
-final class MapCSSTagCheckerIndex {
-    final Map<MapCSSRule, TagCheck> ruleToCheckMap = new HashMap<>();
+public final class MapCSSTagCheckerIndex {
+    final Map<Declaration, TagCheck> ruleToCheckMap = new HashMap<>();
 
     static final boolean ALL_TESTS = true;
@@ -57,15 +58,28 @@
      */
     final MapCSSRuleIndex multipolygonRules = new MapCSSRuleIndex();
-
-    MapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
-        buildIndex(checks, includeOtherSeverity, allTests);
-    }
-
-    private void buildIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
-        List<TagCheck> allChecks = new ArrayList<>();
-        for (Set<TagCheck> cs : checks.values()) {
-            allChecks.addAll(cs);
-        }
-
+    /**
+     * rules to apply canvas properties
+     */
+    final MapCSSRuleIndex canvasRules = new MapCSSRuleIndex();
+
+    static MapCSSTagCheckerIndex createMapCSSTagCheckerIndex(MultiMap<String, TagCheck> checks, boolean includeOtherSeverity, boolean allTests) {
+        final MapCSSTagCheckerIndex index = new MapCSSTagCheckerIndex();
+        final Stream<MapCSSRule> ruleStream = checks.values().stream()
+                .flatMap(Collection::stream)
+                // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
+                .filter(c -> includeOtherSeverity || Severity.OTHER != c.getSeverity() || !c.setClassExpressions.isEmpty())
+                .filter(c -> allTests || c.rule.selectors.stream().anyMatch(Selector.ChildOrParentSelector.class::isInstance))
+                .peek(c -> index.ruleToCheckMap.put(c.rule.declaration, c))
+                .map(c -> c.rule);
+        index.buildIndex(ruleStream);
+        return index;
+    }
+
+    /**
+     * Clear the index.
+     * <p>
+     * You must own the write lock STYLE_SOURCE_LOCK when calling this method.
+     */
+    public void clear() {
         ruleToCheckMap.clear();
         nodeRules.clear();
@@ -74,23 +88,20 @@
         relationRules.clear();
         multipolygonRules.clear();
-
+        canvasRules.clear();
+    }
+
+    /**
+     * Builds and initializes the index.
+     * <p>
+     * You must own the write lock of STYLE_SOURCE_LOCK when calling this method.
+     */
+    public void buildIndex(Stream<MapCSSRule> ruleStream) {
+        clear();
         // optimization: filter rules for different primitive types
-        for (TagCheck c : allChecks) {
-            if (!includeOtherSeverity && Severity.OTHER == c.getSeverity()
-                    && c.setClassExpressions.isEmpty()) {
-                // Ignore "information" level checks if not wanted, unless they also set a MapCSS class
-                continue;
-            }
-
-            for (Selector s : c.rule.selectors) {
-                boolean hasLeftRightSel = s instanceof Selector.ChildOrParentSelector;
-                if (!allTests && !hasLeftRightSel) {
-                    continue;
-                }
-
-                MapCSSRule optRule = new MapCSSRule(s, c.rule.declaration);
-
-                ruleToCheckMap.put(optRule, c);
-                final String base = s.getBase();
+        ruleStream.forEach(rule -> {
+            final Map<String, MapCSSRule> selectorsByBase = rule.selectors.stream()
+                    .collect(Collectors.groupingBy(Selector::getBase,
+                            Collectors.collectingAndThen(Collectors.toList(), selectors -> new MapCSSRule(selectors, rule.declaration))));
+            selectorsByBase.forEach((base, optRule) -> {
                 switch (base) {
                 case Selector.BASE_NODE:
@@ -117,6 +128,9 @@
                     break;
                 case Selector.BASE_CANVAS:
+                    canvasRules.add(optRule);
+                    break;
                 case Selector.BASE_META:
                 case Selector.BASE_SETTING:
+                case Selector.BASE_SETTINGS:
                     break;
                 default:
@@ -125,6 +139,10 @@
                     Logging.error(e);
                 }
-            }
-        }
+            });
+        });
+        initIndex();
+    }
+
+    private void initIndex() {
         nodeRules.initIndex();
         wayRules.initIndex();
@@ -132,12 +150,13 @@
         relationRules.initIndex();
         multipolygonRules.initIndex();
+        canvasRules.initIndex();
     }
 
     /**
      * Get the index of rules for the given primitive.
-     * @param p the primitve
+     * @param p the primitive
      * @return index of rules for the given primitive
      */
-    public MapCSSRuleIndex get(OsmPrimitive p) {
+    public MapCSSRuleIndex get(IPrimitive p) {
         if (p instanceof INode) {
             return nodeRules;
@@ -151,4 +170,6 @@
             if (((IRelation<?>) p).isMultipolygon()) {
                 return multipolygonRules;
+            } else if (p.hasKey("#canvas")) {
+                return canvasRules;
             } else {
                 return relationRules;
@@ -160,4 +181,17 @@
 
     /**
+     * Get a subset of all rules that might match the primitive. Rules not included in the result are guaranteed to
+     * not match this primitive.
+     * <p>
+     * You must have a read lock of STYLE_SOURCE_LOCK when calling this method.
+     *
+     * @param osm the primitive to match
+     * @return An iterator over possible rules in the right order.
+     */
+    public Iterator<MapCSSRule> getRuleCandidates(IPrimitive osm) {
+        return get(osm).getRuleCandidates(osm);
+    }
+
+    /**
      * return the TagCheck for which the given indexed rule was created.
      * @param rule an indexed rule
@@ -165,5 +199,5 @@
      */
     public TagCheck getCheck(MapCSSRule rule) {
-        return ruleToCheckMap.get(rule);
+        return ruleToCheckMap.get(rule.declaration);
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 15987)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 15988)
@@ -650,8 +650,6 @@
     selectors=selectors()
     decl=declaration()
-    { 
-        for (Selector s : selectors) {
-            sheet.rules.add(new MapCSSRule(s, decl));
-        }
+    {
+        sheet.rules.add(new MapCSSRule(selectors, decl));
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java	(revision 15987)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSRule.java	(revision 15988)
@@ -2,7 +2,9 @@
 package org.openstreetmap.josm.gui.mappaint.mapcss;
 
+import java.util.List;
 import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.gui.mappaint.Environment;
+import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -18,5 +20,5 @@
      * The selector. If it matches, this rule should be applied
      */
-    public final Selector selector;
+    public final List<Selector> selectors;
     /**
      * The instructions for this selector
@@ -26,9 +28,9 @@
     /**
      * Constructs a new {@code MapCSSRule}.
-     * @param selector The selector
+     * @param selectors The selectors
      * @param declaration The declaration
      */
-    public MapCSSRule(Selector selector, Declaration declaration) {
-        this.selector = selector;
+    public MapCSSRule(List<Selector> selectors, Declaration declaration) {
+        this.selectors = Utils.toUnmodifiableList(selectors);
         this.declaration = declaration;
     }
@@ -44,5 +46,5 @@
      */
     public boolean matches(Environment env) {
-        return selector.matches(env);
+        return selectors.stream().anyMatch(s -> s.matches(env));
     }
 
@@ -64,7 +66,10 @@
     @Override
     public String toString() {
-        return selector + declaration.instructions.stream()
+        final String selectorsString = selectors.stream().map(String::valueOf)
+                .collect(Collectors.joining(",\n"));
+        final String declarationString = declaration.instructions.stream()
                 .map(String::valueOf)
                 .collect(Collectors.joining("\n  ", " {\n  ", "\n}"));
+        return selectorsString + declarationString;
     }
 }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 15987)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSource.java	(revision 15988)
@@ -14,8 +14,6 @@
 import java.lang.reflect.Field;
 import java.nio.charset.StandardCharsets;
-import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.BitSet;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -36,13 +34,10 @@
 
 import org.openstreetmap.josm.data.Version;
-import org.openstreetmap.josm.data.osm.INode;
 import org.openstreetmap.josm.data.osm.IPrimitive;
-import org.openstreetmap.josm.data.osm.IRelation;
-import org.openstreetmap.josm.data.osm.IWay;
 import org.openstreetmap.josm.data.osm.KeyValueVisitor;
 import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmUtils;
 import org.openstreetmap.josm.data.osm.Tagged;
 import org.openstreetmap.josm.data.preferences.sources.SourceEntry;
+import org.openstreetmap.josm.data.validation.tests.MapCSSTagCheckerIndex;
 import org.openstreetmap.josm.gui.mappaint.Cascade;
 import org.openstreetmap.josm.gui.mappaint.Environment;
@@ -89,27 +84,7 @@
     public final List<MapCSSRule> rules = new ArrayList<>();
     /**
-     * Rules for nodes
-     */
-    public final MapCSSRuleIndex nodeRules = new MapCSSRuleIndex();
-    /**
-     * Rules for ways without tag area=no
-     */
-    public final MapCSSRuleIndex wayRules = new MapCSSRuleIndex();
-    /**
-     * Rules for ways with tag area=no
-     */
-    public final MapCSSRuleIndex wayNoAreaRules = new MapCSSRuleIndex();
-    /**
-     * Rules for relations that are not multipolygon relations
-     */
-    public final MapCSSRuleIndex relationRules = new MapCSSRuleIndex();
-    /**
-     * Rules for multipolygon relations
-     */
-    public final MapCSSRuleIndex multipolygonRules = new MapCSSRuleIndex();
-    /**
-     * rules to apply canvas properties
-     */
-    public final MapCSSRuleIndex canvasRules = new MapCSSRuleIndex();
+     * Index of rules in this style file
+     */
+    private final MapCSSTagCheckerIndex ruleIndex = new MapCSSTagCheckerIndex();
 
     private Color backgroundColorOverride;
@@ -292,19 +267,22 @@
             for (int ruleIndex = 0; ruleIndex < rules.size(); ruleIndex++) {
                 MapCSSRule r = rules.get(ruleIndex);
-                final List<Condition> conditions = r.selector.getConditions();
-                if (conditions == null || conditions.isEmpty()) {
-                    remaining.set(ruleIndex);
-                    continue;
-                }
-                Optional<SimpleKeyValueCondition> lastCondition = Utils.filteredCollection(conditions, SimpleKeyValueCondition.class).stream()
-                        .reduce((first, last) -> last);
-                if (lastCondition.isPresent()) {
-                    getEntryInIndex(lastCondition.get().k).addForKeyAndValue(lastCondition.get().v, ruleIndex);
-                } else {
-                    String key = findAnyRequiredKey(conditions);
-                    if (key != null) {
-                        getEntryInIndex(key).addForKey(ruleIndex);
+                for (Selector selector : r.selectors) {
+                    final List<Condition> conditions = selector.getConditions();
+                    if (conditions == null || conditions.isEmpty()) {
+                        remaining.set(ruleIndex);
+                        continue;
+                    }
+                    Optional<SimpleKeyValueCondition> lastCondition = Utils.filteredCollection(conditions, SimpleKeyValueCondition.class)
+                            .stream()
+                            .reduce((first, last) -> last);
+                    if (lastCondition.isPresent()) {
+                        getEntryInIndex(lastCondition.get().k).addForKeyAndValue(lastCondition.get().v, ruleIndex);
                     } else {
-                        remaining.set(ruleIndex);
+                        String key = findAnyRequiredKey(conditions);
+                        if (key != null) {
+                            getEntryInIndex(key).addForKey(ruleIndex);
+                        } else {
+                            remaining.set(ruleIndex);
+                        }
                     }
                 }
@@ -418,10 +396,5 @@
             init();
             rules.clear();
-            nodeRules.clear();
-            wayRules.clear();
-            wayNoAreaRules.clear();
-            relationRules.clear();
-            multipolygonRules.clear();
-            canvasRules.clear();
+            ruleIndex.clear();
             // remove "areaStyle" pseudo classes intended only for validator (causes StackOverflowError otherwise), see #16183
             removeAreaStylePseudoClass = url == null || !url.contains("validator"); // resource://data/validator/ or xxx.validator.mapcss
@@ -461,49 +434,5 @@
             }
             // optimization: filter rules for different primitive types
-            for (MapCSSRule optRule: rules) {
-                final String base = optRule.selector.getBase();
-                switch (base) {
-                    case Selector.BASE_NODE:
-                        nodeRules.add(optRule);
-                        break;
-                    case Selector.BASE_WAY:
-                        wayNoAreaRules.add(optRule);
-                        wayRules.add(optRule);
-                        break;
-                    case Selector.BASE_AREA:
-                        wayRules.add(optRule);
-                        multipolygonRules.add(optRule);
-                        break;
-                    case Selector.BASE_RELATION:
-                        relationRules.add(optRule);
-                        multipolygonRules.add(optRule);
-                        break;
-                    case Selector.BASE_ANY:
-                        nodeRules.add(optRule);
-                        wayRules.add(optRule);
-                        wayNoAreaRules.add(optRule);
-                        relationRules.add(optRule);
-                        multipolygonRules.add(optRule);
-                        break;
-                    case Selector.BASE_CANVAS:
-                        canvasRules.add(optRule);
-                        break;
-                    case Selector.BASE_META:
-                    case Selector.BASE_SETTING:
-                    case Selector.BASE_SETTINGS:
-                        break;
-                    default:
-                        final RuntimeException e = new JosmRuntimeException(MessageFormat.format("Unknown MapCSS base selector {0}", base));
-                        Logging.warn(tr("Failed to parse Mappaint styles from ''{0}''. Error was: {1}", url, e.getMessage()));
-                        Logging.error(e);
-                        logError(e);
-                }
-            }
-            nodeRules.initIndex();
-            wayRules.initIndex();
-            wayNoAreaRules.initIndex();
-            relationRules.initIndex();
-            multipolygonRules.initIndex();
-            canvasRules.initIndex();
+            ruleIndex.buildIndex(rules.stream());
             loaded = true;
         } finally {
@@ -588,10 +517,10 @@
         // Parse rules
         for (MapCSSRule r : rules) {
-            if (r.selector instanceof GeneralSelector) {
-                GeneralSelector gs = (GeneralSelector) r.selector;
+            final Selector gs = r.selectors.get(0);
+            if (gs instanceof GeneralSelector) {
                 if (Selector.BASE_SETTING.equals(gs.getBase())) {
-                    loadSettings(r, gs, env);
+                    loadSettings(r, ((GeneralSelector) gs), env);
                 } else if (Selector.BASE_SETTINGS.equals(gs.getBase())) {
-                    loadSettings(r, gs, envGroups);
+                    loadSettings(r, ((GeneralSelector) gs), envGroups);
                 }
             }
@@ -639,12 +568,9 @@
 
         for (MapCSSRule r : rules) {
-            if (r.selector instanceof GeneralSelector) {
-                GeneralSelector gs = (GeneralSelector) r.selector;
-                if (gs.getBase().equals(type)) {
-                    if (!gs.matchesConditions(env)) {
-                        continue;
-                    }
-                    r.execute(env);
-                }
+            final boolean matches = r.selectors.stream().anyMatch(gs -> gs instanceof GeneralSelector
+                    && gs.getBase().equals(type)
+                    && ((GeneralSelector) gs).matchesConditions(env));
+            if (matches) {
+                r.execute(env);
             }
         }
@@ -659,24 +585,4 @@
     @Override
     public void apply(MultiCascade mc, IPrimitive osm, double scale, boolean pretendWayIsClosed) {
-        MapCSSRuleIndex matchingRuleIndex;
-        if (osm instanceof INode) {
-            matchingRuleIndex = nodeRules;
-        } else if (osm instanceof IWay) {
-            if (OsmUtils.isFalse(osm.get("area"))) {
-                matchingRuleIndex = wayNoAreaRules;
-            } else {
-                matchingRuleIndex = wayRules;
-            }
-        } else if (osm instanceof IRelation) {
-            if (((IRelation<?>) osm).isMultipolygon()) {
-                matchingRuleIndex = multipolygonRules;
-            } else if (osm.hasKey("#canvas")) {
-                matchingRuleIndex = canvasRules;
-            } else {
-                matchingRuleIndex = relationRules;
-            }
-        } else {
-            throw new IllegalArgumentException("Unsupported type: " + osm);
-        }
 
         Environment env = new Environment(osm, mc, null, this);
@@ -684,12 +590,14 @@
         int lastDeclUsed = -1;
 
-        Iterator<MapCSSRule> candidates = matchingRuleIndex.getRuleCandidates(osm);
+        Iterator<MapCSSRule> candidates = ruleIndex.getRuleCandidates(osm);
         while (candidates.hasNext()) {
             MapCSSRule r = candidates.next();
-            env.clearSelectorMatchingInformation();
-            env.layer = r.selector.getSubpart().getId(env);
-            String sub = env.layer;
-            if (r.selector.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
-                Selector s = r.selector;
+            for (Selector s : r.selectors) {
+                env.clearSelectorMatchingInformation();
+                env.layer = s.getSubpart().getId(env);
+                String sub = env.layer;
+                if (!s.matches(env)) { // as side effect env.parent will be set (if s is a child selector)
+                    continue;
+                }
                 if (s.getRange().contains(scale)) {
                     mc.range = Range.cut(mc.range, s.getRange());
@@ -746,5 +654,5 @@
      */
     public void removeMetaRules() {
-        rules.removeIf(x -> x.selector instanceof GeneralSelector && Selector.BASE_META.equals(x.selector.getBase()));
+        rules.removeIf(x -> x.selectors.get(0) instanceof GeneralSelector && Selector.BASE_META.equals(x.selectors.get(0).getBase()));
     }
 
Index: /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java	(revision 15987)
+++ /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java	(revision 15988)
@@ -67,5 +67,5 @@
         final MapCSSTagChecker test = new MapCSSTagChecker();
         Set<String> errors = new HashSet<>();
-        test.checks.putAll("test", TagCheck.readMapCSS(new StringReader(css), "", errors::add).parseChecks);
+        test.checks.putAll("test", TagCheck.readMapCSS(new StringReader(css), errors::add).parseChecks);
         assertTrue(errors.toString(), errors.isEmpty());
         return test;
Index: /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(revision 15987)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(revision 15988)
@@ -74,5 +74,5 @@
          source.loadStyleSource();
          assertEquals(1, source.rules.size());
-         return (ChildOrParentSelector) source.rules.get(0).selector;
+         return (ChildOrParentSelector) source.rules.get(0).selectors.get(0);
     }
 
