21
21
import java .time .*;
22
22
import java .util .*;
23
23
import java .util .concurrent .*;
24
+ import java .util .concurrent .atomic .*;
24
25
import java .util .function .*;
25
26
26
27
import org .eclipse .swt .*;
@@ -42,6 +43,7 @@ class Edge extends WebBrowser {
42
43
static final String APPLOCAL_DIR_KEY = "org.eclipse.swt.internal.win32.appLocalDir" ;
43
44
static final String EDGE_USER_DATA_FOLDER = "org.eclipse.swt.internal.win32.Edge.userDataFolder" ;
44
45
static final String EDGE_USE_DARK_PREFERED_COLOR_SCHEME = "org.eclipse.swt.internal.win32.Edge.useDarkPreferedColorScheme" ; //$NON-NLS-1$
46
+ static final String WEB_VIEW_OPERATION_TIMEOUT = "org.eclipse.swt.internal.win32.Edge.timeout" ; //$NON-NLS-1$
45
47
46
48
// System.getProperty() keys
47
49
static final String BROWSER_DIR_PROP = "org.eclipse.swt.browser.EdgeDir" ;
@@ -58,6 +60,7 @@ class Edge extends WebBrowser {
58
60
private static final String ABOUT_BLANK = "about:blank" ;
59
61
60
62
private static final int MAXIMUM_CREATION_RETRIES = 5 ;
63
+ private static final Duration MAXIMUM_OPERATION_TIME = Duration .ofMillis (Integer .getInteger (WEB_VIEW_OPERATION_TIMEOUT , 5_000 ));
61
64
62
65
private record WebViewEnvironment (ICoreWebView2Environment environment , ArrayList <Edge > instances ) {
63
66
public WebViewEnvironment (ICoreWebView2Environment environment ) {
@@ -260,14 +263,12 @@ static int callAndWait(long[] ppv, ToIntFunction<IUnknown> callable) {
260
263
phr [0 ] = callable .applyAsInt (completion );
261
264
// "completion" callback may be called asynchronously,
262
265
// so keep processing next OS message that may call it
263
- while (phr [0 ] == COM .S_OK && ppv [0 ] == 0 ) {
264
- processNextOSMessage ();
265
- }
266
+ processOSMessagesUntil (() -> phr [0 ] != COM .S_OK || ppv [0 ] != 0 , Display .getCurrent ());
266
267
completion .Release ();
267
268
return phr [0 ];
268
269
}
269
270
270
- static int callAndWait (String [] pstr , ToIntFunction <IUnknown > callable ) {
271
+ int callAndWait (String [] pstr , ToIntFunction <IUnknown > callable ) {
271
272
int [] phr = new int [1 ];
272
273
IUnknown completion = newCallback ((result , pszJson ) -> {
273
274
phr [0 ] = (int )result ;
@@ -280,9 +281,7 @@ static int callAndWait(String[] pstr, ToIntFunction<IUnknown> callable) {
280
281
phr [0 ] = callable .applyAsInt (completion );
281
282
// "completion" callback may be called asynchronously,
282
283
// so keep processing next OS message that may call it
283
- while (phr [0 ] == COM .S_OK && pstr [0 ] == null ) {
284
- processNextOSMessage ();
285
- }
284
+ processOSMessagesUntil (() -> phr [0 ] != COM .S_OK || pstr [0 ] != null , browser .getDisplay ());
286
285
completion .Release ();
287
286
return phr [0 ];
288
287
}
@@ -298,7 +297,7 @@ class WebViewWrapper {
298
297
299
298
class WebViewProvider {
300
299
301
- private CompletableFuture <WebViewWrapper > webViewWrapperFuture = new CompletableFuture <>();
300
+ private CompletableFuture <WebViewWrapper > webViewWrapperFuture = wakeDisplayAfterFuture ( new CompletableFuture <>() );
302
301
private CompletableFuture <Void > lastWebViewTask = webViewWrapperFuture .thenRun (() -> {});;
303
302
304
303
ICoreWebView2 initializeWebView (ICoreWebView2Controller controller ) {
@@ -365,95 +364,91 @@ private ICoreWebView2_13 initializeWebView_13(ICoreWebView2 webView) {
365
364
return null ;
366
365
}
367
366
368
- ICoreWebView2 getWebView (boolean waitForPendingWebviewTasksToFinish ) {
367
+ private WebViewWrapper getWebViewWrapper (boolean waitForPendingWebviewTasksToFinish ) {
369
368
if (waitForPendingWebviewTasksToFinish ) {
370
- waitForFutureToFinish (lastWebViewTask );
369
+ processOSMessagesUntil (lastWebViewTask :: isDone , browser . getDisplay () );
371
370
}
372
- return webViewWrapperFuture .join ().webView ;
371
+ return webViewWrapperFuture .join ();
372
+ }
373
+
374
+ private WebViewWrapper getWebViewWrapper () {
375
+ processOSMessagesUntil (webViewWrapperFuture ::isDone , browser .getDisplay ());
376
+ return webViewWrapperFuture .join ();
377
+ }
378
+
379
+ ICoreWebView2 getWebView (boolean waitForPendingWebviewTasksToFinish ) {
380
+ return getWebViewWrapper (waitForPendingWebviewTasksToFinish ).webView ;
373
381
}
374
382
375
383
ICoreWebView2_2 getWebView_2 (boolean waitForPendingWebviewTasksToFinish ) {
376
- if (waitForPendingWebviewTasksToFinish ) {
377
- waitForFutureToFinish (lastWebViewTask );
378
- }
379
- return webViewWrapperFuture .join ().webView_2 ;
384
+ return getWebViewWrapper (waitForPendingWebviewTasksToFinish ).webView_2 ;
380
385
}
381
386
382
387
boolean isWebView_2Available () {
383
- waitForFutureToFinish (webViewWrapperFuture );
384
- return webViewWrapperFuture .join ().webView_2 != null ;
388
+ return getWebViewWrapper ().webView_2 != null ;
385
389
}
386
390
387
391
ICoreWebView2_10 getWebView_10 (boolean waitForPendingWebviewTasksToFinish ) {
388
- if (waitForPendingWebviewTasksToFinish ) {
389
- waitForFutureToFinish (lastWebViewTask );
390
- }
391
- return webViewWrapperFuture .join ().webView_10 ;
392
+ return getWebViewWrapper (waitForPendingWebviewTasksToFinish ).webView_10 ;
392
393
}
393
394
394
395
boolean isWebView_10Available () {
395
- waitForFutureToFinish (webViewWrapperFuture );
396
- return webViewWrapperFuture .join ().webView_10 != null ;
396
+ return getWebViewWrapper ().webView_10 != null ;
397
397
}
398
398
399
399
ICoreWebView2_11 getWebView_11 (boolean waitForPendingWebviewTasksToFinish ) {
400
- if (waitForPendingWebviewTasksToFinish ) {
401
- waitForFutureToFinish (lastWebViewTask );
402
- }
403
- return webViewWrapperFuture .join ().webView_11 ;
400
+ return getWebViewWrapper (waitForPendingWebviewTasksToFinish ).webView_11 ;
404
401
}
405
402
406
403
boolean isWebView_11Available () {
407
- waitForFutureToFinish (webViewWrapperFuture );
408
- return webViewWrapperFuture .join ().webView_11 != null ;
404
+ return getWebViewWrapper ().webView_11 != null ;
409
405
}
410
406
411
407
ICoreWebView2_12 getWebView_12 (boolean waitForPendingWebviewTasksToFinish ) {
412
- if (waitForPendingWebviewTasksToFinish ) {
413
- waitForFutureToFinish (lastWebViewTask );
414
- }
415
- return webViewWrapperFuture .join ().webView_12 ;
408
+ return getWebViewWrapper (waitForPendingWebviewTasksToFinish ).webView_12 ;
416
409
}
417
410
418
411
boolean isWebView_12Available () {
419
- waitForFutureToFinish (webViewWrapperFuture );
420
- return webViewWrapperFuture .join ().webView_12 != null ;
412
+ return getWebViewWrapper ().webView_12 != null ;
421
413
}
422
414
423
415
ICoreWebView2_13 getWebView_13 (boolean waitForPendingWebviewTasksToFinish ) {
424
- if (waitForPendingWebviewTasksToFinish ) {
425
- waitForFutureToFinish (lastWebViewTask );
426
- }
427
- return webViewWrapperFuture .join ().webView_13 ;
416
+ return getWebViewWrapper (waitForPendingWebviewTasksToFinish ).webView_13 ;
428
417
}
429
418
430
419
boolean isWebView_13Available () {
431
- waitForFutureToFinish (webViewWrapperFuture );
432
- return webViewWrapperFuture .join ().webView_13 != null ;
420
+ return getWebViewWrapper ().webView_13 != null ;
433
421
}
434
422
435
423
/*
436
424
* Schedule a given runnable in a queue to execute when the webView is free and
437
425
* has finished all the pending tasks queued before it.
438
426
*/
439
427
void scheduleWebViewTask (Runnable action ) {
440
- lastWebViewTask = lastWebViewTask .thenRun (() -> {
441
- action .run ();
428
+ lastWebViewTask = wakeDisplayAfterFuture (lastWebViewTask .thenRun (action ::run ));
429
+ }
430
+
431
+ private <T > CompletableFuture <T > wakeDisplayAfterFuture (CompletableFuture <T > future ) {
432
+ return future .handle ((nil1 , nil2 ) -> {
433
+ Display display = browser .getDisplay ();
434
+ if (!display .isDisposed ()) {
435
+ try {
436
+ display .wake ();
437
+ } catch (SWTException e ) {
438
+ // ignore then, this can happen due to the async nature between our check for
439
+ // disposed and the actual call to wake the display can be disposed
440
+ }
441
+ }
442
+ return null ;
442
443
});
443
444
}
444
-
445
- private <T > void waitForFutureToFinish (CompletableFuture <T > future ) {
446
- while (!future .isDone ()) {
447
- processNextOSMessage ();
448
- }
449
- }
450
-
451
445
}
452
446
453
447
/**
454
- * Processes a single OS message using {@link Display#readAndDispatch()}. This
448
+ * Processes single OS messages using {@link Display#readAndDispatch()}. This
455
449
* is required for processing the OS events during browser initialization, since
456
- * Edge browser initialization happens asynchronously.
450
+ * Edge browser initialization happens asynchronously. Messages are processed
451
+ * until the given condition is fulfilled or a timeout occurs.
457
452
* <p>
458
453
* {@link Display#readAndDispatch()} also processes events scheduled for
459
454
* asynchronous execution via {@link Display#asyncExec(Runnable)}. This may
@@ -462,13 +457,21 @@ private <T> void waitForFutureToFinish(CompletableFuture<T> future) {
462
457
* events for initialization. Thus, this method does not implement an ordinary
463
458
* readAndDispatch loop, but waits for an OS event to be processed.
464
459
*/
465
- private static void processNextOSMessage () {
466
- Display display = Display .getCurrent ();
460
+ private static void processOSMessagesUntil (Supplier <Boolean > condition , Display display ) {
467
461
MSG msg = new MSG ();
468
- while (!OS .PeekMessage (msg , 0 , 0 , 0 , OS .PM_NOREMOVE )) {
469
- display .sleep ();
462
+ AtomicBoolean timeoutOccurred = new AtomicBoolean ();
463
+ // The timer call also wakes up the display to avoid being stuck in display.sleep()
464
+ display .timerExec ((int ) MAXIMUM_OPERATION_TIME .toMillis (), () -> timeoutOccurred .set (true ));
465
+ while (!display .isDisposed () && !condition .get () && !timeoutOccurred .get ()) {
466
+ if (OS .PeekMessage (msg , 0 , 0 , 0 , OS .PM_NOREMOVE )) {
467
+ display .readAndDispatch ();
468
+ } else {
469
+ display .sleep ();
470
+ }
471
+ }
472
+ if (!condition .get ()) {
473
+ SWT .error (SWT .ERROR_UNSPECIFIED , null , " Waiting for Edge operation to terminate timed out" );
470
474
}
471
- display .readAndDispatch ();
472
475
}
473
476
474
477
static ICoreWebView2CookieManager getCookieManager () {
0 commit comments