[Browser][Edge] Code cleanup

* Rename webView2 to webView_2 to avoid confusion, since "WebView2"
  is the official name of the whole component.
* Introduce helpers for waiting for async function result.
* Introduce helper for null-terminating strings.

Change-Id: Ie45b034c6e28b8569a307260677a845e89ff2d81
Signed-off-by: Nikita Nemkin <nikita@nemkin.ru>
Reviewed-on: https://git.eclipse.org/r/c/platform/eclipse.platform.swt/+/182207
Tested-by: Platform Bot <platform-bot@eclipse.org>
Reviewed-by: Niraj Modi <niraj.modi@in.ibm.com>
diff --git a/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
index 9f9a74d..22caa57 100644
--- a/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
+++ b/bundles/org.eclipse.swt/Eclipse SWT Browser/win32/org/eclipse/swt/browser/Edge.java
@@ -15,6 +15,7 @@
 
 import java.nio.charset.*;
 import java.util.*;
+import java.util.function.*;
 
 import org.eclipse.swt.*;
 import org.eclipse.swt.graphics.*;
@@ -45,7 +46,7 @@
 	static ICoreWebView2Environment Environment;
 
 	ICoreWebView2 webView;
-	ICoreWebView2_2 webView2;
+	ICoreWebView2_2 webView_2;
 	ICoreWebView2Controller controller;
 	ICoreWebView2Settings settings;
 	ICoreWebView2Environment2 environment2;
@@ -71,6 +72,10 @@
 	return String.valueOf(data);
 }
 
+static char[] stringToWstr(String s) {
+	return (s != null) ? (s + "\0").toCharArray() : null;
+}
+
 static void error(int code, int hr) {
 	SWT.error(code, null, String.format(" [0x%08x]", hr));
 }
@@ -94,6 +99,45 @@
 	return new IUnknown(pdisp);
 }
 
+static int callAndWait(long[] ppv, ToIntFunction<IUnknown> callable) {
+	int[] phr = {COM.S_OK};
+	IUnknown completion = newCallback((result, pv) -> {
+		phr[0] = (int)result;
+		if ((int)result == COM.S_OK) {
+			ppv[0] = pv;
+			new IUnknown(pv).AddRef();
+		}
+		return COM.S_OK;
+	});
+	ppv[0] = 0;
+	phr[0] = callable.applyAsInt(completion);
+	completion.Release();
+	Display display = Display.getCurrent();
+	while (phr[0] == COM.S_OK && ppv[0] == 0) {
+		if (!display.readAndDispatch()) display.sleep();
+	}
+	return phr[0];
+}
+
+static int callAndWait(String[] pstr, ToIntFunction<IUnknown> callable) {
+	int[] phr = new int[1];
+	IUnknown completion = newCallback((result, pszJson) -> {
+		phr[0] = (int)result;
+		if ((int)result == COM.S_OK) {
+			pstr[0] = wstrToString(pszJson, false);
+		}
+		return COM.S_OK;
+	});
+	pstr[0] = null;
+	phr[0] = callable.applyAsInt(completion);
+	completion.Release();
+	Display display = Display.getCurrent();
+	while (phr[0] == COM.S_OK && pstr[0] == null) {
+		if (!display.readAndDispatch()) display.sleep();
+	}
+	return phr[0];
+}
+
 void checkDeadlock() {
 	// Feature in WebView2. All event handlers, completion handlers
 	// and JavaScript callbacks are serialized. An event handler waiting
@@ -122,42 +166,29 @@
 	long pOpts = COM.CreateSwtWebView2Options();
 	if (pOpts == 0) error(SWT.ERROR_NO_HANDLES, COM.E_OUTOFMEMORY);
 	ICoreWebView2EnvironmentOptions options = new ICoreWebView2EnvironmentOptions(pOpts);
-	char[] pVersion = (SDK_TARGET_VERSION + "\0").toCharArray();
+	char[] pVersion = stringToWstr(SDK_TARGET_VERSION);
 	options.put_TargetCompatibleBrowserVersion(pVersion);
 	if (browserArgs != null) {
-		char[] pBrowserArgs = (browserArgs + "\0").toCharArray();
+		char[] pBrowserArgs = stringToWstr(browserArgs);
 		options.put_AdditionalBrowserArguments(pBrowserArgs);
 	}
 	if (language != null) {
-		char[] pLanguage = (language + "\0").toCharArray();
+		char[] pLanguage = stringToWstr(language);
 		options.put_Language(pLanguage);
 	}
 
 	// Create the environment
-	char[] pBrowserDir = (browserDir != null) ? (browserDir + "\0").toCharArray() : null;
-	char[] pDataDir = (dataDir + "\0").toCharArray();
-	int[] pResult = {COM.S_OK};
-	long[] ppEnv = new long[1];
-	IUnknown completion = newCallback((result, pEnv) -> {
-		pResult[0] = (int)result;
-		if ((int)result == COM.S_OK) {
-			ppEnv[0] = pEnv;
-			new IUnknown(pEnv).AddRef();
-		}
-		return COM.S_OK;
-	});
-	pResult[0] = COM.CreateCoreWebView2EnvironmentWithOptions(
-						pBrowserDir, pDataDir, options.getAddress(), completion.getAddress());
-	completion.Release();
+	char[] pBrowserDir = stringToWstr(browserDir);
+	char[] pDataDir = stringToWstr(dataDir);
+	long[] ppv = new long[1];
+	int hr = callAndWait(ppv, completion -> COM.CreateCoreWebView2EnvironmentWithOptions(
+					pBrowserDir, pDataDir, options.getAddress(), completion.getAddress()));
 	options.Release();
-	if (pResult[0] == OS.HRESULT_FROM_WIN32(OS.ERROR_FILE_NOT_FOUND)) {
+	if (hr == OS.HRESULT_FROM_WIN32(OS.ERROR_FILE_NOT_FOUND)) {
 		SWT.error(SWT.ERROR_NOT_IMPLEMENTED, null, " [WebView2 runtime not found]");
 	}
-	while (pResult[0] == COM.S_OK && ppEnv[0] == 0) {
-		if (!display.readAndDispatch()) display.sleep();
-	}
-	if (pResult[0] != COM.S_OK) error(SWT.ERROR_NO_HANDLES, pResult[0]);
-	Environment = new ICoreWebView2Environment(ppEnv[0]);
+	if (hr != COM.S_OK) error(SWT.ERROR_NO_HANDLES, hr);
+	Environment = new ICoreWebView2Environment(ppv[0]);
 	DataDir = dataDir;
 
 	// Save Edge version for reporting
@@ -179,34 +210,20 @@
 	checkDeadlock();
 	ICoreWebView2Environment environment = createEnvironment();
 
-	int[] pResult = {COM.S_OK};
-	long[] ppv = new long[1], ppHost = new long[1];
+	long[] ppv = new long[1];
 	int hr = environment.QueryInterface(COM.IID_ICoreWebView2Environment2, ppv);
 	if (hr == COM.S_OK) environment2 = new ICoreWebView2Environment2(ppv[0]);
 
-	IUnknown completion = newCallback((result, pHost) -> {
-		pResult[0] = (int)result;
-		if ((int)result == COM.S_OK) {
-			ppHost[0] = pHost;
-			new IUnknown(pHost).AddRef();
-		}
-		return COM.S_OK;
-	});
-	pResult[0] = environment.CreateCoreWebView2Controller(browser.handle, completion);
-	completion.Release();
-	Display display = browser.getDisplay();
-	while (pResult[0] == COM.S_OK && ppHost[0] == 0) {
-		if (!display.readAndDispatch()) display.sleep();
-	}
-	if (pResult[0] != COM.S_OK) error(SWT.ERROR_NO_HANDLES, pResult[0]);
-	controller = new ICoreWebView2Controller(ppHost[0]);
+	hr = callAndWait(ppv, completion -> environment.CreateCoreWebView2Controller(browser.handle, completion));
+	if (hr != COM.S_OK) error(SWT.ERROR_NO_HANDLES, hr);
+	controller = new ICoreWebView2Controller(ppv[0]);
 
 	controller.get_CoreWebView2(ppv);
 	webView = new ICoreWebView2(ppv[0]);
 	webView.get_Settings(ppv);
 	settings = new ICoreWebView2Settings(ppv[0]);
 	hr = webView.QueryInterface(COM.IID_ICoreWebView2_2, ppv);
-	if (hr == COM.S_OK) webView2 = new ICoreWebView2_2(ppv[0]);
+	if (hr == COM.S_OK) webView_2 = new ICoreWebView2_2(ppv[0]);
 
 	long[] token = new long[1];
 	IUnknown handler;
@@ -237,9 +254,9 @@
 	handler = newCallback(this::handleMoveFocusRequested);
 	controller.add_MoveFocusRequested(handler, token);
 	handler.Release();
-	if (webView2 != null) {
+	if (webView_2 != null) {
 		handler = newCallback(this::handleDOMContentLoaded);
-		webView2.add_DOMContentLoaded(handler, token);
+		webView_2.add_DOMContentLoaded(handler, token);
 		handler.Release();
 	}
 
@@ -255,11 +272,11 @@
 }
 
 void browserDispose(Event event) {
-	if (webView2 != null) webView2.Release();
+	if (webView_2 != null) webView_2.Release();
 	if (environment2 != null) environment2.Release();
 	settings.Release();
 	webView.Release();
-	webView2 = null;
+	webView_2 = null;
 	environment2 = null;
 	settings = null;
 	webView = null;
@@ -304,23 +321,11 @@
 	// Disallow programmatic execution manually.
 	if (!jsEnabled) return null;
 
-	int[] pResult = new int[1];
+	String script2 = "(function() {try { " + script + " } catch (e) { return '" + ERROR_ID + "' + e.message; } })();\0";
 	String[] pJson = new String[1];
-	IUnknown completion = newCallback((result, pszJson) -> {
-		pResult[0] = (int)result;
-		if ((int)result == COM.S_OK) {
-			pJson[0] = wstrToString(pszJson, false);
-		}
-		return COM.S_OK;
-	});
-	script = "(function() {try { " + script + " } catch (e) { return '" + ERROR_ID + "' + e.message; } })();\0";
-	pResult[0] = webView.ExecuteScript(script.toCharArray(), completion);
-	completion.Release();
-	Display display = browser.getDisplay();
-	while (pResult[0] == COM.S_OK && pJson[0] == null) {
-		if (!display.readAndDispatch()) display.sleep();
-	}
-	if (pResult[0] != COM.S_OK) error(SWT.ERROR_FAILED_EVALUATE, pResult[0]);
+	int hr = callAndWait(pJson, completion -> webView.ExecuteScript(script2.toCharArray(), completion));
+	if (hr != COM.S_OK) error(SWT.ERROR_FAILED_EVALUATE, hr);
+
 	Object data = JSON.parse(pJson[0]);
 	if (data instanceof String && ((String) data).startsWith(ERROR_ID)) {
 		String errorMessage = ((String) data).substring(ERROR_ID.length());
@@ -336,7 +341,7 @@
 	if (!jsEnabled) return false;
 
 	IUnknown completion = newCallback((long result, long json) -> COM.S_OK);
-	int hr = webView.ExecuteScript((script + "\0").toCharArray(), completion);
+	int hr = webView.ExecuteScript(stringToWstr(script), completion);
 	completion.Release();
 	return hr == COM.S_OK;
 }
@@ -522,7 +527,7 @@
 	long[] pNavId = new long[1];
 	args.get_NavigationId(pNavId);
 	LocationEvent startEvent = navigations.remove(pNavId[0]);
-	if (webView2 == null && startEvent != null && startEvent.top) {
+	if (webView_2 == null && startEvent != null && startEvent.top) {
 		// If DOMContentLoaded isn't available, fire
 		// ProgressListener.completed from here.
 		sendProgressCompleted();
@@ -672,9 +677,9 @@
 		url = "http://" + url;
 	}
 	int hr;
-	char[] pszUrl = (url + "\0").toCharArray();
+	char[] pszUrl = stringToWstr(url);
 	if (postData != null || headers != null) {
-		if (environment2 == null || webView2 == null) {
+		if (environment2 == null || webView_2 == null) {
 			SWT.error(SWT.ERROR_NOT_IMPLEMENTED, null, " [WebView2 version 88+ is required to set postData and headers]");
 		}
 		long[] ppRequest = new long[1];
@@ -699,7 +704,7 @@
 		if (stream != null) stream.Release();
 		if (hr != COM.S_OK) error(SWT.ERROR_NO_HANDLES, hr);
 		IUnknown request = new IUnknown(ppRequest[0]);
-		hr = webView2.NavigateWithWebResourceRequest(request);
+		hr = webView_2.NavigateWithWebResourceRequest(request);
 		request.Release();
 	} else {
 		hr = webView.Navigate(pszUrl);