Add waitAfterSetup delay

This allows to add an artificial single delay after
the setup ad before starting the workload steps.

Change-Id: I8bce550d76707f9816bb63d7cfdad93c76f02b4f
diff --git a/resources/benchmark-runner.mjs b/resources/benchmark-runner.mjs
index 2c5adbf..cdaaced 100644
--- a/resources/benchmark-runner.mjs
+++ b/resources/benchmark-runner.mjs
@@ -559,10 +559,18 @@
             this._recordPrepareMetric(suite, prepareTime);
         }
 
+        if (params.waitAfterSetup > 0) {
+            await new Promise((resolve) => setTimeout(resolve, params.waitAfterSetup));
+        }
+
         this.markStart(suiteLabel, suiteStartLabel);
         for (const test of suite.tests)
             await this._runTestAndRecordResults(suite, test);
         this.markEnd(suiteLabel, suiteStartLabel, suiteEndLabel);
+
+        if (params.waitAfterSuite > 0) {
+            await new Promise((resolve) => setTimeout(resolve, params.waitAfterSuite));
+        }
     }
 
     _prepareMetrics(suite) {
diff --git a/resources/developer-mode.mjs b/resources/developer-mode.mjs
index 24a80c3..24c8561 100644
--- a/resources/developer-mode.mjs
+++ b/resources/developer-mode.mjs
@@ -21,7 +21,9 @@
     settings.append(createUIForMeasurementMethod());
     settings.append(createUIForWarmupSuite());
     settings.append(createUIForWarmupBeforeSync());
+    settings.append(createUIForWaitAfterSetup());
     settings.append(createUIForSyncStepDelay());
+    settings.append(createUIForWaitAfterSuite());
     settings.append(createUIForLeakyIframes());
     settings.append(createUIForMeasurePrepare());
     settings.append(createUIForDomainPerIteration());
@@ -172,6 +174,24 @@
     return label;
 }
 
+function createUIForWaitAfterSetup() {
+    const { range, label } = createTimeRangeUI("Post setup delay: ", params.waitAfterSetup);
+    range.onchange = () => {
+        params.waitAfterSetup = parseInt(range.value);
+        updateURL();
+    };
+    return label;
+}
+
+function createUIForWaitAfterSuite() {
+    const { range, label } = createTimeRangeUI("Post suite delay: ", params.waitAfterSuite);
+    range.onchange = () => {
+        params.waitAfterSuite = parseInt(range.value);
+        updateURL();
+    };
+    return label;
+}
+
 function createTimeRangeUI(labelText, initialValue, unit = "ms", min = 0, max = 1000) {
     const range = document.createElement("input");
     range.type = "range";
@@ -344,7 +364,7 @@
     else
         url.searchParams.delete("measurementMethod");
 
-    const boolParamKeys = ["iterationCount", "useWarmupSuite", "warmupBeforeSync", "waitBeforeSync", "domainPerIteration", "suiteForceGC", "leakyIframes"];
+    const boolParamKeys = ["iterationCount", "useWarmupSuite", "warmupBeforeSync", "waitAfterSetup", "waitBeforeSync", "waitAfterSuite", "domainPerIteration", "suiteForceGC", "leakyIframes"];
     for (const paramKey of boolParamKeys) {
         if (params[paramKey] !== defaultParams[paramKey])
             url.searchParams.set(paramKey, params[paramKey]);
diff --git a/resources/params.mjs b/resources/params.mjs
index 726b2af..9429beb 100644
--- a/resources/params.mjs
+++ b/resources/params.mjs
@@ -16,8 +16,12 @@
     // "timer": The classic (as in Speedometer 2.x) way using setTimeout
     // "raf":   Using rAF callbacks, both for triggering the sync part and for measuring async time.
     measurementMethod = "raf"; // or "timer"
+    // Wait time after suite preparation in ms.
+    waitAfterSetup = 0;
     // Wait time before the sync step in ms.
     waitBeforeSync = 0;
+    // Wait after running a suite.
+    waitAfterSuite = 0;
     // Warmup time before the sync step in ms.
     warmupBeforeSync = 0;
     // Seed for shuffling the execution order of suites.
@@ -116,6 +120,13 @@
             searchParams.delete("useWarmupSuite");
         }
 
+        if (searchParams.has("waitAfterSetup")) {
+            this.waitAfterSetup = this._parseInt(searchParams.get("waitAfterSetup"), "waitAfterSetup");
+            if (this.waitAfterSetup < 0)
+                throw new Error(`Invalid waitAfterSetup param: '${this.waitAfterSetup}', must be >= 0.`);
+            searchParams.delete("waitAfterSetup");
+        }
+
         if (searchParams.has("waitBeforeSync")) {
             this.waitBeforeSync = this._parseInt(searchParams.get("waitBeforeSync"), "waitBeforeSync");
             if (this.waitBeforeSync < 0)
@@ -123,6 +134,13 @@
             searchParams.delete("waitBeforeSync");
         }
 
+        if (searchParams.has("waitAfterSuite")) {
+            this.waitAfterSuite = this._parseInt(searchParams.get("waitAfterSuite"), "waitAfterSuite");
+            if (this.waitAfterSuite < 0)
+                throw new Error(`Invalid waitAfterSuite param: '${this.waitAfterSuite}', must be >= 0.`);
+            searchParams.delete("waitAfterSuite");
+        }
+
         if (searchParams.has("warmupBeforeSync")) {
             this.warmupBeforeSync = this._parseInt(searchParams.get("warmupBeforeSync"), "warmupBeforeSync");
             if (this.warmupBeforeSync < 0)