updated management of Content Worlds, updated evaluateJavascript API

This commit is contained in:
Lorenzo Pichilli 2021-02-06 02:30:15 +01:00
parent 54e027bee0
commit 55242a35f9
9 changed files with 331 additions and 208 deletions

View File

@ -166,7 +166,7 @@ public class ContentBlockerHandler {
@Override
public void run() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(jsScript, (MethodChannel.Result) null);
webView.evaluateJavascript(jsScript, null);
} else {
webView.loadUrl("javascript:" + jsScript);
}

View File

@ -16,6 +16,7 @@ import android.os.Message;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
@ -42,6 +43,7 @@ import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebViewCompat;
import androidx.webkit.WebViewFeature;
@ -56,14 +58,19 @@ import com.pichillilorenzo.flutter_inappwebview.R;
import com.pichillilorenzo.flutter_inappwebview.Shared;
import com.pichillilorenzo.flutter_inappwebview.Util;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import io.flutter.plugin.common.MethodChannel;
@ -106,8 +113,12 @@ final public class InAppWebView extends InputAwareWebView {
public Runnable checkContextMenuShouldBeClosedTask;
public int newCheckContextMenuShouldBeClosedTaskTask = 100; // ms
public Set<String> userScriptsContentWorlds = new HashSet<String>() {{
add("page");
}};
static final String pluginScriptsWrapperJS = "(function(){" +
" if (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" +
" window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded = true;" +
" }" +
@ -187,8 +198,8 @@ final public class InAppWebView extends InputAwareWebView {
" }" +
"})();";
static final String variableForOnLoadResourceJS = "window._flutter_inappwebview_useOnLoadResource";
static final String enableVariableForOnLoadResourceJS = variableForOnLoadResourceJS + " = $PLACEHOLDER_VALUE;";
static final String variableForOnLoadResourceJS = "_flutter_inappwebview_useOnLoadResource";
static final String enableVariableForOnLoadResourceJS = "window." + variableForOnLoadResourceJS + " = $PLACEHOLDER_VALUE;";
static final String resourceObserverJS = "(function() {" +
" var observer = new PerformanceObserver(function(list) {" +
@ -1169,7 +1180,9 @@ final public class InAppWebView extends InputAwareWebView {
String placeholderValue = newOptions.useShouldInterceptAjaxRequest ? "true" : "false";
String sourceJs = InAppWebView.enableVariableForShouldInterceptAjaxRequestJS.replace("$PLACEHOLDER_VALUE", placeholderValue);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
evaluateJavascript(sourceJs, (ValueCallback<String>) null);
for (String contentWorldName : userScriptsContentWorlds) {
evaluateJavascript(sourceJs, contentWorldName, null);
}
} else {
loadUrl("javascript:" + sourceJs);
}
@ -1179,7 +1192,9 @@ final public class InAppWebView extends InputAwareWebView {
String placeholderValue = newOptions.useShouldInterceptFetchRequest ? "true" : "false";
String sourceJs = InAppWebView.enableVariableForShouldInterceptFetchRequestsJS.replace("$PLACEHOLDER_VALUE", placeholderValue);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
evaluateJavascript(sourceJs, (ValueCallback<String>) null);
for (String contentWorldName : userScriptsContentWorlds) {
evaluateJavascript(sourceJs, contentWorldName, null);
}
} else {
loadUrl("javascript:" + sourceJs);
}
@ -1457,7 +1472,7 @@ final public class InAppWebView extends InputAwareWebView {
return (options != null) ? options.getRealOptions(this) : null;
}
public void injectDeferredObject(String source, String jsWrapper, final MethodChannel.Result result) {
public void injectDeferredObject(String source, @Nullable final String contentWorldName, String jsWrapper, final MethodChannel.Result result) {
String scriptToInject = source;
if (jsWrapper != null) {
org.json.JSONArray jsonEsc = new org.json.JSONArray();
@ -1472,39 +1487,58 @@ final public class InAppWebView extends InputAwareWebView {
public void run() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
// This action will have the side-effect of blurring the currently focused element
loadUrl("javascript:" + finalScriptToInject);
loadUrl("javascript:" + finalScriptToInject.replaceAll("[\r\n]+", ""));
result.success("");
} else {
evaluateJavascript(finalScriptToInject, new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
if (result == null)
return;
result.success(s);
if (contentWorldName != null && !contentWorldName.equals("page")) {
String sourceToInject = finalScriptToInject;
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, new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
if (result == null)
return;
result.success(s);
}
});
} else {
evaluateJavascript(finalScriptToInject, new ValueCallback<String>() {
@Override
public void onReceiveValue(String s) {
if (result == null)
return;
result.success(s);
}
});
}
}
}
});
}
public void evaluateJavascript(String source, MethodChannel.Result result) {
injectDeferredObject(source, null, result);
public void evaluateJavascript(String source, String contentWorldName, MethodChannel.Result result) {
injectDeferredObject(source, contentWorldName, null, result);
}
public void injectJavascriptFileFromUrl(String urlFile) {
String jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %s; d.body.appendChild(c); })(document);";
injectDeferredObject(urlFile, jsWrapper, null);
injectDeferredObject(urlFile, null, jsWrapper, null);
}
public void injectCSSCode(String source) {
String jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %s; d.body.appendChild(c); })(document);";
injectDeferredObject(source, jsWrapper, null);
injectDeferredObject(source, null, jsWrapper, null);
}
public void injectCSSFileFromUrl(String urlFile) {
String jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet'; c.type='text/css'; c.href = %s; d.head.appendChild(c); })(document);";
injectDeferredObject(urlFile, jsWrapper, null);
injectDeferredObject(urlFile, null, jsWrapper, null);
}
public HashMap<String, Object> getCopyBackForwardList() {
@ -1999,6 +2033,10 @@ final public class InAppWebView extends InputAwareWebView {
}
public boolean addUserScript(Map<String, Object> userScript) {
String contentWorldName = (String) userScript.get("contentWorld");
if (contentWorldName != null && !userScriptsContentWorlds.contains(contentWorldName)) {
userScriptsContentWorlds.add(contentWorldName);
}
return userScripts.add(userScript);
}
@ -2009,6 +2047,52 @@ final public class InAppWebView extends InputAwareWebView {
public void removeAllUserScripts() {
userScripts.clear();
}
public void resetUserScriptsContentWorlds() {
userScriptsContentWorlds.clear();
userScriptsContentWorlds.add("page");
}
public String prepareAndWrapPluginUserScripts() {
String js = JavaScriptBridgeInterface.callHandlerScriptJS;
js += InAppWebView.consoleLogJS;
if (options.useShouldInterceptAjaxRequest) {
js += InAppWebView.interceptAjaxRequestsJS;
}
if (options.useShouldInterceptFetchRequest) {
js += InAppWebView.interceptFetchRequestsJS;
}
if (options.useOnLoadResource) {
js += InAppWebView.resourceObserverJS;
}
if (!options.useHybridComposition) {
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS;
}
js += InAppWebView.onWindowFocusEventJS;
js += InAppWebView.onWindowBlurEventJS;
js += InAppWebView.printJS;
String jsWrapped = InAppWebView.pluginScriptsWrapperJS
.replace("$PLACEHOLDER_VALUE", js);
return jsWrapped;
}
public String wrapSourceCodeInContentWorld(@Nullable String contentWorldName, String source) {
JSONObject sourceEncoded = new JSONObject();
try {
// encode the javascript source in order to escape special chars and quotes
sourceEncoded.put("source", source);
} catch (JSONException e) {
e.printStackTrace();
}
String sourceWrapped = contentWorldName == null || contentWorldName.equals("page") ? source :
InAppWebView.contentWorldWrapperJS.replace("$CONTENT_WORLD_NAME", contentWorldName)
.replace("$JSON_SOURCE_ENCODED", sourceEncoded.toString());
return sourceWrapped;
}
@Override
public void dispose() {

View File

@ -26,12 +26,8 @@ import androidx.annotation.RequiresApi;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential;
import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase;
import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity;
import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface;
import com.pichillilorenzo.flutter_inappwebview.Util;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.ByteArrayInputStream;
import java.net.MalformedURLException;
import java.net.URI;
@ -166,10 +162,10 @@ public class InAppWebViewClient extends WebViewClient {
private void loadCustomJavaScriptOnPageStarted(WebView view) {
InAppWebView webView = (InAppWebView) view;
String jsPluginScripts = preparePluginUserScripts(webView);
String jsPluginScriptsWrapped = webView.prepareAndWrapPluginUserScripts();
String jsUserScriptsAtDocumentStart = prepareUserScriptsAtDocumentStart(webView);
String js = wrapPluginAndUserScripts(jsPluginScripts, jsUserScriptsAtDocumentStart, null);
String js = wrapPluginAndUserScripts(jsPluginScriptsWrapped, jsUserScriptsAtDocumentStart, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
@ -182,11 +178,11 @@ public class InAppWebViewClient extends WebViewClient {
InAppWebView webView = (InAppWebView) view;
// try to reload also custom scripts if they were not loaded during the onPageStarted event
String jsPluginScripts = preparePluginUserScripts(webView);
String jsPluginScriptsWrapped = webView.prepareAndWrapPluginUserScripts();
String jsUserScriptsAtDocumentStart = prepareUserScriptsAtDocumentStart(webView);
String jsUserScriptsAtDocumentEnd = prepareUserScriptsAtDocumentEnd(webView);
String js = wrapPluginAndUserScripts(jsPluginScripts, jsUserScriptsAtDocumentStart, jsUserScriptsAtDocumentEnd);
String js = wrapPluginAndUserScripts(jsPluginScriptsWrapped, jsUserScriptsAtDocumentStart, jsUserScriptsAtDocumentEnd);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webView.evaluateJavascript(js, (ValueCallback<String>) null);
@ -194,57 +190,25 @@ public class InAppWebViewClient extends WebViewClient {
webView.loadUrl("javascript:" + js.replaceAll("[\r\n]+", ""));
}
}
private String preparePluginUserScripts(InAppWebView webView) {
String js = InAppWebView.consoleLogJS;
js += JavaScriptBridgeInterface.callHandlerScriptJS;
if (webView.options.useShouldInterceptAjaxRequest) {
js += InAppWebView.interceptAjaxRequestsJS;
}
if (webView.options.useShouldInterceptFetchRequest) {
js += InAppWebView.interceptFetchRequestsJS;
}
if (webView.options.useOnLoadResource) {
js += InAppWebView.resourceObserverJS;
}
if (!webView.options.useHybridComposition) {
js += InAppWebView.checkGlobalKeyDownEventToHideContextMenuJS;
}
js += InAppWebView.onWindowFocusEventJS;
js += InAppWebView.onWindowBlurEventJS;
js += InAppWebView.printJS;
return js;
}
private String prepareUserScriptsAtDocumentStart(InAppWebView webView) {
private String prepareUserScripts(InAppWebView webView, int atDocumentInjectionTime) {
StringBuilder js = new StringBuilder();
for (Map<String, Object> userScript : webView.userScripts) {
Integer injectionTime = (Integer) userScript.get("injectionTime");
if (injectionTime == null || injectionTime == 0) {
if ((injectionTime == null && atDocumentInjectionTime == 0) || (injectionTime != null && injectionTime == atDocumentInjectionTime)) {
String source = (String) userScript.get("source");
String contentWorldName = (String) userScript.get("contentWorld");
if (source != null) {
if (contentWorldName != null && !contentWorldName.equals("page")) {
String jsPluginScripts = preparePluginUserScripts(webView);
String jsPluginScripts = webView.prepareAndWrapPluginUserScripts();
source = jsPluginScripts + "\n" + source;
}
JSONObject sourceEncoded = new JSONObject();
try {
// encode the javascript source in order to escape special chars and quotes
sourceEncoded.put("source", source);
} catch (JSONException e) {
e.printStackTrace();
if (contentWorldName != null && !webView.userScriptsContentWorlds.contains(contentWorldName)) {
webView.userScriptsContentWorlds.add(contentWorldName);
}
String sourceWrapped = contentWorldName == null || contentWorldName.equals("page") ? source :
InAppWebView.contentWorldWrapperJS.replace("$CONTENT_WORLD_NAME", contentWorldName)
.replace("$CONTENT_WORLD_NAME", contentWorldName)
.replace("$JSON_SOURCE_ENCODED", sourceEncoded.toString());
if (contentWorldName != null && !contentWorldName.equals("page")) {
String sourceWrapped = webView.wrapSourceCodeInContentWorld(contentWorldName, source);
if (atDocumentInjectionTime == 0 && contentWorldName != null && !contentWorldName.equals("page")) {
// adds another wrapper because sometimes document.body is not ready and it is undefined, causing an error and not adding the iframe element.
sourceWrapped = InAppWebView.documentReadyWrapperJS.replace("$PLACEHOLDER_VALUE", sourceWrapped)
.replace("$PLACEHOLDER_VALUE", sourceWrapped);
@ -258,43 +222,15 @@ public class InAppWebViewClient extends WebViewClient {
return js.toString();
}
private String prepareUserScriptsAtDocumentStart(InAppWebView webView) {
return prepareUserScripts(webView, 0);
}
private String prepareUserScriptsAtDocumentEnd(InAppWebView webView) {
StringBuilder js = new StringBuilder();
for (Map<String, Object> userScript : webView.userScripts) {
Integer injectionTime = (Integer) userScript.get("injectionTime");
if (injectionTime != null && injectionTime == 1) {
String source = (String) userScript.get("source");
String contentWorldName = (String) userScript.get("contentWorld");
if (source != null) {
if (contentWorldName != null && !contentWorldName.equals("page")) {
String jsPluginScripts = preparePluginUserScripts(webView);
source = jsPluginScripts + "\n" + source;
}
JSONObject sourceEncoded = new JSONObject();
try {
// encode the javascript source in order to escape special chars and quotes
sourceEncoded.put("source", source);
} catch (JSONException e) {
e.printStackTrace();
}
String sourceWrapped = contentWorldName == null || contentWorldName.equals("page") ? source :
InAppWebView.contentWorldWrapperJS.replace("$CONTENT_WORLD_NAME", contentWorldName)
.replace("$CONTENT_WORLD_NAME", contentWorldName)
.replace("$JSON_SOURCE_ENCODED", sourceEncoded.toString());
js.append(sourceWrapped);
}
}
}
return js.toString();
return prepareUserScripts(webView, 1);
}
private String wrapPluginAndUserScripts(String jsPluginScripts, @Nullable String jsUserScriptsAtDocumentStart, @Nullable String jsUserScriptsAtDocumentEnd) {
String jsPluginScriptsWrapped = InAppWebView.pluginScriptsWrapperJS
.replace("$PLACEHOLDER_VALUE", jsPluginScripts);
private String wrapPluginAndUserScripts(String jsPluginScriptsWrapped, @Nullable String jsUserScriptsAtDocumentStart, @Nullable String jsUserScriptsAtDocumentEnd) {
String jsUserScriptsAtDocumentStartWrapped = jsUserScriptsAtDocumentStart == null || jsUserScriptsAtDocumentStart.isEmpty() ? "" :
InAppWebView.userScriptsAtDocumentStartWrapperJS.replace("$PLACEHOLDER_VALUE", jsUserScriptsAtDocumentStart);
String jsUserScriptsAtDocumentEndWrapped = jsUserScriptsAtDocumentEnd == null || jsUserScriptsAtDocumentEnd.isEmpty() ? "" :
@ -305,6 +241,7 @@ public class InAppWebViewClient extends WebViewClient {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
final InAppWebView webView = (InAppWebView) view;
webView.resetUserScriptsContentWorlds();
loadCustomJavaScriptOnPageStarted(webView);

View File

@ -74,7 +74,8 @@ public class InAppWebViewMethodHandler implements MethodChannel.MethodCallHandle
case "evaluateJavascript":
if (webView != null) {
String source = (String) call.argument("source");
webView.evaluateJavascript(source, result);
String contentWorldName = (String) call.argument("contentWorld");
webView.evaluateJavascript(source, contentWorldName, result);
}
else {
result.success(null);

View File

@ -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-04 22:05:52.361400","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-06 02:03:14.260971","version":"1.26.0-18.0.pre.90"}

View File

@ -58,7 +58,7 @@ window.\(JAVASCRIPT_BRIDGE_NAME).callHandler = function() {
return new Promise(function(resolve, reject) {
window.\(JAVASCRIPT_BRIDGE_NAME)[_callHandlerID] = resolve;
});
}
};
"""
// the message needs to be concatenated with '' in order to have the same behavior like on Android
@ -269,8 +269,8 @@ function wkwebview_FindNext(forward) {
}
"""
let variableForOnLoadResourceJS = "window._flutter_inappwebview_useOnLoadResource"
let enableVariableForOnLoadResourceJS = "\(variableForOnLoadResourceJS) = $PLACEHOLDER_VALUE;"
let variableForOnLoadResourceJS = "_flutter_inappwebview_useOnLoadResource"
let enableVariableForOnLoadResourceJS = "window.\(variableForOnLoadResourceJS) = $PLACEHOLDER_VALUE;"
let resourceObserverJS = """
(function() {
@ -877,7 +877,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
static var windowWebViews: [Int64:WebViewTransport] = [:]
static var windowAutoincrementId: Int64 = 0;
var userScriptsContentWorlds: [String] = ["defaultClient", "page"]
var userScriptsContentWorlds: [String] = ["page"]
init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, contextMenu: [String: Any]?, channel: FlutterMethodChannel?) {
super.init(frame: frame, configuration: configuration)
@ -1219,60 +1219,88 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
configuration.userContentController.addUserScript(userScriptWindowId)
}
@available(iOS 14.0, *)
func addSharedPluginUserScriptsBetweenContentWorlds(contentWorlds: [WKContentWorld]) -> Void {
for contentWorld in contentWorlds {
let promisePolyfillJSScript = WKUserScript(source: promisePolyfillJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(promisePolyfillJSScript)
func getAllPluginUserScriptMergedJS() -> String {
var allPluginUserScriptMergedJS = promisePolyfillJS + "\n" +
javaScriptBridgeJS + "\n" +
consoleLogJS + "\n" +
printJS + "\n"
if let options = options {
if options.useShouldInterceptAjaxRequest {
allPluginUserScriptMergedJS += interceptAjaxRequestsJS + "\n"
}
let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "callHandler")
let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(consoleLogJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleLog")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleDebug", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleDebug")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleError", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleError")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleInfo", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleInfo")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleWarn")
if let options = options {
if options.useShouldInterceptAjaxRequest {
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(interceptAjaxRequestsJSScript)
}
if options.useShouldInterceptFetchRequest {
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
}
if options.useShouldInterceptFetchRequest {
allPluginUserScriptMergedJS += interceptFetchRequestsJS + "\n"
}
}
return allPluginUserScriptMergedJS
}
func addPluginUserScripts() -> Void {
if #available(iOS 14.0, *) {
let contentWorlds = userScriptsContentWorlds.map { (contentWorldName) -> WKContentWorld in
return getContentWorld(name: contentWorldName)
@available(iOS 14.0, *)
func addSharedPluginUserScriptsInContentWorld(contentWorldName: String) -> Void {
let contentWorld = getContentWorld(name: contentWorldName)
let promisePolyfillJSScript = WKUserScript(source: promisePolyfillJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(promisePolyfillJSScript)
let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "callHandler")
let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(consoleLogJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleLog")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleDebug", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleDebug")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleError", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleError")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleInfo", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleInfo")
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn", contentWorld: contentWorld)
configuration.userContentController.add(self, contentWorld: contentWorld, name: "consoleWarn")
if let options = options {
if options.useShouldInterceptAjaxRequest {
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(interceptAjaxRequestsJSScript)
}
if options.useShouldInterceptFetchRequest {
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
}
}
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false, in: contentWorld)
configuration.userContentController.addUserScript(printJSScript)
}
func addSharedPluginUserScriptsInContentWorlds() -> Void {
if #available(iOS 14.0, *) {
for contentWorldName in userScriptsContentWorlds {
addSharedPluginUserScriptsInContentWorld(contentWorldName: contentWorldName)
}
addSharedPluginUserScriptsBetweenContentWorlds(contentWorlds: contentWorlds)
} else {
let promisePolyfillJSScript = WKUserScript(source: promisePolyfillJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
let promisePolyfillJSScript = WKUserScript(
source: promisePolyfillJS,
injectionTime: .atDocumentStart,
forMainFrameOnly: false)
configuration.userContentController.addUserScript(promisePolyfillJSScript)
let javaScriptBridgeJSScript = WKUserScript(source: javaScriptBridgeJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
let javaScriptBridgeJSScript = WKUserScript(
source: javaScriptBridgeJS,
injectionTime: .atDocumentStart,
forMainFrameOnly: false)
configuration.userContentController.addUserScript(javaScriptBridgeJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "callHandler")
configuration.userContentController.add(self, name: "callHandler")
let consoleLogJSScript = WKUserScript(source: consoleLogJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
let consoleLogJSScript = WKUserScript(
source: consoleLogJS,
injectionTime: .atDocumentStart,
forMainFrameOnly: false)
configuration.userContentController.addUserScript(consoleLogJSScript)
configuration.userContentController.removeScriptMessageHandler(forName: "consoleLog")
configuration.userContentController.add(self, name: "consoleLog")
@ -1287,23 +1315,33 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if let options = options {
if options.useShouldInterceptAjaxRequest {
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
let interceptAjaxRequestsJSScript = WKUserScript(
source: interceptAjaxRequestsJS,
injectionTime: .atDocumentStart,
forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptAjaxRequestsJSScript)
}
if options.useShouldInterceptFetchRequest {
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
let interceptFetchRequestsJSScript = WKUserScript(
source: interceptFetchRequestsJS,
injectionTime: .atDocumentStart,
forMainFrameOnly: false)
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
}
}
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(printJSScript)
}
}
func addPluginUserScripts() -> Void {
addSharedPluginUserScriptsInContentWorlds()
let findElementsAtPointJSScript = WKUserScript(source: findElementsAtPointJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(findElementsAtPointJSScript)
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(printJSScript)
let lastTouchedAnchorOrImageJSScript = WKUserScript(source: lastTouchedAnchorOrImageJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(lastTouchedAnchorOrImageJSScript)
@ -1352,18 +1390,19 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
public func appendUserScript(userScript: [String: Any]) -> Void {
var wkUserScript: WKUserScript?
if #available(iOS 14.0, *), let contentWorldName = userScript["contentWorld"] as? String {
if !userScriptsContentWorlds.contains(contentWorldName) {
userScriptsContentWorlds.append(contentWorldName)
}
let contentWorldName = userScript["contentWorld"] as? String
if contentWorldName != nil, !userScriptsContentWorlds.contains(contentWorldName!) {
userScriptsContentWorlds.append(contentWorldName!)
}
if #available(iOS 14.0, *), let contentWorldName = contentWorldName {
wkUserScript = WKUserScript(source: userScript["source"] as! String,
injectionTime: WKUserScriptInjectionTime.init(rawValue: userScript["injectionTime"] as! Int) ?? .atDocumentStart,
forMainFrameOnly: userScript["iosForMainFrameOnly"] as! Bool,
in: getContentWorld(name: contentWorldName))
} else {
wkUserScript = WKUserScript(source: userScript["source"] as! String,
injectionTime: WKUserScriptInjectionTime.init(rawValue: userScript["injectionTime"] as! Int) ?? .atDocumentStart,
forMainFrameOnly: userScript["iosForMainFrameOnly"] as! Bool)
injectionTime: WKUserScriptInjectionTime.init(rawValue: userScript["injectionTime"] as! Int) ?? .atDocumentStart,
forMainFrameOnly: userScript["iosForMainFrameOnly"] as! Bool)
}
userScripts.append(wkUserScript!)
}
@ -1784,17 +1823,35 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
if newOptionsMap["useOnLoadResource"] != nil && options?.useOnLoadResource != newOptions.useOnLoadResource && newOptions.useOnLoadResource {
let placeholderValue = newOptions.useOnLoadResource ? "true" : "false"
evaluateJavaScript(enableVariableForOnLoadResourceJS.replacingOccurrences(of: "$PLACEHOLDER_VALUE", with: placeholderValue), completionHandler: nil)
let source = enableVariableForOnLoadResourceJS.replacingOccurrences(of: "$PLACEHOLDER_VALUE", with: placeholderValue)
evaluateJavaScript(source, completionHandler: nil)
}
if newOptionsMap["useShouldInterceptAjaxRequest"] != nil && options?.useShouldInterceptAjaxRequest != newOptions.useShouldInterceptAjaxRequest && newOptions.useShouldInterceptAjaxRequest {
let placeholderValue = newOptions.useShouldInterceptAjaxRequest ? "true" : "false"
evaluateJavaScript(enableVariableForShouldInterceptAjaxRequestJS.replacingOccurrences(of: "$PLACEHOLDER_VALUE", with: placeholderValue), completionHandler: nil)
let source = enableVariableForShouldInterceptAjaxRequestJS.replacingOccurrences(of: "$PLACEHOLDER_VALUE", with: placeholderValue)
if #available(iOS 14.0, *) {
for contentWorldName in userScriptsContentWorlds {
let contentWorld = getContentWorld(name: contentWorldName)
evaluateJavaScript(source, in: nil, in: contentWorld, completionHandler: nil)
}
} else {
evaluateJavaScript(source, completionHandler: nil)
}
}
if newOptionsMap["useShouldInterceptFetchRequest"] != nil && options?.useShouldInterceptFetchRequest != newOptions.useShouldInterceptFetchRequest && newOptions.useShouldInterceptFetchRequest {
let placeholderValue = newOptions.useShouldInterceptFetchRequest ? "true" : "false"
evaluateJavaScript(enableVariableForShouldInterceptFetchRequestsJS.replacingOccurrences(of: "$PLACEHOLDER_VALUE", with: placeholderValue), completionHandler: nil)
let source = enableVariableForShouldInterceptAjaxRequestJS.replacingOccurrences(of: "$PLACEHOLDER_VALUE", with: placeholderValue)
if #available(iOS 14.0, *) {
for contentWorldName in userScriptsContentWorlds {
let contentWorld = getContentWorld(name: contentWorldName)
evaluateJavaScript(source, in: nil, in: contentWorld, completionHandler: nil)
}
} else {
evaluateJavaScript(source, completionHandler: nil)
}
}
if newOptionsMap["mediaPlaybackRequiresUserGesture"] != nil && options?.mediaPlaybackRequiresUserGesture != newOptions.mediaPlaybackRequiresUserGesture {
@ -2000,7 +2057,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
}
}
public func injectDeferredObject(source: String, withWrapper jsWrapper: String?, result: FlutterResult?) {
public func injectDeferredObject(source: String, contentWorldName: String?, withWrapper jsWrapper: String?, result: FlutterResult?) {
var jsToInject = source
if let wrapper = jsWrapper {
let jsonData: Data? = try? JSONSerialization.data(withJSONObject: [source], options: [])
@ -2008,42 +2065,71 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
let sourceString: String? = (sourceArrayString! as NSString).substring(with: NSRange(location: 1, length: (sourceArrayString?.count ?? 0) - 2))
jsToInject = String(format: wrapper, sourceString!)
}
evaluateJavaScript(jsToInject, completionHandler: {(value, error) in
if result == nil {
return
if #available(iOS 14.0, *), let contentWorldName = 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
}
if error != nil {
let userInfo = (error! as NSError).userInfo
self.onConsoleMessage(message: userInfo["WKJavaScriptExceptionMessage"] as? String ?? "", messageLevel: 3)
let contentWorld = getContentWorld(name: contentWorldName)
evaluateJavaScript(jsToInject, in: nil, in: contentWorld) { (evalResult) in
guard let result = result else {
return
}
switch (evalResult) {
case .success(let value):
result(value)
return
case .failure(let error):
let userInfo = (error as NSError).userInfo
self.onConsoleMessage(message: userInfo["WKJavaScriptExceptionMessage"] as? String ?? "", messageLevel: 3)
break
}
result(nil)
}
if value == nil {
result!(nil)
return
} else {
evaluateJavaScript(jsToInject) { (value, error) in
guard let result = result else {
return
}
if error != nil {
let userInfo = (error! as NSError).userInfo
self.onConsoleMessage(message: userInfo["WKJavaScriptExceptionMessage"] as? String ?? "", messageLevel: 3)
}
if value == nil {
result(nil)
return
}
result(value)
}
result!(value)
})
}
}
public func evaluateJavascript(source: String, result: FlutterResult?) {
injectDeferredObject(source: source, withWrapper: nil, result: result)
public func evaluateJavascript(source: String, contentWorldName: String?, result: FlutterResult?) {
injectDeferredObject(source: source, contentWorldName: contentWorldName, withWrapper: nil, result: result)
}
public func injectJavascriptFileFromUrl(urlFile: String) {
let jsWrapper = "(function(d) { var c = d.createElement('script'); c.src = %@; d.body.appendChild(c); })(document);"
injectDeferredObject(source: urlFile, withWrapper: jsWrapper, result: nil)
injectDeferredObject(source: urlFile, contentWorldName: nil, withWrapper: jsWrapper, result: nil)
}
public func injectCSSCode(source: String) {
let jsWrapper = "(function(d) { var c = d.createElement('style'); c.innerHTML = %@; d.body.appendChild(c); })(document);"
injectDeferredObject(source: source, withWrapper: jsWrapper, result: nil)
injectDeferredObject(source: source, contentWorldName: nil, withWrapper: jsWrapper, result: nil)
}
public func injectCSSFileFromUrl(urlFile: String) {
let jsWrapper = "(function(d) { var c = d.createElement('link'); c.rel='stylesheet', c.type='text/css'; c.href = %@; d.body.appendChild(c); })(document);"
injectDeferredObject(source: urlFile, withWrapper: jsWrapper, result: nil)
injectDeferredObject(source: urlFile, contentWorldName: nil, withWrapper: jsWrapper, result: nil)
}
public func getCopyBackForwardList() -> [String: Any] {
@ -2181,6 +2267,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
currentURL = url
InAppWebView.credentialsProposed = []
evaluateJavaScript(platformReadyJS, completionHandler: nil)
onLoadStop(url: url?.absoluteString)
if IABController != nil {

View File

@ -30,15 +30,15 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
result( (webView != nil) ? Int(webView!.estimatedProgress * 100) : nil )
break
case "loadUrl":
let url = (arguments!["url"] as? String)!
let headers = (arguments!["headers"] as? [String: String])!
let url = arguments!["url"] as! String
let headers = arguments!["headers"] as! [String: String]
webView?.loadUrl(url: URL(string: url)!, headers: headers)
result(true)
break
case "postUrl":
if webView != nil {
let url = (arguments!["url"] as? String)!
let postData = (arguments!["postData"] as? FlutterStandardTypedData)!
let url = arguments!["url"] as! String
let postData = arguments!["postData"] as! FlutterStandardTypedData
webView!.postUrl(url: URL(string: url)!, postData: postData.data, completionHandler: { () -> Void in
result(true)
})
@ -48,16 +48,16 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
}
break
case "loadData":
let data = (arguments!["data"] as? String)!
let mimeType = (arguments!["mimeType"] as? String)!
let encoding = (arguments!["encoding"] as? String)!
let baseUrl = (arguments!["baseUrl"] as? String)!
let data = arguments!["data"] as! String
let mimeType = arguments!["mimeType"] as! String
let encoding = arguments!["encoding"] as! String
let baseUrl = arguments!["baseUrl"] as! String
webView?.loadData(data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl)
result(true)
break
case "loadFile":
let url = (arguments!["url"] as? String)!
let headers = (arguments!["headers"] as? [String: String])!
let url = arguments!["url"] as! String
let headers = arguments!["headers"] as! [String: String]
do {
try webView?.loadFile(url: url, headers: headers)
@ -70,25 +70,26 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
break
case "evaluateJavascript":
if webView != nil {
let source = (arguments!["source"] as? String)!
webView!.evaluateJavascript(source: source, result: result)
let source = arguments!["source"] as! String
let contentWorldName = arguments!["contentWorld"] as? String
webView!.evaluateJavascript(source: source, contentWorldName: contentWorldName, result: result)
}
else {
result(nil)
}
break
case "injectJavascriptFileFromUrl":
let urlFile = (arguments!["urlFile"] as? String)!
let urlFile = arguments!["urlFile"] as! String
webView?.injectJavascriptFileFromUrl(urlFile: urlFile)
result(true)
break
case "injectCSSCode":
let source = (arguments!["source"] as? String)!
let source = arguments!["source"] as! String
webView?.injectCSSCode(source: source)
result(true)
break
case "injectCSSFileFromUrl":
let urlFile = (arguments!["urlFile"] as? String)!
let urlFile = arguments!["urlFile"] as! String
webView?.injectCSSFileFromUrl(urlFile: urlFile)
result(true)
break
@ -111,12 +112,12 @@ class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
result(webView?.canGoForward ?? false)
break
case "goBackOrForward":
let steps = (arguments!["steps"] as? Int)!
let steps = arguments!["steps"] as! Int
webView?.goBackOrForward(steps: steps)
result(true)
break
case "canGoBackOrForward":
let steps = (arguments!["steps"] as? Int)!
let steps = arguments!["steps"] as! Int
result(webView?.canGoBackOrForward(steps: steps) ?? false)
break
case "stopLoading":

View File

@ -1351,7 +1351,14 @@ class InAppWebViewController {
await _channel.invokeMethod('stopLoading', args);
}
///Evaluates JavaScript code into the WebView and returns the result of the evaluation.
///Evaluates JavaScript [source] code into the WebView and returns the result of the evaluation.
///
///[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 doesnt apply to changes you make to the underlying web content, such as the documents 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**: This method shouldn't be called in the [WebView.onWebViewCreated] or [WebView.onLoadStart] events,
///because, in these events, the [WebView] is not ready to handle it yet.
@ -1360,10 +1367,13 @@ class InAppWebViewController {
///
///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#evaluateJavascript(java.lang.String,%20android.webkit.ValueCallback%3Cjava.lang.String%3E)
///
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript
Future<dynamic> evaluateJavascript({required String source}) async {
///**Official iOS API**:
///- https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript
///- https://developer.apple.com/documentation/webkit/wkwebview/3656442-evaluatejavascript
Future<dynamic> evaluateJavascript({required String source, ContentWorld? contentWorld}) async {
Map<String, dynamic> args = <String, dynamic>{};
args.putIfAbsent('source', () => source);
args.putIfAbsent('contentWorld', () => contentWorld?.name);
var data = await _channel.invokeMethod('evaluateJavascript', args);
if (data != null && defaultTargetPlatform == TargetPlatform.android) data = json.decode(data);
return data;

View File

@ -4599,12 +4599,15 @@ class UserScript {
///Class that represents an object that defines a scope of execution for JavaScript code, and which you use to prevent conflicts between different scripts.
///
///**NOTE for iOS 14.0+**: this class represents the native [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld) class.
///**NOTE for iOS**: available on iOS 14.0+. This class represents the native [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld) class.
///
///**NOTE for Android**: it will create and append an `<iframe>` HTML element with `id` equals to `flutter_inappwebview_[name]` to the webpage's content that contains only the scripts
///in order to define a new scope of execution for JavaScript code. Unfortunately, there isn't any other way to do it.
///For any [ContentWorld], except [ContentWorld.page], if you need to access to the `window` or `document` global Object,
///you need to use `window.top` and `window.top.document` because the code runs inside an `<iframe>`.
///**NOTE for Android**: it will create and append an `<iframe>` HTML element with `id` equals to `flutter_inappwebview_[name]`
///to the webpage's content that contains only the inline `<script>` HTML elements in order to define a new scope of execution for JavaScript code.
///Unfortunately, there isn't any other way to do it.
///There are some limitations:
///- for any [ContentWorld], except [ContentWorld.page] (that is the webpage itself), if you need to access to the `window` or `document` global Object,
///you need to use `window.top` and `window.top.document` because the code runs inside an `<iframe>`;
///- also, the execution of the inline `<script>` could be blocked by the `Content-Security-Policy` header.
class ContentWorld {
///The name of a custom content world.
final String name;