Skip to content

Commit f1c51cb

Browse files
committed
[Win32] Spin event loop in Edge instead of only processing OS messages
Before recent improvements of the Edge implementation, it was possible that a browser instance was disposed during WebView initialization, which made the initialization fail exceptionally. This has been worked around by not processing all kinds of asynchronously scheduled events (like a disposal) while waiting for WebView initialization 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 the initialization happen asynchronously anyway, such that the browser instantiation itself cannot throw an error anymore, but the asynchronously happening initialization is now capable of processing asynchronous browser disposals. This change simplifies the event processing inside Edge to not only process the next OS message but to just ordinarily spin the event loop. This reduces the risk of operations executed on an Edge browser to block execution.
1 parent 23684ac commit f1c51cb

File tree

2 files changed

+27
-31
lines changed

2 files changed

+27
-31
lines changed

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

Lines changed: 18 additions & 28 deletions
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];
@@ -316,8 +316,8 @@ ICoreWebView2 initializeWebView(ICoreWebView2Controller controller) {
316316
return webView;
317317
}
318318

319-
private void abortInitialization() {
320-
webViewWrapperFuture.cancel(true);
319+
private void abortInitialization(String abortMessage) {
320+
webViewWrapperFuture.completeExceptionally(new SWTError(abortMessage));
321321
}
322322

323323
private ICoreWebView2_2 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() {
@@ -588,34 +574,38 @@ private void createInstance(int previousAttempts) {
588574

589575
private IUnknown createControllerInitializationCallback(int previousAttempts) {
590576
Runnable initializationRollback = () -> {
591-
webViewProvider.abortInitialization();
592577
if (environment2 != null) {
593578
environment2.Release();
594579
environment2 = null;
595580
}
596581
containingEnvironment.instances().remove(this);
597582
};
583+
Consumer<String> initializationAbortion = abortMessage -> {
584+
webViewProvider.abortInitialization(abortMessage);
585+
initializationRollback.run();
586+
};
598587
return newCallback((result, pv) -> {
599588
if (browser.isDisposed()) {
600-
initializationRollback.run();
589+
initializationAbortion.accept("Edge initialization was aborted");
601590
return COM.S_OK;
602591
}
603592
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");
593+
String message = "Edge instance with same data folder but different environment options already exists";
594+
initializationAbortion.accept(message);
595+
SWT.error(SWT.ERROR_INVALID_ARGUMENT, null, " " + message);
596+
return COM.S_OK;
607597
}
608598
switch ((int) result) {
609599
case COM.S_OK:
610600
new IUnknown(pv).AddRef();
611601
setupBrowser((int) result, pv);
612602
break;
613603
case COM.E_WRONG_THREAD:
614-
initializationRollback.run();
604+
initializationAbortion.accept("Invalid thread access");
615605
error(SWT.ERROR_THREAD_INVALID_ACCESS, (int) result);
616606
break;
617607
case COM.E_ABORT:
618-
initializationRollback.run();
608+
initializationAbortion.accept("Edge initialization was aborted");
619609
break;
620610
default:
621611
initializationRollback.run();

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

Lines changed: 9 additions & 3 deletions
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)