Index: src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15945)
+++ src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(working copy)
@@ -94,6 +94,7 @@
 public class MapCSSTagChecker extends Test.TagTest {
     MapCSSTagCheckerIndex indexData;
     final Set<OsmPrimitive> tested = new HashSet<>();
+    final Map<IPrimitive, Area> mpAreaCache = new HashMap<>();
 
     /**
     * A grouped MapCSSRule with multiple selectors for a single declaration.
@@ -708,6 +709,7 @@
         MapCSSRuleIndex matchingRuleIndex = indexData.get(p);
 
         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;
 
@@ -774,10 +776,12 @@
         return false;
     }
 
-    private static Collection<TestError> getErrorsForPrimitive(OsmPrimitive p, boolean includeOtherSeverity,
+    private Collection<TestError> getErrorsForPrimitive(OsmPrimitive p, boolean includeOtherSeverity,
             Collection<Set<TagCheck>> checksCol) {
+        // this variant is only used by the assertion tests
         final List<TestError> r = new ArrayList<>();
         final Environment env = new Environment(p, new MultiCascade(), Environment.DEFAULT_LAYER, null);
+        env.mpAreaCache = mpAreaCache;
         for (Set<TagCheck> schecks : checksCol) {
             for (TagCheck check : schecks) {
                 boolean ignoreError = Severity.OTHER == check.getSeverity() && !includeOtherSeverity;
@@ -991,6 +995,7 @@
             indexData = new MapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), MapCSSTagCheckerIndex.ALL_TESTS);
         }
         tested.clear();
+        mpAreaCache.clear();
     }
 
     @Override
@@ -1025,5 +1030,6 @@
         super.endTest();
         // no need to keep the index, it is quickly build and doubles the memory needs
         indexData = null;
+        mpAreaCache.clear();
     }
 }
Index: src/org/openstreetmap/josm/gui/mappaint/Environment.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/Environment.java	(revision 15945)
+++ src/org/openstreetmap/josm/gui/mappaint/Environment.java	(working copy)
@@ -76,6 +76,11 @@
     public Map<IPrimitive, Area> intersections;
 
     /**
+     * Cache for multipolygon areas, can be null, used with CrossingFinder
+     */
+    public Map<IPrimitive, Area> mpAreaCache;
+
+    /**
      * Creates a new uninitialized environment.
      */
     public Environment() {
@@ -126,6 +131,7 @@
         this.context = other.getContext();
         this.children = other.children == null ? null : new LinkedHashSet<>(other.children);
         this.intersections = other.intersections == null ? null : new HashMap<>(other.intersections);
+        this.mpAreaCache = other.mpAreaCache; // don't create a copy
     }
 
     /**
Index: src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
===================================================================
--- src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 15945)
+++ src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(working copy)
@@ -305,27 +305,34 @@
 
             private CrossingFinder(Environment e) {
                 super(e);
-                CheckParameterUtil.ensureThat(e.osm instanceof IWay, "Only ways are supported");
+                CheckParameterUtil.ensureThat(isArea(e.osm), "Only areas are supported");
                 layer = OsmUtils.getLayer(e.osm);
             }
 
             @Override
-            public void visit(IWay<?> w) {
-                if (Objects.equals(layer, OsmUtils.getLayer(w))
-                    && left.matches(new Environment(w).withParent(e.osm))
-                    && e.osm instanceof IWay) {
-                    if (area == null) {
-                        area = Geometry.getAreaEastNorth(e.osm);
-                    }
-                    Pair<PolygonIntersection, Area> is = Geometry.polygonIntersectionResult(
-                            Geometry.getAreaEastNorth(w), area, Geometry.INTERSECTION_EPS_EAST_NORTH);
-                    if (Geometry.PolygonIntersection.CROSSING == is.a) {
-                        addToChildren(e, w);
-                        // store intersection area to improve highlight and zoom to problem
-                        if (e.intersections == null) {
-                            e.intersections = new HashMap<>();
+            public void visit(Collection<? extends IPrimitive> primitives) {
+                List<? extends IPrimitive> toIgnore;
+                if (e.osm instanceof Relation) {
+                    toIgnore = ((IRelation<?>) e.osm).getMemberPrimitivesList();
+                } else
+                    toIgnore = null;
+                for (IPrimitive p : primitives) {
+                    if (isPrimitiveUsable(p) && Objects.equals(layer, OsmUtils.getLayer(p))
+                            && left.matches(new Environment(p).withParent(e.osm)) && isArea(p)
+                            && (toIgnore == null || !toIgnore.contains(p))) {
+                        if (area == null) {
+                            area = getAreaEastNorth(e.osm, e);
                         }
-                        e.intersections.put(w, is.b);
+                        Pair<PolygonIntersection, Area> is = Geometry.polygonIntersectionResult(getAreaEastNorth(p, e),
+                                area, Geometry.INTERSECTION_EPS_EAST_NORTH);
+                        if (Geometry.PolygonIntersection.CROSSING == is.a) {
+                            addToChildren(e, p);
+                            // store intersection area to improve highlight and zoom to problem
+                            if (e.intersections == null) {
+                                e.intersections = new HashMap<>();
+                            }
+                            e.intersections.put(p, is.b);
+                        }
                     }
                 }
             }
@@ -452,13 +459,12 @@
                 visitBBox(e, insideOrEqualFinder);
                 return ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type ? e.children != null : e.children == null;
 
-            } else if (ChildOrParentSelectorType.CROSSING == type && e.osm instanceof IWay) {
+            } else if (ChildOrParentSelectorType.CROSSING == type) {
                 e.parent = e.osm;
-                if (right instanceof OptimizedGeneralSelector
-                        && e.osm.getDataSet() != null
-                        && ((OptimizedGeneralSelector) right).matchesBase(OsmPrimitiveType.WAY)) {
+                if (e.osm.getDataSet() != null && isArea(e.osm)) {
                     final CrossingFinder crossingFinder = new CrossingFinder(e);
-                    crossingFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
+                    visitBBox(e, crossingFinder);
+                    return e.children != null;
                 }
                 return e.children != null;
             } else if (ChildOrParentSelectorType.SIBLING == type) {
@@ -538,6 +544,18 @@
             return new ChildOrParentSelector(left, link, right.optimizedBaseCheck(), type);
         }
 
+        private Area getAreaEastNorth(IPrimitive p, Environment e) {
+            if (e.mpAreaCache != null && p.isMultipolygon()) {
+                Area area = e.mpAreaCache.get(p);
+                if (area == null) {
+                    area = Geometry.getAreaEastNorth(p);
+                    e.mpAreaCache.put(p, area);
+                }
+                return area;
+            }
+            return Geometry.getAreaEastNorth(p);
+        }
+
         @Override
         public String toString() {
             return left.toString() + ' ' + (ChildOrParentSelectorType.PARENT == type ? '<' : '>') + link + ' ' + right;
