Index: src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 15994)
+++ src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(working copy)
@@ -5,6 +5,7 @@
 
 import java.awt.geom.Area;
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Reader;
@@ -17,6 +18,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -66,6 +68,7 @@
 import org.openstreetmap.josm.tools.I18n;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.MultiMap;
+import org.openstreetmap.josm.tools.Stopwatch;
 import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -75,7 +78,6 @@
 public class MapCSSTagChecker extends Test.TagTest {
     private MapCSSStyleIndex indexData;
     final Map<MapCSSRule, MapCSSTagCheckerAndRule> ruleToCheckMap = new HashMap<>();
-    private final Set<OsmPrimitive> tested = new HashSet<>();
     private static final Map<IPrimitive, Area> mpAreaCache = new HashMap<>();
     static final boolean ALL_TESTS = true;
     static final boolean ONLY_SELECTED_TESTS = false;
@@ -762,9 +764,6 @@
         for (TestError e : getErrorsForPrimitive(p, ValidatorPrefHelper.PREF_OTHER.get())) {
             addIfNotSimilar(e, errors);
         }
-        if (partialSelection) {
-            tested.add(p);
-        }
     }
 
     /**
@@ -855,15 +854,77 @@
     public synchronized void startTest(ProgressMonitor progressMonitor) {
         super.startTest(progressMonitor);
         super.setShowElements(true);
-        if (indexData == null) {
-            indexData = createMapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), ALL_TESTS);
+    }
+
+    @Override
+    public synchronized void endTest() {
+        // no need to keep the index, it is quickly build and doubles the memory needs
+        indexData = null;
+        // always clear the cache to make sure that we catch changes in geometry
+        mpAreaCache.clear();
+        super.endTest();
+    }
+
+    @Override
+    public void visit(Collection<OsmPrimitive> selection) {
+        if (progressMonitor != null) {
+            progressMonitor.setTicksCount(selection.size() * checks.size());
         }
-        tested.clear();
+
         mpAreaCache.clear();
+
+        Set<OsmPrimitive> surrounding = new HashSet<>();
+        for (Entry<String, Set<TagCheck>> entry : checks.entrySet()) {
+            if (isCanceled()) {
+                break;
+            }
+            visit(entry.getKey(), entry.getValue(), selection, surrounding);
+        }
     }
 
-    @Override
-    public synchronized void endTest() {
+    /**
+     * Perform the checks for one check url
+     * @param url the url for the checks
+     * @param checksForUrl the checks to perform
+     * @param selection collection primitives
+     * @param surrounding surrounding primitives, evtl. filled by this routine
+     */
+    private void visit(String url, Set<TagCheck> checksForUrl, Collection<OsmPrimitive> selection,
+            Set<OsmPrimitive> surrounding) {
+        MultiMap<String, TagCheck> currentCheck = new MultiMap<>();
+        currentCheck.putAll(url, checksForUrl);
+        indexData = createMapCSSTagCheckerIndex(currentCheck, includeOtherSeverityChecks(), ALL_TESTS);
+        Set<OsmPrimitive> tested = new HashSet<>();
+
+
+        String source = simplifySourceName(url);
+        if (progressMonitor != null) {
+            progressMonitor.setExtraText(tr(" {0}", source));
+        }
+        long cnt = 0;
+        Stopwatch stopwatch = Stopwatch.createStarted();
+        for (OsmPrimitive p : selection) {
+            if (isCanceled()) {
+                break;
+            }
+            if (isPrimitiveUsable(p)) {
+                check(p);
+                if (partialSelection) {
+                    tested.add(p);
+                }
+            }
+            if (progressMonitor != null) {
+                progressMonitor.worked(1);
+                cnt++;
+                // add frequently changing info to progress monitor so that it
+                // doesn't seem to hang when test takes longer than 0.5 seconds
+                if (cnt % 10000 == 0 && stopwatch.elapsed() >= 500) {
+                    progressMonitor
+                            .setExtraText(tr(" {0}: {1} of {2} elements done", source, cnt, selection.size()));
+                }
+            }
+        }
+
         if (partialSelection && !tested.isEmpty()) {
             // #14287: see https://josm.openstreetmap.de/ticket/14287#comment:15
             // execute tests for objects which might contain or cross previously tested elements
@@ -870,15 +931,17 @@
 
             // rebuild index with a reduced set of rules (those that use ChildOrParentSelector) and thus may have left selectors
             // matching the previously tested elements
-            indexData = createMapCSSTagCheckerIndex(checks, includeOtherSeverityChecks(), ONLY_SELECTED_TESTS);
+            indexData = createMapCSSTagCheckerIndex(currentCheck, includeOtherSeverityChecks(), ONLY_SELECTED_TESTS);
 
-            Set<OsmPrimitive> surrounding = new HashSet<>();
-            for (OsmPrimitive p : tested) {
-                if (p.getDataSet() != null) {
-                    surrounding.addAll(p.getDataSet().searchWays(p.getBBox()));
-                    surrounding.addAll(p.getDataSet().searchRelations(p.getBBox()));
+            if (surrounding.isEmpty()) {
+                for (OsmPrimitive p : tested) {
+                    if (p.getDataSet() != null) {
+                        surrounding.addAll(p.getDataSet().searchWays(p.getBBox()));
+                        surrounding.addAll(p.getDataSet().searchRelations(p.getBBox()));
+                    }
                 }
             }
+
             final boolean includeOtherSeverity = includeOtherSeverityChecks();
             for (OsmPrimitive p : surrounding) {
                 if (tested.contains(p))
@@ -889,12 +952,16 @@
                         addIfNotSimilar(e, errors);
                 }
             }
-            tested.clear();
         }
-        // no need to keep the index, it is quickly build and doubles the memory needs
-        indexData = null;
-        // always clear the cache to make sure that we catch changes in geometry
-        mpAreaCache.clear();
-        super.endTest();
     }
+
+    private static String simplifySourceName(String source) {
+        if (source.endsWith(".mapcss")) // do we have others?
+            source = new File(source).getName();
+        if (source.length() > 33) {
+            source = "..." + source.substring(source.length() - 30);
+        }
+        return source;
+    }
+
 }
