Skip to content

Commit f9ee6fd

Browse files
committed
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 0fe4a76 commit f9ee6fd

File tree

2 files changed

+30
-10
lines changed

2 files changed

+30
-10
lines changed

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

Lines changed: 30 additions & 4 deletions
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 = false;
8485
HashMap<Long, LocationEvent> navigations = new HashMap<>();
8586
private boolean ignoreGotFocus;
8687
private boolean ignoreFocusIn;
@@ -906,10 +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+
}
916+
inEvaluate = true;
910917
String script2 = "(function() {try { " + script + " } catch (e) { return '" + ERROR_ID + "' + e.message; } })();\0";
911918
String[] pJson = new String[1];
912-
int hr = callAndWait(pJson, completion -> webViewProvider.getWebView(true).ExecuteScript(script2.toCharArray(), completion));
919+
int hr;
920+
try {
921+
hr = callAndWait(pJson, completion -> webViewProvider.getWebView(true).ExecuteScript(script2.toCharArray(), completion));
922+
} finally {
923+
inEvaluate = false;
924+
}
913925
if (hr != COM.S_OK) error(SWT.ERROR_FAILED_EVALUATE, hr);
914926

915927
Object data = JSON.parse(pJson[0]);
@@ -1319,7 +1331,7 @@ int handleNewWindowRequested(long pView, long pArgs) {
13191331
args.GetDeferral(ppv);
13201332
ICoreWebView2Deferral deferral = new ICoreWebView2Deferral(ppv[0]);
13211333
inNewWindow = true;
1322-
browser.getDisplay().asyncExec(() -> {
1334+
Runnable openWindowHandler = () -> {
13231335
try {
13241336
if (browser.isDisposed()) return;
13251337
WindowEvent openEvent = new WindowEvent(browser);
@@ -1355,7 +1367,21 @@ int handleNewWindowRequested(long pView, long pArgs) {
13551367
args.Release();
13561368
inNewWindow = false;
13571369
}
1358-
});
1370+
};
1371+
1372+
// In case a new window is opened using evaluate with scripts like
1373+
// 'window.open()', there can be a deadlock if we execute the handler
1374+
// asynchronously. And in case a new browser is initialized in the same
1375+
// environment inside an OpenWindowListener of another browser, there can also
1376+
// be a deadlock. Hence, generally it should be run asynchronously. However, a
1377+
// combination of opening a new window from evaluate and starting a new instance
1378+
// of browser inside the OpenWindowListener should be avoided.
1379+
if (inEvaluate) {
1380+
openWindowHandler.run();
1381+
} else {
1382+
browser.getDisplay().asyncExec(openWindowHandler);
1383+
}
1384+
13591385
return COM.S_OK;
13601386
}
13611387

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

Lines changed: 0 additions & 6 deletions
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)