Index: /trunk/data_nodist/amenity-in-amenity.osm
===================================================================
--- /trunk/data_nodist/amenity-in-amenity.osm	(revision 15101)
+++ /trunk/data_nodist/amenity-in-amenity.osm	(revision 15102)
@@ -5,11 +5,11 @@
   <node id='-3875' action='modify' visible='true' lat='-22.6907376271' lon='-48.56085907164' />
   <node id='-3871' action='modify' visible='true' lat='-22.69070289438' lon='-48.56081834975' />
-  <node id='-3864' visible='true' lat='-22.69073023197' lon='-48.56082133585'>
-    <tag k='amenity' v='hospital' />
-  </node>
   <node id='-3863' visible='true' lat='-22.69071631095' lon='-48.56081028236' />
   <node id='-3862' visible='true' lat='-22.69074204865' lon='-48.56081115962' />
   <node id='-3861' visible='true' lat='-22.69071809155' lon='-48.56082765213' />
   <node id='-3860' visible='true' lat='-22.69074528609' lon='-48.56083782836' />
+  <node id='123' version='3' lat='-22.69073023197' lon='-48.56082133585'>
+    <tag k='amenity' v='hospital' />
+  </node>
   <way id='-3880' action='modify' visible='true'>
     <nd ref='-3877' />
Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15101)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15102)
@@ -867,5 +867,7 @@
             if (partialSelection && r.selector instanceof Selector.ChildOrParentSelector) {
                 ChildOrParentSelector sel = (Selector.ChildOrParentSelector) r.selector;
-                if (sel.type == ChildOrParentSelectorType.ELEMENT_OF && p.getDataSet() != null) {
+                boolean needEnclosing = sel.type == ChildOrParentSelectorType.SUBSET_OR_EQUAL
+                        || sel.type == ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL;
+                if (needEnclosing && p.getDataSet() != null) {
                     List<OsmPrimitive> toCheck = new ArrayList<>();
                     toCheck.addAll(p.getDataSet().searchWays(p.getBBox()));
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 15101)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 15102)
@@ -217,5 +217,8 @@
 |   < FULLSTOP: "." >
 |   < DEG: "°" >
-|   < ELEMENT_OF: "∈" >
+|   < SUBSET_OR_EQUAL: ["∈","⊆"] >
+|   < NOT_SUBSET_OR_EQUAL: "⊈" >
+|   < SUPERSET_OR_EQUAL: "⊇" >
+|   < NOT_SUPERSET_OR_EQUAL: "⊉" >
 |   < CROSSING: "⧉" >
 |   < PERCENT: "%" >
@@ -691,5 +694,11 @@
                 ( ( c=condition(Context.LINK) | c=class_or_pseudoclass(Context.LINK) ) { conditions.add(c); } )*
             |
-                <ELEMENT_OF> { type = Selector.ChildOrParentSelectorType.ELEMENT_OF; }
+                <SUBSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.SUBSET_OR_EQUAL; }
+            |
+                <NOT_SUBSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL; }
+            |
+                <SUPERSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.SUPERSET_OR_EQUAL; }
+            |
+                <NOT_SUPERSET_OR_EQUAL> { type = Selector.ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL; }
             |
                 <CROSSING> { type = Selector.ChildOrParentSelectorType.CROSSING; }
Index: /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 15101)
+++ /trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/Selector.java	(revision 15102)
@@ -118,5 +118,5 @@
      */
     enum ChildOrParentSelectorType {
-        CHILD, PARENT, ELEMENT_OF, CROSSING, SIBLING
+        CHILD, PARENT, SUBSET_OR_EQUAL, NOT_SUBSET_OR_EQUAL, SUPERSET_OR_EQUAL, NOT_SUPERSET_OR_EQUAL, CROSSING, SIBLING,
     }
 
@@ -313,4 +313,7 @@
         }
 
+        /**
+         * Finds elements which are inside the right element, collects those in {@code children}
+         */
         private class ContainsFinder extends AbstractFinder {
             protected List<IPrimitive> toCheck;
@@ -349,4 +352,59 @@
         }
 
+        /**
+         * Finds elements which are inside the left element, or in other words, it finds elements enclosing e.osm.
+         * The found enclosing elements are collected in {@code e.children}.
+         */
+        private class InsideOrEqualFinder extends AbstractFinder {
+
+            protected InsideOrEqualFinder(Environment e) {
+                super(e);
+            }
+
+            @Override
+            public void visit(IWay<?> w) {
+                if (left.matches(new Environment(w).withParent(e.osm))
+                        && w.getBBox().bounds(e.osm.getBBox())
+                        && !Geometry.filterInsidePolygon(Collections.singletonList(e.osm), w).isEmpty()) {
+                    addToChildren(e, w);
+                }
+            }
+
+            @Override
+            public void visit(IRelation<?> r) {
+                if (r instanceof Relation && r.isMultipolygon() && r.getBBox().bounds(e.osm.getBBox())
+                        && left.matches(new Environment(r).withParent(e.osm))
+                        && !Geometry.filterInsideMultipolygon(Collections.singletonList(e.osm), (Relation) r).isEmpty()) {
+                    addToChildren(e, r);
+                }
+            }
+        }
+
+        private void visitBBox(Environment e, AbstractFinder finder) {
+            boolean withNodes = finder instanceof ContainsFinder;
+            if (left instanceof OptimizedGeneralSelector) {
+                if (withNodes && ((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
+                    finder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
+                }
+                if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
+                    finder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
+                }
+                if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.RELATION)) {
+                    finder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
+                }
+            } else {
+                if (withNodes) {
+                    finder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
+                }
+                finder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
+                finder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
+            }
+        }
+
+        private static boolean isArea(IPrimitive p) {
+            return (p instanceof IWay && ((IWay<?>) p).isClosed() && ((IWay<?>) p).getNodesCount() >= 4)
+                    || (p instanceof IRelation && p.isMultipolygon() && !p.isIncomplete());
+        }
+
         @Override
         public boolean matches(Environment e) {
@@ -355,30 +413,29 @@
                 return false;
 
-            if (ChildOrParentSelectorType.ELEMENT_OF == type) {
-
-                if (e.osm instanceof INode || e.osm.getDataSet() == null) {
-                    // nodes cannot contain elements
-                    return false;
-                }
-
+            if (ChildOrParentSelectorType.SUBSET_OR_EQUAL == type || ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL == type) {
+
+                if (e.osm.getDataSet() == null || !isArea(e.osm)) {
+                    // only areas can contain elements
+                    return ChildOrParentSelectorType.NOT_SUBSET_OR_EQUAL == type;
+                }
                 ContainsFinder containsFinder = new ContainsFinder(e);
                 e.parent = e.osm;
 
-                if (left instanceof OptimizedGeneralSelector) {
-                    if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.NODE)) {
-                        containsFinder.visit(e.osm.getDataSet().searchNodes(e.osm.getBBox()));
-                    }
-                    if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.WAY)) {
-                        containsFinder.visit(e.osm.getDataSet().searchWays(e.osm.getBBox()));
-                    }
-                    if (((OptimizedGeneralSelector) left).matchesBase(OsmPrimitiveType.RELATION)) {
-                        containsFinder.visit(e.osm.getDataSet().searchRelations(e.osm.getBBox()));
-                    }
-                } else {
-                    // use slow test
-                    containsFinder.visit(e.osm.getDataSet().allPrimitives());
-                }
+                visitBBox(e, containsFinder);
                 containsFinder.execGeometryTests();
-                return e.children != null;
+                return ChildOrParentSelectorType.SUBSET_OR_EQUAL == type ? e.children != null : e.children == null;
+
+            } else if (ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type || ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL == type) {
+
+                if (e.osm.getDataSet() == null || (e.osm instanceof INode && ((INode) e.osm).getCoor() == null)
+                        || (!(e.osm instanceof INode) && !isArea(e.osm))) {
+                    return ChildOrParentSelectorType.NOT_SUPERSET_OR_EQUAL == type;
+                }
+
+                InsideOrEqualFinder insideOrEqualFinder = new InsideOrEqualFinder(e);
+                e.parent = e.osm;
+
+                visitBBox(e, insideOrEqualFinder);
+                return ChildOrParentSelectorType.SUPERSET_OR_EQUAL == type ? e.children != null : e.children == null;
 
             } else if (ChildOrParentSelectorType.CROSSING == type && e.osm instanceof IWay) {
Index: /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(revision 15101)
+++ /trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(revision 15102)
@@ -187,10 +187,30 @@
     }
 
+    /**
+     * Test inside/contains selectors (spatial test)
+     */
     @Test
     public void testContains() throws Exception {
         ds = OsmReader.parseDataSet(Files.newInputStream(Paths.get("data_nodist/amenity-in-amenity.osm")), null);
         ChildOrParentSelector css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ∈ *[amenity] {}");
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
         assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
         assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ⊆  *[amenity] {}");
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("node[tag(\"amenity\") = parent_tag(\"amenity\")] ⊈  *[amenity] {}");
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("*[tag(\"amenity\") = parent_tag(\"amenity\")] ⊇  *[amenity] {}");
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
+        css = parse("*[tag(\"amenity\") = parent_tag(\"amenity\")] ⊉  *[amenity] {}");
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.NODE))));
+        assertFalse(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.WAY))));
+        assertTrue(css.matches(new Environment(ds.getPrimitiveById(123, OsmPrimitiveType.RELATION))));
     }
 }
