Skip to content

Support cascaded java callbacks in Edge #1919 #2019

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public WebViewEnvironment(ICoreWebView2Environment environment) {

WebViewEnvironment containingEnvironment;

static boolean inCallback;
static int inCallback;
boolean inNewWindow;
private boolean inEvaluate;
HashMap<Long, LocationEvent> navigations = new HashMap<>();
Expand Down Expand Up @@ -235,11 +235,11 @@ static void error(int code, int hr) {

static IUnknown newCallback(ICoreWebView2SwtCallback handler) {
long punk = COM.CreateSwtWebView2Callback((arg0, arg1) -> {
inCallback = true;
inCallback++;
try {
return handler.Invoke(arg0, arg1);
} finally {
inCallback = false;
inCallback--;
}
});
if (punk == 0) error(SWT.ERROR_NO_HANDLES, COM.E_OUTOFMEMORY);
Expand Down Expand Up @@ -552,7 +552,7 @@ void checkDeadlock() {
// and JavaScript callbacks are serialized. An event handler waiting
// for a completion of another handler will deadlock. Detect this
// situation and throw an exception instead.
if (inCallback || inNewWindow) {
if (inCallback > 0 || inNewWindow) {
SWT.error(SWT.ERROR_FAILED_EVALUATE, null, " [WebView2: deadlock detected]");
}
}
Expand Down Expand Up @@ -821,7 +821,7 @@ void browserDispose(Event event) {
if(controller != null) {
// Bug in WebView2. Closing the controller from an event handler results
// in a crash. The fix is to delay the closure with asyncExec.
if (inCallback) {
if (inCallback > 0) {
ICoreWebView2Controller controller1 = controller;
controller.put_IsVisible(false);
browser.getDisplay().asyncExec(() -> {
Expand Down Expand Up @@ -916,7 +916,7 @@ public Object evaluate(String script) throws SWTException {
// Feature in WebView2. ExecuteScript works regardless of IsScriptEnabled setting.
// Disallow programmatic execution manually.
if (!jsEnabled) return null;
if(inCallback) {
if(inCallback > 0) {
// Execute script, but do not wait for async call to complete as otherwise it
// can cause a deadlock if execute inside a WebView callback.
execute(script);
Expand Down Expand Up @@ -1017,12 +1017,15 @@ long handleCallJava(int index, long bstrToken, long bstrArgsJson) {
String token = bstrToString(bstrToken);
BrowserFunction function = functions.get(index);
if (function != null && token.equals (function.token)) {
inCallback++;
try {
String argsJson = bstrToString(bstrArgsJson);
Object args = JSON.parse(argsJson.toCharArray());
result = function.function ((Object[]) args);
} catch (Throwable e) {
result = WebBrowser.CreateErrorString(e.getLocalizedMessage());
} finally {
inCallback--;
}
}
String json = JSON.stringify(result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2247,6 +2247,61 @@ function callCustomFunction() {
assertTrue(message, passed);
}

/**
* Test for stacked (cascaded) calls between Java and JS i.e. java calls JS
* which calls Java which calls JS and so on.
*
* @see https://github.com/eclipse-platform/eclipse.platform.swt/issues/1919
*
*/
@Test
public void test_BrowserFunction_callback_stackedCalls() {
assumeFalse("Not currently working on Linux, see https://github.com/eclipse-platform/eclipse.platform.swt/issues/2021", SwtTestUtil.isGTK);
AtomicInteger javaCallbackExecuted = new AtomicInteger();
final int DEPTH = 5;

class DeepJavascriptCallback extends BrowserFunction { // Note: Local class defined inside method.
DeepJavascriptCallback(Browser browser, String name) {
super(browser, name);
}

@Override
public Object function(Object[] arguments) {
if (javaCallbackExecuted.get() < DEPTH) {
javaCallbackExecuted.incrementAndGet();
browser.evaluate("jsCallbackToJava();");
}

return null;
}
}

// Define a javascript function and call it
String htmlWithScript = """
<html><head>
<script language="JavaScript">
function callCustomFunction() {
document.body.style.backgroundColor = 'red'
jsCallbackToJava()
}

</script>
</head>
<body> Going to make a callback to Java </body>
</html>
""";

browser.setText(htmlWithScript);
new DeepJavascriptCallback(browser, "jsCallbackToJava");

browser.addProgressListener(callCustomFunctionUponLoad);

shell.open();
boolean passed = waitForPassCondition(() -> javaCallbackExecuted.get() == DEPTH);
String message = "Java failed to get a callback from javascript. Test timed out";
assertTrue(message, passed);
}

/**
* Test that javascript can call java and pass an integer to java.
* loosely based on Snippet307.
Expand Down
Loading