@@ -294,12 +294,33 @@ class WebViewWrapper {
294294 private ICoreWebView2_11 webView_11 ;
295295 private ICoreWebView2_12 webView_12 ;
296296 private ICoreWebView2_13 webView_13 ;
297+
298+ void releaseWebViews () {
299+ if (webView != null ) {
300+ webView .Release ();
301+ }
302+ if (webView_2 != null ) {
303+ webView_2 .Release ();
304+ }
305+ if (webView_10 != null ) {
306+ webView_10 .Release ();
307+ }
308+ if (webView_11 != null ) {
309+ webView_11 .Release ();
310+ }
311+ if (webView_12 != null ) {
312+ webView_12 .Release ();
313+ }
314+ if (webView_13 != null ) {
315+ webView_13 .Release ();
316+ }
317+ }
297318}
298319
299320class WebViewProvider {
300321
301- private CompletableFuture <WebViewWrapper > webViewWrapperFuture = new CompletableFuture <>() ;
302- private CompletableFuture <Void > lastWebViewTask = webViewWrapperFuture . thenRun (() -> {}); ;
322+ private CompletableFuture <WebViewWrapper > webViewWrapperFuture ;
323+ private CompletableFuture <Void > lastWebViewTask ;
303324
304325 ICoreWebView2 initializeWebView (ICoreWebView2Controller controller ) {
305326 long [] ppv = new long [1 ];
@@ -312,14 +333,34 @@ ICoreWebView2 initializeWebView(ICoreWebView2Controller controller) {
312333 webViewWrapper .webView_11 = initializeWebView_11 (webView );
313334 webViewWrapper .webView_12 = initializeWebView_12 (webView );
314335 webViewWrapper .webView_13 = initializeWebView_13 (webView );
315- webViewWrapperFuture .complete (webViewWrapper );
336+ boolean success = webViewWrapperFuture .complete (webViewWrapper );
337+ // Release the webViews if the webViewWrapperFuture has already timed out and completed exceptionally
338+ if (!success && webViewWrapperFuture .isCompletedExceptionally ()) {
339+ webViewWrapper .releaseWebViews ();
340+ return null ;
341+ }
316342 return webView ;
317343 }
318344
345+ void initializeWebViewFutureWithTimeOut () {
346+ if (webViewWrapperFuture != null ) {
347+ return ;
348+ }
349+ webViewWrapperFuture = new CompletableFuture <>();
350+ webViewWrapperFuture .orTimeout (3 , TimeUnit .SECONDS ).exceptionallyAsync (exception -> {
351+ // Needs to be executed on the display thread since the exceptionally spawns a different thread
352+ replaceWithErrorLabel ();
353+ // Throw exception on the Display thread directly to prevent CompletableFuture
354+ // to wrap the exception and throw it silently
355+ browser .getDisplay ().execute (() -> errorTimedOut (exception ));
356+ return null ;
357+ }, browser .getDisplay ());
358+ lastWebViewTask = webViewWrapperFuture .thenRun (() -> {});
359+ }
360+
319361 private void abortInitialization () {
320362 webViewWrapperFuture .cancel (true );
321363 }
322-
323364 private ICoreWebView2_2 initializeWebView_2 (ICoreWebView2 webView ) {
324365 long [] ppv = new long [1 ];
325366 int hr = webView .QueryInterface (COM .IID_ICoreWebView2_2 , ppv );
@@ -365,6 +406,11 @@ private ICoreWebView2_13 initializeWebView_13(ICoreWebView2 webView) {
365406 return null ;
366407 }
367408
409+ void releaseWebView () {
410+ waitForFutureToFinish (webViewWrapperFuture );
411+ webViewWrapperFuture .join ().releaseWebViews ();
412+ }
413+
368414 ICoreWebView2 getWebView (boolean waitForPendingWebviewTasksToFinish ) {
369415 if (waitForPendingWebviewTasksToFinish ) {
370416 waitForFutureToFinish (lastWebViewTask );
@@ -500,6 +546,10 @@ void checkDeadlock() {
500546 }
501547}
502548
549+ void errorTimedOut (Throwable throwable ) {
550+ SWT .error (SWT .ERROR_UNSPECIFIED , throwable , "Edge Browser initialization timed out" );
551+ }
552+
503553WebViewEnvironment createEnvironment () {
504554 Display display = Display .getCurrent ();
505555 WebViewEnvironment existingEnvironment = webViewEnvironments .get (display );
@@ -571,8 +621,18 @@ private String getDataDir(Display display) {
571621 return dataDir ;
572622}
573623
624+ private void replaceWithErrorLabel () {
625+ Label errorLabel = new Label (browser .getParent (), SWT .WRAP );
626+ errorLabel .setForeground (browser .getDisplay ().getSystemColor (SWT .COLOR_RED ));
627+ errorLabel .setText ("Edge browser initialization failed" );
628+ errorLabel .setLocation (0 , 0 );
629+ errorLabel .setSize (browser .getSize ());
630+ browser .setVisible (false );
631+ }
632+
574633@ Override
575634public void create (Composite parent , int style ) {
635+ webViewProvider .initializeWebViewFutureWithTimeOut ();
576636 createInstance (0 );
577637}
578638
@@ -587,38 +647,34 @@ private void createInstance(int previousAttempts) {
587647}
588648
589649private IUnknown createControllerInitializationCallback (int previousAttempts ) {
590- Runnable initializationRollback = () -> {
591- webViewProvider .abortInitialization ();
592- if (environment2 != null ) {
593- environment2 .Release ();
594- environment2 = null ;
595- }
596- containingEnvironment .instances ().remove (this );
597- };
598650 return newCallback ((result , pv ) -> {
599651 if (browser .isDisposed ()) {
600- initializationRollback . run ();
652+ rollbackInitialization ();
601653 return COM .S_OK ;
602654 }
603655 if (result == OS .HRESULT_FROM_WIN32 (OS .ERROR_INVALID_STATE )) {
604- initializationRollback . run ();
656+ rollbackInitialization ();
605657 SWT .error (SWT .ERROR_INVALID_ARGUMENT , null ,
606- " Edge instance with same data folder but different environment options already exists " );
658+ "Edge Initialization Timed Out " );
607659 }
608660 switch ((int ) result ) {
609661 case COM .S_OK :
610662 new IUnknown (pv ).AddRef ();
611- setupBrowser ((int ) result , pv );
663+ boolean success = setupBrowser ((int ) result , pv );
664+ if (!success ) {
665+ rollbackInitialization ();
666+ errorTimedOut (null );
667+ }
612668 break ;
613669 case COM .E_WRONG_THREAD :
614- initializationRollback . run ();
670+ rollbackInitialization ();
615671 error (SWT .ERROR_THREAD_INVALID_ACCESS , (int ) result );
616672 break ;
617673 case COM .E_ABORT :
618- initializationRollback . run ();
674+ rollbackInitialization ();
619675 break ;
620676 default :
621- initializationRollback . run ();
677+ rollbackInitialization ();
622678 if (previousAttempts < MAXIMUM_CREATION_RETRIES ) {
623679 System .err .println (String .format ("Edge initialization failed, retrying (attempt %d / %d)" , previousAttempts + 1 , MAXIMUM_CREATION_RETRIES ));
624680 createInstance (previousAttempts + 1 );
@@ -632,10 +688,22 @@ private IUnknown createControllerInitializationCallback(int previousAttempts) {
632688 });
633689}
634690
635- void setupBrowser (int hr , long pv ) {
691+ private void rollbackInitialization () {
692+ webViewProvider .abortInitialization ();
693+ if (environment2 != null ) {
694+ environment2 .Release ();
695+ environment2 = null ;
696+ }
697+ containingEnvironment .instances ().remove (this );
698+ }
699+
700+ boolean setupBrowser (int hr , long pv ) {
636701 long [] ppv = new long [] {pv };
637702 controller = new ICoreWebView2Controller (ppv [0 ]);
638703 final ICoreWebView2 webView = webViewProvider .initializeWebView (controller );
704+ if (webView == null ) {
705+ return false ;
706+ }
639707 webView .get_Settings (ppv );
640708 settings = new ICoreWebView2Settings (ppv [0 ]);
641709
@@ -735,17 +803,15 @@ void setupBrowser(int hr, long pv) {
735803 if (browser .isFocusControl ()) {
736804 browserFocusIn (new Event ());
737805 }
806+ return true ;
738807}
739808
740809void browserDispose (Event event ) {
741810 containingEnvironment .instances .remove (this );
742811 webViewProvider .scheduleWebViewTask (() -> {
743- webViewProvider .getWebView ( false ). Release ();
812+ webViewProvider .releaseWebView ();
744813 if (environment2 != null ) environment2 .Release ();
745814 if (settings != null ) settings .Release ();
746- if (webViewProvider .isWebView_2Available ()) webViewProvider .getWebView_2 (false ).Release ();
747- if (webViewProvider .isWebView_11Available ()) webViewProvider .getWebView_11 (false ).Release ();
748- if (webViewProvider .isWebView_12Available ()) webViewProvider .getWebView_12 (false ).Release ();
749815 if (controller != null ) {
750816 // Bug in WebView2. Closing the controller from an event handler results
751817 // in a crash. The fix is to delay the closure with asyncExec.
0 commit comments