Skip to content

Commit

Permalink
[Win32] Spin event loop in Edge instead of processing OS messages ecl…
Browse files Browse the repository at this point in the history
…ipse-platform#1771

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.

eclipse-platform#1771
  • Loading branch information
HeikoKlare committed Jan 29, 2025
1 parent 632e54c commit 2772617
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ static int callAndWait(long[] ppv, ToIntFunction<IUnknown> callable) {
// "completion" callback may be called asynchronously,
// so keep processing next OS message that may call it
while (phr[0] == COM.S_OK && ppv[0] == 0) {
processNextOSMessage();
spinEventLoop();
}
completion.Release();
return phr[0];
Expand All @@ -281,7 +281,7 @@ static int callAndWait(String[] pstr, ToIntFunction<IUnknown> callable) {
// "completion" callback may be called asynchronously,
// so keep processing next OS message that may call it
while (phr[0] == COM.S_OK && pstr[0] == null) {
processNextOSMessage();
spinEventLoop();
}
completion.Release();
return phr[0];
Expand Down Expand Up @@ -444,31 +444,17 @@ void scheduleWebViewTask(Runnable action) {

private <T> void waitForFutureToFinish(CompletableFuture<T> future) {
while(!future.isDone()) {
processNextOSMessage();
spinEventLoop();
}
}

}

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

static ICoreWebView2CookieManager getCookieManager() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.FixMethodOrder;
import org.junit.Ignore;
import org.junit.Rule;
Expand Down Expand Up @@ -158,16 +157,6 @@ public Test_org_eclipse_swt_browser_Browser(int swtBrowserSettings) {
this.swtBrowserSettings = swtBrowserSettings;
}

@BeforeClass
public static void setupEdgeEnvironment() {
// initialize Edge environment before any test runs to isolate environment setup
if (SwtTestUtil.isWindows) {
Shell shell = new Shell();
new Browser(shell, SWT.EDGE);
shell.dispose();
}
}

@Override
@Before
public void setUp() {
Expand Down Expand Up @@ -302,11 +291,17 @@ private int reportOpenedDescriptors() {
}

private Browser createBrowser(Shell s, int flags) {
return createBrowser(s, flags, true);
}

private Browser createBrowser(Shell s, int flags, boolean expectSuccess) {
long maximumBrowserCreationMilliseconds = 90_000;
long createStartTime = System.currentTimeMillis();
Browser b = new Browser(s, flags);
// Wait for asynchronous initialization via getting URL
b.getUrl();
if (expectSuccess) {
b.getUrl();
}
createdBroswers.add(b);
long createDuration = System.currentTimeMillis() - createStartTime;
assertTrue("creating browser took too long: " + createDuration + "ms", createDuration < maximumBrowserCreationMilliseconds);
Expand Down Expand Up @@ -334,7 +329,7 @@ public void test_Constructor_asyncParentDisposal() {
Display.getCurrent().asyncExec(() -> {
shell.dispose();
});
Browser browser = createBrowser(shell, swtBrowserSettings);
Browser browser = createBrowser(shell, swtBrowserSettings, false);
assertFalse(browser.isDisposed());
}

Expand Down

0 comments on commit 2772617

Please sign in to comment.