Skip to content

Commit c2136c9

Browse files
amartya4256fedejeanne
authored andcommitted
Edge:evaluate deadlock fix #1466
This commit cotributes to replicating the behaviour of Edge:evaluate as it is in WebKit - it must not wait for the execution of script to obtain the result, in case evaluate is called inside a WebView callback. This commit also makes sure that OpenWindowListeners are execute synchronously or asynchronously depending on if the handleNewWindowRequested is called from the evaluate script. Moreover, this enables all the tests which were failing because of Edge:evaluate limitations. contributes to #1771 and #1919
1 parent 601136c commit c2136c9

File tree

2 files changed

+30
-11
lines changed

2 files changed

+30
-11
lines changed

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

+30-5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public WebViewEnvironment(ICoreWebView2Environment environment) {
8181

8282
static boolean inCallback;
8383
boolean inNewWindow;
84+
private boolean inEvaluate;
8485
HashMap<Long, LocationEvent> navigations = new HashMap<>();
8586
private boolean ignoreGotFocus;
8687
private boolean ignoreFocusIn;
@@ -906,11 +907,21 @@ public Object evaluate(String script) throws SWTException {
906907
// Feature in WebView2. ExecuteScript works regardless of IsScriptEnabled setting.
907908
// Disallow programmatic execution manually.
908909
if (!jsEnabled) return null;
909-
910+
if(inCallback) {
911+
// Execute script, but do not wait for async call to complete as otherwise it
912+
// can cause a deadlock if execute inside a WebView callback.
913+
execute(script);
914+
return null;
915+
}
910916
String script2 = "(function() {try { " + script + " } catch (e) { return '" + ERROR_ID + "' + e.message; } })();\0";
911917
String[] pJson = new String[1];
912-
int hr = callAndWait(pJson, completion -> webViewProvider.getWebView(true).ExecuteScript(script2.toCharArray(), completion));
913-
if (hr != COM.S_OK) error(SWT.ERROR_FAILED_EVALUATE, hr);
918+
inEvaluate = true;
919+
try {
920+
int hr = callAndWait(pJson, completion -> webViewProvider.getWebView(true).ExecuteScript(script2.toCharArray(), completion));
921+
if (hr != COM.S_OK) error(SWT.ERROR_FAILED_EVALUATE, hr);
922+
} finally {
923+
inEvaluate = false;
924+
}
914925

915926
Object data = JSON.parse(pJson[0]);
916927
if (data instanceof String && ((String) data).startsWith(ERROR_ID)) {
@@ -1319,7 +1330,7 @@ int handleNewWindowRequested(long pView, long pArgs) {
13191330
args.GetDeferral(ppv);
13201331
ICoreWebView2Deferral deferral = new ICoreWebView2Deferral(ppv[0]);
13211332
inNewWindow = true;
1322-
browser.getDisplay().asyncExec(() -> {
1333+
Runnable openWindowHandler = () -> {
13231334
try {
13241335
if (browser.isDisposed()) return;
13251336
WindowEvent openEvent = new WindowEvent(browser);
@@ -1355,7 +1366,21 @@ int handleNewWindowRequested(long pView, long pArgs) {
13551366
args.Release();
13561367
inNewWindow = false;
13571368
}
1358-
});
1369+
};
1370+
1371+
// Creating a new browser instance within the same environment from inside the OpenWindowListener of another browser
1372+
// can lead to a deadlock. To prevent this, handlers should typically run asynchronously.
1373+
// However, if a new window is opened using `evaluate(window.open)`, running the handler asynchronously in that context
1374+
// may also result in a deadlock.
1375+
// Therefore, whether the listener runs synchronously or asynchronously should depend on the `inEvaluate` condition.
1376+
// That said, combining both situations—opening a window via `evaluate` and launching a new browser inside the OpenWindowListener—
1377+
// should be avoided altogether, as it significantly increases the risk of deadlocks.
1378+
if (inEvaluate) {
1379+
openWindowHandler.run();
1380+
} else {
1381+
browser.getDisplay().asyncExec(openWindowHandler);
1382+
}
1383+
13591384
return COM.S_OK;
13601385
}
13611386

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

-6
Original file line numberDiff line numberDiff line change
@@ -745,7 +745,6 @@ public void changed(LocationEvent event) {
745745

746746
@Test
747747
public void test_LocationListener_LocationListener_ordered_changing () {
748-
assumeFalse("Currently broken for Edge", isEdge);
749748
List<String> locations = Collections.synchronizedList(new ArrayList<>());
750749
browser.addLocationListener(changingAdapter(event -> {
751750
locations.add(event.location);
@@ -1578,8 +1577,6 @@ public void completed(ProgressEvent event) {
15781577
*/
15791578
@Test
15801579
public void test_LocationListener_evaluateInCallback() {
1581-
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);
1582-
15831580
AtomicBoolean changingFinished = new AtomicBoolean(false);
15841581
AtomicBoolean changedFinished = new AtomicBoolean(false);
15851582
browser.addLocationListener(new LocationListener() {
@@ -1628,8 +1625,6 @@ public void changed(LocationEvent event) {
16281625
/** Verify that evaluation works inside an OpenWindowListener */
16291626
@Test
16301627
public void test_OpenWindowListener_evaluateInCallback() {
1631-
assumeFalse("behavior is not (yet) supported by Edge browser", isEdge);
1632-
16331628
AtomicBoolean eventFired = new AtomicBoolean(false);
16341629
browser.addOpenWindowListener(event -> {
16351630
browser.evaluate("SWTopenListener = true");
@@ -2189,7 +2184,6 @@ public void test_evaluate_array_mixedTypes () {
21892184
*/
21902185
@Test
21912186
public void test_BrowserFunction_callback () {
2192-
assumeFalse("Currently broken for Edge", isEdge);
21932187
AtomicBoolean javaCallbackExecuted = new AtomicBoolean(false);
21942188

21952189
class JavascriptCallback extends BrowserFunction { // Note: Local class defined inside method.

0 commit comments

Comments
 (0)