2121import java .time .*;
2222import java .util .*;
2323import java .util .concurrent .*;
24+ import java .util .concurrent .atomic .*;
2425import java .util .function .*;
2526
2627import org .eclipse .swt .*;
@@ -42,6 +43,7 @@ class Edge extends WebBrowser {
4243 static final String APPLOCAL_DIR_KEY = "org.eclipse.swt.internal.win32.appLocalDir" ;
4344 static final String EDGE_USER_DATA_FOLDER = "org.eclipse.swt.internal.win32.Edge.userDataFolder" ;
4445 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$
4547
4648 // System.getProperty() keys
4749 static final String BROWSER_DIR_PROP = "org.eclipse.swt.browser.EdgeDir" ;
@@ -58,6 +60,7 @@ class Edge extends WebBrowser {
5860 private static final String ABOUT_BLANK = "about:blank" ;
5961
6062 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 ));
6164
6265 private record WebViewEnvironment (ICoreWebView2Environment environment , ArrayList <Edge > instances ) {
6366 public WebViewEnvironment (ICoreWebView2Environment environment ) {
@@ -260,14 +263,12 @@ static int callAndWait(long[] ppv, ToIntFunction<IUnknown> callable) {
260263 phr [0 ] = callable .applyAsInt (completion );
261264 // "completion" callback may be called asynchronously,
262265 // 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 ());
266267 completion .Release ();
267268 return phr [0 ];
268269}
269270
270- static int callAndWait (String [] pstr , ToIntFunction <IUnknown > callable ) {
271+ int callAndWait (String [] pstr , ToIntFunction <IUnknown > callable ) {
271272 int [] phr = new int [1 ];
272273 IUnknown completion = newCallback ((result , pszJson ) -> {
273274 phr [0 ] = (int )result ;
@@ -280,9 +281,7 @@ static int callAndWait(String[] pstr, ToIntFunction<IUnknown> callable) {
280281 phr [0 ] = callable .applyAsInt (completion );
281282 // "completion" callback may be called asynchronously,
282283 // 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 ());
286285 completion .Release ();
287286 return phr [0 ];
288287}
@@ -298,7 +297,7 @@ class WebViewWrapper {
298297
299298class WebViewProvider {
300299
301- private CompletableFuture <WebViewWrapper > webViewWrapperFuture = new CompletableFuture <>();
300+ private CompletableFuture <WebViewWrapper > webViewWrapperFuture = wakeDisplayAfterFuture ( new CompletableFuture <>() );
302301 private CompletableFuture <Void > lastWebViewTask = webViewWrapperFuture .thenRun (() -> {});;
303302
304303 ICoreWebView2 initializeWebView (ICoreWebView2Controller controller ) {
@@ -365,95 +364,91 @@ private ICoreWebView2_13 initializeWebView_13(ICoreWebView2 webView) {
365364 return null ;
366365 }
367366
368- ICoreWebView2 getWebView (boolean waitForPendingWebviewTasksToFinish ) {
367+ private WebViewWrapper getWebViewWrapper (boolean waitForPendingWebviewTasksToFinish ) {
369368 if (waitForPendingWebviewTasksToFinish ) {
370- waitForFutureToFinish (lastWebViewTask );
369+ processOSMessagesUntil (lastWebViewTask :: isDone , browser . getDisplay () );
371370 }
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 ;
373381 }
374382
375383 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 ;
380385 }
381386
382387 boolean isWebView_2Available () {
383- waitForFutureToFinish (webViewWrapperFuture );
384- return webViewWrapperFuture .join ().webView_2 != null ;
388+ return getWebViewWrapper ().webView_2 != null ;
385389 }
386390
387391 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 ;
392393 }
393394
394395 boolean isWebView_10Available () {
395- waitForFutureToFinish (webViewWrapperFuture );
396- return webViewWrapperFuture .join ().webView_10 != null ;
396+ return getWebViewWrapper ().webView_10 != null ;
397397 }
398398
399399 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 ;
404401 }
405402
406403 boolean isWebView_11Available () {
407- waitForFutureToFinish (webViewWrapperFuture );
408- return webViewWrapperFuture .join ().webView_11 != null ;
404+ return getWebViewWrapper ().webView_11 != null ;
409405 }
410406
411407 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 ;
416409 }
417410
418411 boolean isWebView_12Available () {
419- waitForFutureToFinish (webViewWrapperFuture );
420- return webViewWrapperFuture .join ().webView_12 != null ;
412+ return getWebViewWrapper ().webView_12 != null ;
421413 }
422414
423415 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 ;
428417 }
429418
430419 boolean isWebView_13Available () {
431- waitForFutureToFinish (webViewWrapperFuture );
432- return webViewWrapperFuture .join ().webView_13 != null ;
420+ return getWebViewWrapper ().webView_13 != null ;
433421 }
434422
435423 /*
436424 * Schedule a given runnable in a queue to execute when the webView is free and
437425 * has finished all the pending tasks queued before it.
438426 */
439427 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 ;
442443 });
443444 }
444-
445- private <T > void waitForFutureToFinish (CompletableFuture <T > future ) {
446- while (!future .isDone ()) {
447- processNextOSMessage ();
448- }
449- }
450-
451445}
452446
453447/**
454- * Processes a single OS message using {@link Display#readAndDispatch()}. This
448+ * Processes single OS messages using {@link Display#readAndDispatch()}. This
455449 * 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.
457452 * <p>
458453 * {@link Display#readAndDispatch()} also processes events scheduled for
459454 * asynchronous execution via {@link Display#asyncExec(Runnable)}. This may
@@ -462,13 +457,21 @@ private <T> void waitForFutureToFinish(CompletableFuture<T> future) {
462457 * events for initialization. Thus, this method does not implement an ordinary
463458 * readAndDispatch loop, but waits for an OS event to be processed.
464459 */
465- private static void processNextOSMessage () {
466- Display display = Display .getCurrent ();
460+ private static void processOSMessagesUntil (Supplier <Boolean > condition , Display display ) {
467461 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" );
470474 }
471- display .readAndDispatch ();
472475}
473476
474477static ICoreWebView2CookieManager getCookieManager () {
0 commit comments