Skip to content

Commit 565580b

Browse files
committed
[Win32] Spin event loop in Edge instead of only processing OS messages
Before recent improvements of the Edge implementation, capabilities of to deal with asynchronous disposals of the browser were missing, leading to unhandled errors inside the WebView initialization. They have been worked around by not processing all kinds of asynchronously scheduled events (like a disposal) but only those being processes by the OS event queue. This was still necessary to process the OS callbacks for WebView initialization and other operations. In some cases, this may lead to an Edge browser instance blocking the UI thread, as some asynchronously scheduled tasks need to be processed but are not. In addition, enhancements of the Edge implementation made it capable of processing asynchronous browser display disposals. This change simplifies the event processing inside Edge to not just process the next OS message but to just ordinarily spin the event loop.
1 parent 56767ec commit 565580b

File tree

2 files changed

+24
-33
lines changed

2 files changed

+24
-33
lines changed

bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java

+15-30
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ static int callAndWait(long[] ppv, ToIntFunction<IUnknown> callable) {
261261
// "completion" callback may be called asynchronously,
262262
// so keep processing next OS message that may call it
263263
while (phr[0] == COM.S_OK && ppv[0] == 0) {
264-
processNextOSMessage();
264+
spinEventLoop();
265265
}
266266
completion.Release();
267267
return phr[0];
@@ -281,7 +281,7 @@ static int callAndWait(String[] pstr, ToIntFunction<IUnknown> callable) {
281281
// "completion" callback may be called asynchronously,
282282
// so keep processing next OS message that may call it
283283
while (phr[0] == COM.S_OK && pstr[0] == null) {
284-
processNextOSMessage();
284+
spinEventLoop();
285285
}
286286
completion.Release();
287287
return phr[0];
@@ -311,8 +311,8 @@ ICoreWebView2 initializeWebView(ICoreWebView2Controller controller) {
311311
return webView;
312312
}
313313

314-
private void abortInitialization() {
315-
webViewFuture.cancel(true);
314+
private void abortInitialization(String abortMessage) {
315+
webViewFuture.completeExceptionally(new SWTError(abortMessage));
316316
}
317317

318318
private void initializeWebView_2(ICoreWebView2 webView) {
@@ -444,31 +444,17 @@ void scheduleWebViewTask(Runnable action) {
444444

445445
private <T> void waitForFutureToFinish(CompletableFuture<T> future) {
446446
while(!future.isDone()) {
447-
processNextOSMessage();
447+
spinEventLoop();
448448
}
449449
}
450450

451451
}
452452

453-
/**
454-
* Processes a single OS message using {@link Display#readAndDispatch()}. This
455-
* is required for processing the OS events during browser initialization, since
456-
* Edge browser initialization happens asynchronously.
457-
* <p>
458-
* {@link Display#readAndDispatch()} also processes events scheduled for
459-
* asynchronous execution via {@link Display#asyncExec(Runnable)}. This may
460-
* include events such as the disposal of the browser's parent composite, which
461-
* leads to a failure in browser initialization if processed in between the OS
462-
* events for initialization. Thus, this method does not implement an ordinary
463-
* readAndDispatch loop, but waits for an OS event to be processed.
464-
*/
465-
private static void processNextOSMessage() {
453+
private static void spinEventLoop() {
466454
Display display = Display.getCurrent();
467-
MSG msg = new MSG();
468-
while (!OS.PeekMessage (msg, 0, 0, 0, OS.PM_NOREMOVE)) {
455+
if (!display.readAndDispatch()) {
469456
display.sleep();
470457
}
471-
display.readAndDispatch();
472458
}
473459

474460
static ICoreWebView2CookieManager getCookieManager() {
@@ -587,8 +573,8 @@ private void createInstance(int previousAttempts) {
587573
}
588574

589575
private IUnknown createControllerInitializationCallback(int previousAttempts) {
590-
Runnable initializationRollback = () -> {
591-
webViewProvider.abortInitialization();
576+
Consumer<String> initializationRollback = abortMessage -> {
577+
webViewProvider.abortInitialization(abortMessage);
592578
if (environment2 != null) {
593579
environment2.Release();
594580
environment2 = null;
@@ -597,28 +583,27 @@ private IUnknown createControllerInitializationCallback(int previousAttempts) {
597583
};
598584
return newCallback((result, pv) -> {
599585
if (browser.isDisposed()) {
600-
initializationRollback.run();
586+
initializationRollback.accept("Edge initialization was aborted");
601587
return COM.S_OK;
602588
}
603589
if (result == OS.HRESULT_FROM_WIN32(OS.ERROR_INVALID_STATE)) {
604-
initializationRollback.run();
605-
SWT.error(SWT.ERROR_INVALID_ARGUMENT, null,
606-
" Edge instance with same data folder but different environment options already exists");
590+
initializationRollback.accept("Edge instance with same data folder but different environment options already exists");
591+
return COM.S_OK;
607592
}
608593
switch ((int) result) {
609594
case COM.S_OK:
610595
new IUnknown(pv).AddRef();
611596
setupBrowser((int) result, pv);
612597
break;
613598
case COM.E_WRONG_THREAD:
614-
initializationRollback.run();
599+
initializationRollback.accept("Wrong thread access");
615600
error(SWT.ERROR_THREAD_INVALID_ACCESS, (int) result);
616601
break;
617602
case COM.E_ABORT:
618-
initializationRollback.run();
603+
initializationRollback.accept("Edge initialization was aborted");
619604
break;
620605
default:
621-
initializationRollback.run();
606+
initializationRollback.accept("Edge initialization failed, retrying");
622607
if (previousAttempts < MAXIMUM_CREATION_RETRIES) {
623608
System.err.println(String.format("Edge initialization failed, retrying (attempt %d / %d)", previousAttempts + 1, MAXIMUM_CREATION_RETRIES));
624609
createInstance(previousAttempts + 1);

tests/org.eclipse.swt.tests/JUnit Tests/org/eclipse/swt/tests/junit/Test_org_eclipse_swt_browser_Browser.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -302,11 +302,17 @@ private int reportOpenedDescriptors() {
302302
}
303303

304304
private Browser createBrowser(Shell s, int flags) {
305-
long maximumBrowserCreationMilliseconds = 90_000;
305+
return createBrowser(s, flags, true);
306+
}
307+
308+
private Browser createBrowser(Shell s, int flags, boolean expectSuccess) {
309+
long maximumBrowserCreationMilliseconds = 5_000;
306310
long createStartTime = System.currentTimeMillis();
307311
Browser b = new Browser(s, flags);
308312
// Wait for asynchronous initialization via getting URL
309-
b.getUrl();
313+
if (expectSuccess) {
314+
b.getUrl();
315+
}
310316
createdBroswers.add(b);
311317
long createDuration = System.currentTimeMillis() - createStartTime;
312318
assertTrue("creating browser took too long: " + createDuration + "ms", createDuration < maximumBrowserCreationMilliseconds);
@@ -334,7 +340,7 @@ public void test_Constructor_asyncParentDisposal() {
334340
Display.getCurrent().asyncExec(() -> {
335341
shell.dispose();
336342
});
337-
Browser browser = createBrowser(shell, swtBrowserSettings);
343+
Browser browser = createBrowser(shell, swtBrowserSettings, false);
338344
assertFalse(browser.isDisposed());
339345
}
340346

0 commit comments

Comments
 (0)