Skip to content

Commit

Permalink
Added callAsyncJavaScript WebView method, fix #642, fix #614
Browse files Browse the repository at this point in the history
  • Loading branch information
pichillilorenzo committed Feb 7, 2021
1 parent 9b3ea1c commit 7c5931b
Show file tree
Hide file tree
Showing 14 changed files with 404 additions and 30 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method
- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient` classes
- Added `initialUserScripts` WebView option
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts` WebView methods
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts`, `callAsyncJavaScript` WebView methods
- Added `contentWorld` argument to `evaluateJavascript` WebView method
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains` iOS-specific webview options
- Added `handlesURLScheme` iOS-specific webview method
- Updated integration tests
Expand All @@ -29,13 +30,16 @@
- Fixed missing `clearHistory` webview method implementation on Android
- Fixed iOS crash when using CookieManager getCookies for an URL and the host URL is `null`
- Fixed "IOS does not support allowUniversalAccessFromFileURLs" [#654](https://github.com/pichillilorenzo/flutter_inappwebview/issues/654)
- Fixed "Failed to load WebView provider: No WebView installed" [#642](https://github.com/pichillilorenzo/flutter_inappwebview/issues/642)
- Fixed "java.net.MalformedURLException: unknown protocol: wss - Error using library sipml5 in flutter_inappwebview" [#614](https://github.com/pichillilorenzo/flutter_inappwebview/issues/614)

### BREAKING CHANGES

- Minimum Flutter version required is `1.22.0` and Dart SDK `>=2.12.0-0 <3.0.0`
- iOS Xcode version `>= 12`
- Removed `debuggingEnabled` WebView option; on Android you should use now the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method; on iOS, debugging is always enabled
- `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options moved from Android-specific options to cross-platform options.
- `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options moved from Android-specific options to cross-platform options
- Added `callAsyncJavaScript` name to the list of javaScriptHandlerForbiddenNames

## 4.0.0+4

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
Expand All @@ -42,6 +43,7 @@
import android.widget.LinearLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewCompat;
Expand All @@ -67,9 +69,11 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Pattern;

import io.flutter.plugin.common.MethodChannel;
Expand Down Expand Up @@ -116,6 +120,8 @@ final public class InAppWebView extends InputAwareWebView {
add("page");
}};

public Map<String, MethodChannel.Result> callAsyncJavaScriptResults = new HashMap<>();

static final String pluginScriptsWrapperJS = "(function(){" +
" if (window." + JavaScriptBridgeInterface.name + " == null || window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded == null || !window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded) {" +
" $PLACEHOLDER_VALUE" +
Expand Down Expand Up @@ -659,11 +665,22 @@ final public class InAppWebView extends InputAwareWebView {
" });" +
"})();";

static final String onWindowBlurEventJS = "(function(){" +
" window.addEventListener('blur', function(e) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onWindowBlur');" +
" });" +
"})();";
static final String onWindowBlurEventJS = "(function(){" +
" window.addEventListener('blur', function(e) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('onWindowBlur');" +
" });" +
"})();";

static final String callAsyncJavaScriptWrapperJS = "(function(obj) {" +
" (async function($FUNCTION_ARGUMENT_NAMES) {" +
" $FUNCTION_BODY" +
" })($FUNCTION_ARGUMENT_VALUES).then(function(value) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('callAsyncJavaScript', {'value': value, 'error': null, 'resultUuid': '$RESULT_UUID'});" +
" }).catch(function(error) {" +
" window." + JavaScriptBridgeInterface.name + ".callHandler('callAsyncJavaScript', {'value': null, 'error': error, 'resultUuid': '$RESULT_UUID'});" +
" });" +
" return null;" +
"})($FUNCTION_ARGUMENTS_OBJ);";

public InAppWebView(Context context) {
super(context);
Expand Down Expand Up @@ -1521,7 +1538,7 @@ public void onReceiveValue(String s) {
});
}

public void evaluateJavascript(String source, String contentWorldName, MethodChannel.Result result) {
public void evaluateJavascript(String source, @Nullable String contentWorldName, MethodChannel.Result result) {
injectDeferredObject(source, contentWorldName, null, result);
}

Expand Down Expand Up @@ -2093,6 +2110,47 @@ public String wrapSourceCodeInContentWorld(@Nullable String contentWorldName, St
return sourceWrapped;
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void callAsyncJavaScript(String functionBody, Map<String, Object> arguments, @Nullable String contentWorldName, @NonNull MethodChannel.Result result) {
String resultUuid = UUID.randomUUID().toString();
callAsyncJavaScriptResults.put(resultUuid, result);

JSONObject functionArguments = new JSONObject(arguments);
Iterator<String> keys = functionArguments.keys();

List<String> functionArgumentNamesList = new ArrayList<>();
List<String> functionArgumentValuesList = new ArrayList<>();
while (keys.hasNext()) {
String key = keys.next();
functionArgumentNamesList.add(key);
functionArgumentValuesList.add("obj." + key);
}

String functionArgumentNames = TextUtils.join(", ", functionArgumentNamesList);
String functionArgumentValues = TextUtils.join(", ", functionArgumentValuesList);
String functionArgumentsObj = Util.JSONStringify(arguments);

String sourceToInject = InAppWebView.callAsyncJavaScriptWrapperJS
.replace("$FUNCTION_ARGUMENT_NAMES", functionArgumentNames)
.replace("$FUNCTION_ARGUMENT_VALUES", functionArgumentValues)
.replace("$FUNCTION_ARGUMENTS_OBJ", functionArgumentsObj)
.replace("$FUNCTION_BODY", functionBody)
.replace("$RESULT_UUID", resultUuid);

if (contentWorldName != null && !contentWorldName.equals("page")) {
if (!userScriptsContentWorlds.contains(contentWorldName)) {
userScriptsContentWorlds.add(contentWorldName);
// Add only the first time all the plugin scripts needed.
String jsPluginScripts = prepareAndWrapPluginUserScripts();
sourceToInject = jsPluginScripts + "\n" + sourceToInject;
}
sourceToInject = wrapSourceCodeInContentWorld(contentWorldName, sourceToInject);

}

evaluateJavascript(sourceToInject, null);
}

@Override
public void dispose() {
if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
Expand All @@ -2106,6 +2164,7 @@ public void dispose() {
removeCallbacks(checkContextMenuShouldBeClosedTask);
if (checkScrollStoppedTask != null)
removeCallbacks(checkScrollStoppedTask);
callAsyncJavaScriptResults.clear();
super.dispose();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
import com.pichillilorenzo.flutter_inappwebview.Util;

import java.io.ByteArrayInputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
Expand Down Expand Up @@ -341,10 +340,10 @@ public void onReceivedHttpError (WebView view, WebResourceRequest request, WebRe
@Override
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) {

URL url;
URI uri;
try {
url = new URL(view.getUrl());
} catch (MalformedURLException e) {
uri = new URI(view.getUrl());
} catch (URISyntaxException e) {
e.printStackTrace();

credentialsProposed = null;
Expand All @@ -354,8 +353,8 @@ public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler
return;
}

final String protocol = url.getProtocol();
final int port = url.getPort();
final String protocol = uri.getScheme();
final int port = uri.getPort();

previousAuthRequestFailureCount++;

Expand Down Expand Up @@ -422,19 +421,19 @@ public void notImplemented() {

@Override
public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) {
URL url;
URI uri;
try {
url = new URL(error.getUrl());
} catch (MalformedURLException e) {
uri = new URI(view.getUrl());
} catch (URISyntaxException e) {
e.printStackTrace();
handler.cancel();
return;
}

final String host = url.getHost();
final String protocol = url.getProtocol();
final String host = uri.getHost();
final String protocol = uri.getScheme();
final String realm = null;
final int port = url.getPort();
final int port = uri.getPort();

Map<String, Object> obj = new HashMap<>();
obj.put("host", host);
Expand Down Expand Up @@ -507,16 +506,16 @@ public void notImplemented() {
@Override
public void onReceivedClientCertRequest(final WebView view, final ClientCertRequest request) {

URL url;
URI uri;
try {
url = new URL(view.getUrl());
} catch (MalformedURLException e) {
uri = new URI(view.getUrl());
} catch (URISyntaxException e) {
e.printStackTrace();
request.cancel();
return;
}

final String protocol = url.getProtocol();
final String protocol = uri.getScheme();
final String realm = null;

Map<String, Object> obj = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,17 @@ public void onReceiveValue(String value) {
}
result.success(true);
break;
case "callAsyncJavaScript":
if (webView != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
String functionBody = (String) call.argument("functionBody");
Map<String, Object> functionArguments = (Map<String, Object>) call.argument("arguments");
String contentWorldName = (String) call.argument("contentWorld");
webView.callAsyncJavaScript(functionBody, functionArguments, contentWorldName, result);
}
else {
result.success(null);
}
break;
default:
result.notImplemented();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

Expand Down Expand Up @@ -96,6 +100,21 @@ public void run() {

if (handlerName.equals("onPrint") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webView.printCurrentPage();
} else if (handlerName.equals("callAsyncJavaScript")) {
try {
JSONArray arguments = new JSONArray(args);
JSONObject jsonObject = arguments.getJSONObject(0);
String resultUuid = jsonObject.getString("resultUuid");
if (webView.callAsyncJavaScriptResults.containsKey(resultUuid)) {
MethodChannel.Result callAsyncJavaScriptResult = webView.callAsyncJavaScriptResults.get(resultUuid);
callAsyncJavaScriptResult.success(jsonObject.toString());

webView.callAsyncJavaScriptResults.remove(resultUuid);
}
} catch (JSONException e) {
e.printStackTrace();
}
return;
}

channel.invokeMethod("onCallJsHandler", obj, new MethodChannel.Result() {
Expand Down
Loading

0 comments on commit 7c5931b

Please sign in to comment.