parent
9b3ea1c6a2
commit
7c5931b0f9
|
@ -7,7 +7,8 @@
|
||||||
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method
|
- Added `IOSCookieManager` class and `CookieManager.instance().ios.getAllCookies` iOS-specific method
|
||||||
- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient` classes
|
- Added `UserScript`, `UserScriptInjectionTime`, `ContentWorld`, `AndroidWebViewFeature`, `AndroidServiceWorkerController`, `AndroidServiceWorkerClient` classes
|
||||||
- Added `initialUserScripts` WebView option
|
- 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 `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains` iOS-specific webview options
|
||||||
- Added `handlesURLScheme` iOS-specific webview method
|
- Added `handlesURLScheme` iOS-specific webview method
|
||||||
- Updated integration tests
|
- Updated integration tests
|
||||||
|
@ -29,13 +30,16 @@
|
||||||
- Fixed missing `clearHistory` webview method implementation on Android
|
- 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 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 "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
|
### BREAKING CHANGES
|
||||||
|
|
||||||
- Minimum Flutter version required is `1.22.0` and Dart SDK `>=2.12.0-0 <3.0.0`
|
- Minimum Flutter version required is `1.22.0` and Dart SDK `>=2.12.0-0 <3.0.0`
|
||||||
- iOS Xcode version `>= 12`
|
- 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
|
- 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
|
## 4.0.0+4
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import android.os.Message;
|
||||||
import android.print.PrintAttributes;
|
import android.print.PrintAttributes;
|
||||||
import android.print.PrintDocumentAdapter;
|
import android.print.PrintDocumentAdapter;
|
||||||
import android.print.PrintManager;
|
import android.print.PrintManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.ActionMode;
|
import android.view.ActionMode;
|
||||||
|
@ -42,6 +43,7 @@ import android.widget.HorizontalScrollView;
|
||||||
import android.widget.LinearLayout;
|
import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.RequiresApi;
|
import androidx.annotation.RequiresApi;
|
||||||
import androidx.webkit.WebViewCompat;
|
import androidx.webkit.WebViewCompat;
|
||||||
|
@ -67,9 +69,11 @@ import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import io.flutter.plugin.common.MethodChannel;
|
import io.flutter.plugin.common.MethodChannel;
|
||||||
|
@ -116,6 +120,8 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
add("page");
|
add("page");
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
public Map<String, MethodChannel.Result> callAsyncJavaScriptResults = new HashMap<>();
|
||||||
|
|
||||||
static final String pluginScriptsWrapperJS = "(function(){" +
|
static final String pluginScriptsWrapperJS = "(function(){" +
|
||||||
" if (window." + JavaScriptBridgeInterface.name + " == null || window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded == null || !window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded) {" +
|
" if (window." + JavaScriptBridgeInterface.name + " == null || window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded == null || !window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded) {" +
|
||||||
" $PLACEHOLDER_VALUE" +
|
" $PLACEHOLDER_VALUE" +
|
||||||
|
@ -659,11 +665,22 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
" });" +
|
" });" +
|
||||||
"})();";
|
"})();";
|
||||||
|
|
||||||
static final String onWindowBlurEventJS = "(function(){" +
|
static final String onWindowBlurEventJS = "(function(){" +
|
||||||
" window.addEventListener('blur', function(e) {" +
|
" window.addEventListener('blur', function(e) {" +
|
||||||
" window." + JavaScriptBridgeInterface.name + ".callHandler('onWindowBlur');" +
|
" 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) {
|
public InAppWebView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -1521,7 +1538,7 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
injectDeferredObject(source, contentWorldName, null, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2093,6 +2110,47 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
return sourceWrapped;
|
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
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
|
if (windowId != null && InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
|
||||||
|
@ -2106,6 +2164,7 @@ final public class InAppWebView extends InputAwareWebView {
|
||||||
removeCallbacks(checkContextMenuShouldBeClosedTask);
|
removeCallbacks(checkContextMenuShouldBeClosedTask);
|
||||||
if (checkScrollStoppedTask != null)
|
if (checkScrollStoppedTask != null)
|
||||||
removeCallbacks(checkScrollStoppedTask);
|
removeCallbacks(checkScrollStoppedTask);
|
||||||
|
callAsyncJavaScriptResults.clear();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,6 @@ import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivit
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -341,10 +340,10 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
@Override
|
@Override
|
||||||
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) {
|
public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) {
|
||||||
|
|
||||||
URL url;
|
URI uri;
|
||||||
try {
|
try {
|
||||||
url = new URL(view.getUrl());
|
uri = new URI(view.getUrl());
|
||||||
} catch (MalformedURLException e) {
|
} catch (URISyntaxException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
|
||||||
credentialsProposed = null;
|
credentialsProposed = null;
|
||||||
|
@ -354,8 +353,8 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String protocol = url.getProtocol();
|
final String protocol = uri.getScheme();
|
||||||
final int port = url.getPort();
|
final int port = uri.getPort();
|
||||||
|
|
||||||
previousAuthRequestFailureCount++;
|
previousAuthRequestFailureCount++;
|
||||||
|
|
||||||
|
@ -422,19 +421,19 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) {
|
public void onReceivedSslError(final WebView view, final SslErrorHandler handler, final SslError error) {
|
||||||
URL url;
|
URI uri;
|
||||||
try {
|
try {
|
||||||
url = new URL(error.getUrl());
|
uri = new URI(view.getUrl());
|
||||||
} catch (MalformedURLException e) {
|
} catch (URISyntaxException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
handler.cancel();
|
handler.cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String host = url.getHost();
|
final String host = uri.getHost();
|
||||||
final String protocol = url.getProtocol();
|
final String protocol = uri.getScheme();
|
||||||
final String realm = null;
|
final String realm = null;
|
||||||
final int port = url.getPort();
|
final int port = uri.getPort();
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
obj.put("host", host);
|
obj.put("host", host);
|
||||||
|
@ -507,16 +506,16 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
@Override
|
@Override
|
||||||
public void onReceivedClientCertRequest(final WebView view, final ClientCertRequest request) {
|
public void onReceivedClientCertRequest(final WebView view, final ClientCertRequest request) {
|
||||||
|
|
||||||
URL url;
|
URI uri;
|
||||||
try {
|
try {
|
||||||
url = new URL(view.getUrl());
|
uri = new URI(view.getUrl());
|
||||||
} catch (MalformedURLException e) {
|
} catch (URISyntaxException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
request.cancel();
|
request.cancel();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String protocol = url.getProtocol();
|
final String protocol = uri.getScheme();
|
||||||
final String realm = null;
|
final String realm = null;
|
||||||
|
|
||||||
Map<String, Object> obj = new HashMap<>();
|
Map<String, Object> obj = new HashMap<>();
|
||||||
|
|
|
@ -438,6 +438,17 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
|
||||||
}
|
}
|
||||||
result.success(true);
|
result.success(true);
|
||||||
break;
|
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:
|
default:
|
||||||
result.notImplemented();
|
result.notImplemented();
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,10 @@ import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivit
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.FlutterWebView;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebView.InAppWebView;
|
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.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -96,6 +100,21 @@ public class JavaScriptBridgeInterface {
|
||||||
|
|
||||||
if (handlerName.equals("onPrint") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (handlerName.equals("onPrint") && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
webView.printCurrentPage();
|
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() {
|
channel.invokeMethod("onCallJsHandler", obj, new MethodChannel.Result() {
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
package com.pichillilorenzo.flutter_inappwebview;
|
package com.pichillilorenzo.flutter_inappwebview;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.AsyncTask;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.os.Message;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.webkit.CookieManager;
|
import android.webkit.CookieManager;
|
||||||
import android.webkit.CookieSyncManager;
|
import android.webkit.CookieSyncManager;
|
||||||
import android.webkit.ValueCallback;
|
import android.webkit.ValueCallback;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
@ -16,6 +24,7 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import io.flutter.plugin.common.BinaryMessenger;
|
import io.flutter.plugin.common.BinaryMessenger;
|
||||||
import io.flutter.plugin.common.MethodCall;
|
import io.flutter.plugin.common.MethodCall;
|
||||||
|
@ -29,10 +38,13 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
public static MethodChannel channel;
|
public static MethodChannel channel;
|
||||||
public static CookieManager cookieManager;
|
public static CookieManager cookieManager;
|
||||||
|
|
||||||
|
// As CookieManager was synchronous before API 21 this class emulates the async behavior on <21.
|
||||||
|
private static final boolean USES_LEGACY_STORE = Build.VERSION.SDK_INT < 21;
|
||||||
|
|
||||||
public MyCookieManager(BinaryMessenger messenger) {
|
public MyCookieManager(BinaryMessenger messenger) {
|
||||||
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_cookiemanager");
|
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_cookiemanager");
|
||||||
channel.setMethodCallHandler(this);
|
channel.setMethodCallHandler(this);
|
||||||
cookieManager = CookieManager.getInstance();
|
cookieManager = getCookieManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,6 +104,40 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instantiating CookieManager will load the Chromium task taking a 100ish ms so we do it lazily
|
||||||
|
* to make sure it's done on a background thread as needed.
|
||||||
|
*
|
||||||
|
* https://github.com/facebook/react-native/blob/1903f6680d9750e244d97c3cd4a9f755a9a47c61/ReactAndroid/src/main/java/com/facebook/react/modules/network/ForwardingCookieHandler.java#L132
|
||||||
|
*/
|
||||||
|
static private @Nullable CookieManager getCookieManager() {
|
||||||
|
if (cookieManager == null) {
|
||||||
|
try {
|
||||||
|
cookieManager = CookieManager.getInstance();
|
||||||
|
} catch (IllegalArgumentException ex) {
|
||||||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=559720
|
||||||
|
return null;
|
||||||
|
} catch (Exception exception) {
|
||||||
|
String message = exception.getMessage();
|
||||||
|
// We cannot catch MissingWebViewPackageException as it is in a private / system API
|
||||||
|
// class. This validates the exception's message to ensure we are only handling this
|
||||||
|
// specific exception.
|
||||||
|
// https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/webkit/WebViewFactory.java#348
|
||||||
|
if (message != null
|
||||||
|
&& exception
|
||||||
|
.getClass()
|
||||||
|
.getCanonicalName()
|
||||||
|
.equals("android.webkit.WebViewFactory.MissingWebViewPackageException")) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cookieManager;
|
||||||
|
}
|
||||||
|
|
||||||
public static void setCookie(String url,
|
public static void setCookie(String url,
|
||||||
String name,
|
String name,
|
||||||
String value,
|
String value,
|
||||||
|
@ -103,6 +149,8 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
Boolean isHttpOnly,
|
Boolean isHttpOnly,
|
||||||
String sameSite,
|
String sameSite,
|
||||||
final MethodChannel.Result result) {
|
final MethodChannel.Result result) {
|
||||||
|
cookieManager = getCookieManager();
|
||||||
|
if (cookieManager == null) return;
|
||||||
|
|
||||||
String cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
|
String cookieValue = name + "=" + value + "; Domain=" + domain + "; Path=" + path;
|
||||||
|
|
||||||
|
@ -146,6 +194,9 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
|
|
||||||
final List<Map<String, Object>> cookieListMap = new ArrayList<>();
|
final List<Map<String, Object>> cookieListMap = new ArrayList<>();
|
||||||
|
|
||||||
|
cookieManager = getCookieManager();
|
||||||
|
if (cookieManager == null) return cookieListMap;
|
||||||
|
|
||||||
String cookiesString = cookieManager.getCookie(url);
|
String cookiesString = cookieManager.getCookie(url);
|
||||||
|
|
||||||
if (cookiesString != null) {
|
if (cookiesString != null) {
|
||||||
|
@ -173,6 +224,8 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteCookie(String url, String name, String domain, String path, final MethodChannel.Result result) {
|
public static void deleteCookie(String url, String name, String domain, String path, final MethodChannel.Result result) {
|
||||||
|
cookieManager = getCookieManager();
|
||||||
|
if (cookieManager == null) return;
|
||||||
|
|
||||||
String cookieValue = name + "=; Path=" + path + "; Domain=" + domain + "; Max-Age=-1;";
|
String cookieValue = name + "=; Path=" + path + "; Domain=" + domain + "; Max-Age=-1;";
|
||||||
|
|
||||||
|
@ -196,6 +249,8 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteCookies(String url, String domain, String path, final MethodChannel.Result result) {
|
public static void deleteCookies(String url, String domain, String path, final MethodChannel.Result result) {
|
||||||
|
cookieManager = getCookieManager();
|
||||||
|
if (cookieManager == null) return;
|
||||||
|
|
||||||
CookieSyncManager cookieSyncMngr = null;
|
CookieSyncManager cookieSyncMngr = null;
|
||||||
|
|
||||||
|
@ -228,6 +283,8 @@ public class MyCookieManager implements MethodChannel.MethodCallHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteAllCookies(final MethodChannel.Result result) {
|
public static void deleteAllCookies(final MethodChannel.Result result) {
|
||||||
|
cookieManager = getCookieManager();
|
||||||
|
if (cookieManager == null) return;
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
cookieManager.removeAllCookies(new ValueCallback<Boolean>() {
|
cookieManager.removeAllCookies(new ValueCallback<Boolean>() {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package com.pichillilorenzo.flutter_inappwebview;
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.res.AssetManager;
|
||||||
import android.net.http.SslCertificate;
|
import android.net.http.SslCertificate;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
@ -9,6 +10,12 @@ import android.os.Looper;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -22,6 +29,7 @@ import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
@ -234,4 +242,20 @@ public class Util {
|
||||||
|
|
||||||
return x509Certificate;
|
return x509Certificate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
|
||||||
|
public static String JSONStringify(Object value) {
|
||||||
|
if (value == null) {
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
if (value instanceof Map) {
|
||||||
|
return new JSONObject((Map<String, Object>) value).toString();
|
||||||
|
} else if (value instanceof List) {
|
||||||
|
return new JSONArray((List<Object>) value).toString();
|
||||||
|
} else if (value instanceof String) {
|
||||||
|
return JSONObject.quote((String) value);
|
||||||
|
} else {
|
||||||
|
return JSONObject.wrap(value).toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-06 02:03:14.260971","version":"1.26.0-18.0.pre.90"}
|
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"android":[{"name":"device_info","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/device_info-2.0.0-nullsafety.2/","dependencies":[]},{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":["device_info"]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/.pub-cache/git/plugins-16f3281b04b0db12e609352b1c9544901392e428/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.27/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.1+1/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.4/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+8/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.0.1+2/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.0.4+3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"device_info","dependencies":[]},{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":["device_info"]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-02-07 16:00:15.712688","version":"1.26.0-18.0.pre.90"}
|
|
@ -18,10 +18,22 @@
|
||||||
EDC1147F21735BC200D2247A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
EDC1147F21735BC200D2247A /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
6174FE1725CEB74E00A5020C /* Embed App Extensions */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 13;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Embed App Extensions";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
26ADC1E5EAF404A509D528C5 /* Pods_Runner_copy.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner_copy.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
61FF72FF23634CA10069C557 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
61FF72FF23634CA10069C557 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
|
||||||
61FF730123634DD10069C557 /* flutter_downloader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_downloader.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
61FF730123634DD10069C557 /* flutter_downloader.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = flutter_downloader.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -59,7 +71,6 @@
|
||||||
children = (
|
children = (
|
||||||
61FF730123634DD10069C557 /* flutter_downloader.framework */,
|
61FF730123634DD10069C557 /* flutter_downloader.framework */,
|
||||||
61FF72FF23634CA10069C557 /* libsqlite3.tbd */,
|
61FF72FF23634CA10069C557 /* libsqlite3.tbd */,
|
||||||
26ADC1E5EAF404A509D528C5 /* Pods_Runner_copy.framework */,
|
|
||||||
B0FC2CF7A6002799890B3102 /* Pods_Runner.framework */,
|
B0FC2CF7A6002799890B3102 /* Pods_Runner.framework */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
|
@ -141,6 +152,7 @@
|
||||||
97C146EC1CF9000F007C117D /* Resources */,
|
97C146EC1CF9000F007C117D /* Resources */,
|
||||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||||
903A9F2558754FA70D0A7EA8 /* [CP] Embed Pods Frameworks */,
|
903A9F2558754FA70D0A7EA8 /* [CP] Embed Pods Frameworks */,
|
||||||
|
6174FE1725CEB74E00A5020C /* Embed App Extensions */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
|
@ -157,6 +169,7 @@
|
||||||
97C146E61CF9000F007C117D /* Project object */ = {
|
97C146E61CF9000F007C117D /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
LastSwiftUpdateCheck = 1240;
|
||||||
LastUpgradeCheck = 1110;
|
LastUpgradeCheck = 1110;
|
||||||
ORGANIZATIONNAME = "The Chromium Authors";
|
ORGANIZATIONNAME = "The Chromium Authors";
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
</array>
|
</array>
|
||||||
<key>NSBonjourServices</key>
|
<key>NSBonjourServices</key>
|
||||||
<array>
|
<array>
|
||||||
<string></string>
|
<string>_dartobservatory._tcp</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
|
|
@ -827,6 +827,19 @@ let onWindowBlurEventJS = """
|
||||||
})();
|
})();
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
let callAsyncJavaScriptBelowIOS14WrapperJS = """
|
||||||
|
(function(obj) {
|
||||||
|
(async function($FUNCTION_ARGUMENT_NAMES) {
|
||||||
|
$FUNCTION_BODY
|
||||||
|
})($FUNCTION_ARGUMENT_VALUES).then(function(value) {
|
||||||
|
window.webkit.messageHandlers['onCallAsyncJavaScriptResultBelowIOS14Received'].postMessage({'value': value, 'error': null, 'resultUuid': '$RESULT_UUID'});
|
||||||
|
}).catch(function(error) {
|
||||||
|
window.webkit.messageHandlers['onCallAsyncJavaScriptResultBelowIOS14Received'].postMessage({'value': null, 'error': error, 'resultUuid': '$RESULT_UUID'});
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
})($FUNCTION_ARGUMENTS_OBJ);
|
||||||
|
"""
|
||||||
|
|
||||||
var SharedLastTouchPointTimestamp: [InAppWebView: Int64] = [:]
|
var SharedLastTouchPointTimestamp: [InAppWebView: Int64] = [:]
|
||||||
|
|
||||||
public class WebViewTransport: NSObject {
|
public class WebViewTransport: NSObject {
|
||||||
|
@ -879,6 +892,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
|
|
||||||
var userScriptsContentWorlds: [String] = ["page"]
|
var userScriptsContentWorlds: [String] = ["page"]
|
||||||
|
|
||||||
|
var callAsyncJavaScriptBelowIOS14Results: [String:FlutterResult] = [:]
|
||||||
|
|
||||||
init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, contextMenu: [String: Any]?, channel: FlutterMethodChannel?) {
|
init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, contextMenu: [String: Any]?, channel: FlutterMethodChannel?) {
|
||||||
super.init(frame: frame, configuration: configuration)
|
super.init(frame: frame, configuration: configuration)
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
|
@ -1333,6 +1348,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
|
|
||||||
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||||
configuration.userContentController.addUserScript(printJSScript)
|
configuration.userContentController.addUserScript(printJSScript)
|
||||||
|
|
||||||
|
configuration.userContentController.removeScriptMessageHandler(forName: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
||||||
|
configuration.userContentController.add(self, name: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2113,10 +2131,79 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func evaluateJavascript(source: String, contentWorldName: String?, result: FlutterResult?) {
|
public func evaluateJavascript(source: String, contentWorldName: String?, result: @escaping FlutterResult) {
|
||||||
injectDeferredObject(source: source, contentWorldName: contentWorldName, withWrapper: nil, result: result)
|
injectDeferredObject(source: source, contentWorldName: contentWorldName, withWrapper: nil, result: result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 10.3, *)
|
||||||
|
public func callAsyncJavaScript(functionBody: String, arguments: [String:Any], contentWorldName: String?, result: @escaping FlutterResult) {
|
||||||
|
var jsToInject = functionBody
|
||||||
|
if #available(iOS 14.0, *) {
|
||||||
|
var contentWorld = WKContentWorld.page
|
||||||
|
if let contentWorldName = contentWorldName {
|
||||||
|
contentWorld = getContentWorld(name: contentWorldName)
|
||||||
|
if !userScriptsContentWorlds.contains(contentWorldName) {
|
||||||
|
userScriptsContentWorlds.append(contentWorldName)
|
||||||
|
addSharedPluginUserScriptsInContentWorld(contentWorldName: contentWorldName)
|
||||||
|
// Add only the first time all the plugin user scripts needed.
|
||||||
|
// In the next page load, it will use the WKUserScripts loaded
|
||||||
|
jsToInject = getAllPluginUserScriptMergedJS() + "\n" + jsToInject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callAsyncJavaScript(jsToInject, arguments: arguments, in: nil, in: contentWorld) { (evalResult) in
|
||||||
|
var body: [String: Any?] = [
|
||||||
|
"value": nil,
|
||||||
|
"error": nil
|
||||||
|
]
|
||||||
|
|
||||||
|
switch (evalResult) {
|
||||||
|
case .success(let value):
|
||||||
|
body["value"] = value
|
||||||
|
break
|
||||||
|
case .failure(let error):
|
||||||
|
body["error"] = error
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
result(body)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let resultUuid = NSUUID().uuidString
|
||||||
|
callAsyncJavaScriptBelowIOS14Results[resultUuid] = result
|
||||||
|
|
||||||
|
var functionArgumentNamesList: [String] = []
|
||||||
|
var functionArgumentValuesList: [String] = []
|
||||||
|
let keys = arguments.keys
|
||||||
|
keys.forEach { (key) in
|
||||||
|
functionArgumentNamesList.append(key)
|
||||||
|
functionArgumentValuesList.append("obj.\(key)")
|
||||||
|
}
|
||||||
|
|
||||||
|
let functionArgumentNames = functionArgumentNamesList.joined(separator: ", ")
|
||||||
|
let functionArgumentValues = functionArgumentValuesList.joined(separator: ", ")
|
||||||
|
|
||||||
|
jsToInject = callAsyncJavaScriptBelowIOS14WrapperJS
|
||||||
|
.replacingOccurrences(of: "$FUNCTION_ARGUMENT_NAMES", with: functionArgumentNames)
|
||||||
|
.replacingOccurrences(of: "$FUNCTION_ARGUMENT_VALUES", with: functionArgumentValues)
|
||||||
|
.replacingOccurrences(of: "$FUNCTION_ARGUMENTS_OBJ", with: JSONStringify(value: arguments))
|
||||||
|
.replacingOccurrences(of: "$FUNCTION_BODY", with: jsToInject)
|
||||||
|
.replacingOccurrences(of: "$RESULT_UUID", with: resultUuid)
|
||||||
|
|
||||||
|
evaluateJavaScript(jsToInject) { (value, error) in
|
||||||
|
if error != nil {
|
||||||
|
let userInfo = (error! as NSError).userInfo
|
||||||
|
self.onConsoleMessage(message:
|
||||||
|
userInfo["WKJavaScriptExceptionMessage"] as? String ??
|
||||||
|
userInfo["NSLocalizedDescription"] as? String ??
|
||||||
|
"",
|
||||||
|
messageLevel: 3)
|
||||||
|
result(nil)
|
||||||
|
self.callAsyncJavaScriptBelowIOS14Results.removeValue(forKey: resultUuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func injectJavascriptFileFromUrl(urlFile: String) {
|
public func injectJavascriptFileFromUrl(urlFile: String) {
|
||||||
let jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document);"
|
let jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document);"
|
||||||
injectDeferredObject(source: urlFile, contentWorldName: nil, withWrapper: jsWrapper, result: nil)
|
injectDeferredObject(source: urlFile, contentWorldName: nil, withWrapper: jsWrapper, result: nil)
|
||||||
|
@ -3270,6 +3357,16 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
webView = webViewTransport.webView
|
webView = webViewTransport.webView
|
||||||
}
|
}
|
||||||
webView.onFindResultReceived(activeMatchOrdinal: activeMatchOrdinal, numberOfMatches: numberOfMatches, isDoneCounting: isDoneCounting)
|
webView.onFindResultReceived(activeMatchOrdinal: activeMatchOrdinal, numberOfMatches: numberOfMatches, isDoneCounting: isDoneCounting)
|
||||||
|
} else if message.name == "onCallAsyncJavaScriptResultBelowIOS14Received" {
|
||||||
|
let body = message.body as! [String: Any?]
|
||||||
|
let resultUuid = body["resultUuid"] as! String
|
||||||
|
if let result = callAsyncJavaScriptBelowIOS14Results[resultUuid] {
|
||||||
|
result([
|
||||||
|
"value": body["value"],
|
||||||
|
"error": body["error"]
|
||||||
|
])
|
||||||
|
callAsyncJavaScriptBelowIOS14Results.removeValue(forKey: resultUuid)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3436,6 +3533,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
|
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
|
||||||
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
|
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
|
||||||
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
|
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
|
||||||
|
configuration.userContentController.removeScriptMessageHandler(forName: "onCallAsyncJavaScriptResultBelowIOS14Received")
|
||||||
if #available(iOS 14.0, *) {
|
if #available(iOS 14.0, *) {
|
||||||
configuration.userContentController.removeAllScriptMessageHandlers()
|
configuration.userContentController.removeAllScriptMessageHandlers()
|
||||||
for contentWorldName in userScriptsContentWorlds {
|
for contentWorldName in userScriptsContentWorlds {
|
||||||
|
@ -3469,6 +3567,7 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
if let wId = windowId, InAppWebView.windowWebViews[wId] != nil {
|
if let wId = windowId, InAppWebView.windowWebViews[wId] != nil {
|
||||||
InAppWebView.windowWebViews.removeValue(forKey: wId)
|
InAppWebView.windowWebViews.removeValue(forKey: wId)
|
||||||
}
|
}
|
||||||
|
callAsyncJavaScriptBelowIOS14Results.removeAll()
|
||||||
super.removeFromSuperview()
|
super.removeFromSuperview()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,6 +384,17 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
||||||
webView?.removeAllUserScripts()
|
webView?.removeAllUserScripts()
|
||||||
result(true)
|
result(true)
|
||||||
break
|
break
|
||||||
|
case "callAsyncJavaScript":
|
||||||
|
if webView != nil, #available(iOS 10.3, *) {
|
||||||
|
let functionBody = arguments!["functionBody"] as! String
|
||||||
|
let functionArguments = arguments!["arguments"] as! [String:Any]
|
||||||
|
let contentWorldName = arguments!["contentWorld"] as? String
|
||||||
|
webView!.callAsyncJavaScript(functionBody: functionBody, arguments: functionArguments, contentWorldName: contentWorldName, result: result)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result(nil)
|
||||||
|
}
|
||||||
|
break
|
||||||
default:
|
default:
|
||||||
result(FlutterMethodNotImplemented)
|
result(FlutterMethodNotImplemented)
|
||||||
break
|
break
|
||||||
|
|
|
@ -32,6 +32,7 @@ const javaScriptHandlerForbiddenNames = [
|
||||||
"onPrint",
|
"onPrint",
|
||||||
"onWindowFocus",
|
"onWindowFocus",
|
||||||
"onWindowBlur",
|
"onWindowBlur",
|
||||||
|
"callAsyncJavaScript"
|
||||||
];
|
];
|
||||||
|
|
||||||
///Controls a WebView, such as an [InAppWebView] widget instance, a [HeadlessInAppWebView] instance or [InAppBrowser] WebView instance.
|
///Controls a WebView, such as an [InAppWebView] widget instance, a [HeadlessInAppWebView] instance or [InAppBrowser] WebView instance.
|
||||||
|
@ -2039,6 +2040,46 @@ class InAppWebViewController {
|
||||||
await _channel.invokeMethod('removeAllUserScripts', args);
|
await _channel.invokeMethod('removeAllUserScripts', args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///Executes the specified string as an asynchronous JavaScript function.
|
||||||
|
///
|
||||||
|
///[functionBody] is the JavaScript string to use as the function body.
|
||||||
|
///This method treats the string as an anonymous JavaScript function body and calls it with the named arguments in the arguments parameter.
|
||||||
|
///
|
||||||
|
///[arguments] is a dictionary of the arguments to pass to the function call.
|
||||||
|
///Each key in the dictionary corresponds to the name of an argument in the [functionBody] string,
|
||||||
|
///and the value of that key is the value to use during the evaluation of the code.
|
||||||
|
///Supported value types can be found in the official Flutter docs:
|
||||||
|
///[Platform channel data types support and codecs](https://flutter.dev/docs/development/platform-integration/platform-channels#codec),
|
||||||
|
///except for [Uint8List], [Int32List], [Int64List], and [Float64List] that should be converted into a [List].
|
||||||
|
///All items in an array or dictionary must also be one of the supported types.
|
||||||
|
///
|
||||||
|
///[contentWorld], on iOS, it represents the namespace in which to evaluate the JavaScript [source] code.
|
||||||
|
///Instead, on Android, it will run the [source] code into an iframe.
|
||||||
|
///This parameter doesn’t apply to changes you make to the underlying web content, such as the document’s DOM structure.
|
||||||
|
///Those changes remain visible to all scripts, regardless of which content world you specify.
|
||||||
|
///For more information about content worlds, see [ContentWorld].
|
||||||
|
///Available on iOS 14.0+.
|
||||||
|
///
|
||||||
|
///**NOTE for iOS**: available only on iOS 10.3+.
|
||||||
|
///
|
||||||
|
///**NOTE for Android**: available only on Android 21+.
|
||||||
|
///
|
||||||
|
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/3656441-callasyncjavascript
|
||||||
|
Future<CallAsyncJavaScriptResult?> callAsyncJavaScript({required String functionBody, Map<String, dynamic> arguments = const <String, dynamic>{}, ContentWorld? contentWorld}) async {
|
||||||
|
Map<String, dynamic> args = <String, dynamic>{};
|
||||||
|
args.putIfAbsent('functionBody', () => functionBody);
|
||||||
|
args.putIfAbsent('arguments', () => arguments);
|
||||||
|
args.putIfAbsent('contentWorld', () => contentWorld?.name);
|
||||||
|
var data = await _channel.invokeMethod('callAsyncJavaScript', args);
|
||||||
|
if (data == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||||
|
data = json.decode(data);
|
||||||
|
}
|
||||||
|
return CallAsyncJavaScriptResult(value: data["value"], error: data["error"]);
|
||||||
|
}
|
||||||
|
|
||||||
///Gets the default user agent.
|
///Gets the default user agent.
|
||||||
///
|
///
|
||||||
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
|
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebSettings#getDefaultUserAgent(android.content.Context)
|
||||||
|
|
|
@ -4623,4 +4623,41 @@ class ContentWorld {
|
||||||
///Be careful when manipulating variables in this content world.
|
///Be careful when manipulating variables in this content world.
|
||||||
///If you modify a variable with the same name as one the webpage uses, you may unintentionally disrupt the normal operation of that page.
|
///If you modify a variable with the same name as one the webpage uses, you may unintentionally disrupt the normal operation of that page.
|
||||||
static ContentWorld page = ContentWorld.world(name: "page");
|
static ContentWorld page = ContentWorld.world(name: "page");
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {"name": name};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///Class that represents either a success or a failure, including an associated value in each case for [InAppWebViewController.callAsyncJavaScript].
|
||||||
|
class CallAsyncJavaScriptResult {
|
||||||
|
///It contains the success value.
|
||||||
|
dynamic value;
|
||||||
|
|
||||||
|
///It contains the failure value.
|
||||||
|
dynamic error;
|
||||||
|
|
||||||
|
CallAsyncJavaScriptResult({this.value, this.error});
|
||||||
|
|
||||||
|
Map<String, dynamic> toMap() {
|
||||||
|
return {"value": value, "error": error};
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return this.toMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return toMap().toString();
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue