|
37 | 37 | import java.time.Duration; |
38 | 38 | import java.util.ArrayList; |
39 | 39 | import java.util.List; |
40 | | -import java.util.Objects; |
41 | | -import java.util.concurrent.atomic.AtomicInteger; |
42 | 40 | import java.util.function.Function; |
43 | 41 | import java.util.logging.Level; |
44 | 42 |
|
@@ -225,56 +223,16 @@ public List<String> getRequestEntries(String partialUrl) { |
225 | 223 | public void waitForAjax() { |
226 | 224 | long ajaxTimeout = ConfigurationService.get(WebSettings.class).getTimeoutSettings().getWaitForAjaxTimeout(); |
227 | 225 | long sleepInterval = ConfigurationService.get(WebSettings.class).getTimeoutSettings().getSleepInterval(); |
228 | | - AtomicInteger ajaxConnections = new AtomicInteger(); |
229 | 226 | try { |
230 | | - Wait.retry(() -> { |
231 | | - var numberOfAjaxConnections = getJavascriptExecutor().executeScript("return !isNaN(window.$openHTTPs) ? window.$openHTTPs : null"); |
232 | | - if (Objects.nonNull(numberOfAjaxConnections)) { |
233 | | - ajaxConnections.set(Integer.parseInt(numberOfAjaxConnections.toString())); |
234 | | - if (ajaxConnections.get() > 0) { |
235 | | - String message = "Waiting for %s Ajax Connections...".formatted(ajaxConnections); |
236 | | - injectInfoNotificationToast(message); |
237 | | - Log.info(message); |
238 | | - throw new TimeoutException(message); |
239 | | - } |
240 | | - } else { |
241 | | - monkeyPatchXMLHttpRequest(); |
242 | | - } |
243 | | - }, |
244 | | - Duration.ofSeconds(ajaxTimeout), |
245 | | - Duration.ofSeconds(sleepInterval), |
246 | | - true, |
247 | | - TimeoutException.class); |
| 227 | + waitForXhrIdle(Duration.ofSeconds(ajaxTimeout), Duration.ofSeconds(sleepInterval)); |
248 | 228 | } |
249 | 229 | catch (Exception e) { |
250 | | - var message = "Timed out waiting for %s Ajax connections.".formatted(ajaxConnections.get()); |
| 230 | + var message = "Timed out waiting for Async requests. Continuing..."; |
251 | 231 | Log.error(message); |
252 | 232 | injectErrorNotificationToast(message); |
253 | 233 | } |
254 | 234 | } |
255 | 235 |
|
256 | | - private void monkeyPatchXMLHttpRequest() { |
257 | | - var numberOfAjaxConnections = getJavascriptExecutor().executeScript(("return !isNaN(window.$openHTTPs) ? window.$openHTTPs : null")); |
258 | | - |
259 | | - if (Objects.isNull(numberOfAjaxConnections)) { |
260 | | - var script = "(function() {" + |
261 | | - "const oldOpen = XMLHttpRequest.prototype.open;" + |
262 | | - "window.$openHTTPs = 0;" + |
263 | | - "XMLHttpRequest.prototype.open = function() {" + |
264 | | - "window.$openHTTPs++;" + |
265 | | - "this.addEventListener('readystatechange', function() {" + |
266 | | - "if(this.readyState == 4) {" + |
267 | | - "window.$openHTTPs--;" + |
268 | | - "}" + |
269 | | - "}, false);" + |
270 | | - "return oldOpen.call(this, ...arguments);" + |
271 | | - "}" + |
272 | | - "})();"; |
273 | | - |
274 | | - getJavascriptExecutor().executeScript(script); |
275 | | - } |
276 | | - } |
277 | | - |
278 | 236 | public void waitForAjaxRequest(String requestPartialUrl, int additionalTimeoutInSeconds) { |
279 | 237 | long ajaxTimeout = ConfigurationService.get(WebSettings.class).getTimeoutSettings().getWaitForAjaxTimeout(); |
280 | 238 | long sleepInterval = ConfigurationService.get(WebSettings.class).getTimeoutSettings().getSleepInterval(); |
@@ -559,4 +517,41 @@ public String getLastClipboardEntry() { |
559 | 517 | return ""; |
560 | 518 | } |
561 | 519 | } |
| 520 | + |
| 521 | + public void waitForXhrIdle(Duration idleGap, |
| 522 | + Duration timeout) { |
| 523 | + |
| 524 | + JavascriptExecutor js = this.getJavascriptExecutor(); |
| 525 | + long start = System.nanoTime(); |
| 526 | + long deadline = start + timeout.toNanos(); |
| 527 | + long gapMs = idleGap.toMillis(); |
| 528 | + |
| 529 | + while (System.nanoTime() < deadline) { |
| 530 | + try { |
| 531 | + Long active = (Long) js.executeScript(XHR_ACTIVE_JS, gapMs); |
| 532 | + if (active == 0) { |
| 533 | + logElapsed(start); |
| 534 | + return; |
| 535 | + } |
| 536 | + Thread.sleep(40); |
| 537 | + } catch (Exception e) { |
| 538 | + // ignore and retry until timeout |
| 539 | + } |
| 540 | + } |
| 541 | + logElapsed(start); |
| 542 | +// Log.error("Timed-out waiting for XHR idle"); |
| 543 | + } |
| 544 | + |
| 545 | + private void logElapsed(long startNano) { |
| 546 | + long ms = (System.nanoTime() - startNano) / 1_000_000; |
| 547 | + Log.info("[Browser Wait] Waited %d ms for pending XHRs %n", ms); |
| 548 | + } |
| 549 | + |
| 550 | + private final String XHR_ACTIVE_JS = |
| 551 | + "const now = performance.now();" + |
| 552 | + "return performance.getEntriesByType('resource')" + |
| 553 | + " .filter(e => e.initiatorType === 'xmlhttprequest' || e.initiatorType === 'fetch')" + |
| 554 | + " .filter(e => !e.responseEnd || e.responseEnd > now - arguments[0])" + // still running |
| 555 | + " .length;"; |
| 556 | + |
562 | 557 | } |
0 commit comments