Added mediaType, pageZoom, limitsNavigationsToAppBoundDomains iOS-specific webview options, Added handlesURLScheme iOS-specific webview method, Added ContentWorld class, minor bug fixes
This commit is contained in:
parent
37546a1dc0
commit
a023d34fd9
|
@ -8,7 +8,9 @@
|
|||
- Added `UserScript` and `UserScriptInjectionTime` classes
|
||||
- Added `initialUserScripts` WebView option
|
||||
- Added `addUserScript`, `addUserScripts`, `removeUserScript`, `removeUserScripts`, `removeAllUserScripts` WebView methods
|
||||
- Added `isDirectionalLockEnabled` iOS-specific webview option
|
||||
- Added `isDirectionalLockEnabled`, `mediaType`, `pageZoom`, `limitsNavigationsToAppBoundDomains` iOS-specific webview options
|
||||
- Added `handlesURLScheme` iOS-specific webview method
|
||||
- Added `ContentWorld` class
|
||||
- Updated integration tests
|
||||
- Merge "Upgraded appcompat to 1.2.0-rc-02" [#465](https://github.com/pichillilorenzo/flutter_inappwebview/pull/465) (thanks to [andreidiaconu](https://github.com/andreidiaconu))
|
||||
- Merge "Added missing field 'headers' which returned by WebResourceResponse.toMap()" [#490](https://github.com/pichillilorenzo/flutter_inappwebview/pull/490) (thanks to [Doflatango](https://github.com/Doflatango))
|
||||
|
@ -32,6 +34,7 @@
|
|||
### BREAKING CHANGES
|
||||
|
||||
- Minimum Flutter version required is `1.22.0` and Dart SDK `>=2.12.0-0 <3.0.0`
|
||||
- iOS Xcode version `>= 12`
|
||||
- Removed `debuggingEnabled` WebView option; on Android you should use now the `AndroidInAppWebViewController.setWebContentsDebuggingEnabled(bool debuggingEnabled)` static method; on iOS, debugging is always enabled
|
||||
- `allowUniversalAccessFromFileURLs` and `allowFileAccessFromFileURLs` WebView options moved from Android-specific options to cross-platform options.
|
||||
|
||||
|
|
|
@ -33,7 +33,7 @@ If you need a specific version, please change the **GitHub branch** of this repo
|
|||
- Dart sdk: ">=2.12.0-0 <3.0.0"
|
||||
- Flutter: ">=1.22.0"
|
||||
- Android: `minSdkVersion 17` and add support for `androidx` (see [AndroidX Migration](https://flutter.dev/docs/development/androidx-migration) to migrate an existing app)
|
||||
- iOS: `--ios-language swift`, Xcode version `>= 11`
|
||||
- iOS: `--ios-language swift`, Xcode version `>= 12`
|
||||
|
||||
### IMPORTANT Note for Android and iOS
|
||||
|
||||
|
@ -494,6 +494,7 @@ iOS-specific methods can be called using the `InAppWebViewController.ios` attrib
|
|||
|
||||
* `hasOnlySecureContent`: A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections.
|
||||
* `reloadFromOrigin`: Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible.
|
||||
* `static handlesURLScheme(String urlScheme)`: Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme.
|
||||
|
||||
##### About the JavaScript handler
|
||||
|
||||
|
@ -643,9 +644,12 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
|
|||
* `ignoresViewportScaleLimits`: Set to `true` if you want that the WebView should always allow scaling of the webpage, regardless of the author's intent.
|
||||
* `isFraudulentWebsiteWarningEnabled`: A Boolean value indicating whether warnings should be shown for suspected fraudulent content such as phishing or malware.
|
||||
* `isPagingEnabled`: A Boolean value that determines whether paging is enabled for the scroll view. The default value is `false`.
|
||||
* `isDirectionalLockEnabled`: A Boolean value that determines whether scrolling is disabled in a particular direction.
|
||||
* `isDirectionalLockEnabled`: A Boolean value that determines whether scrolling is disabled in a particular direction. The default value is `false`.
|
||||
* `limitsNavigationsToAppBoundDomains`: A Boolean value that indicates whether the web view limits navigation to pages within the app’s domain. The default value is `false`.
|
||||
* `maximumZoomScale`: A floating-point value that specifies the maximum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
|
||||
* `mediaType`: The media type for the contents of the web view. The default value is `null`.
|
||||
* `minimumZoomScale`: A floating-point value that specifies the minimum scale factor that can be applied to the scroll view's content. The default value is `1.0`.
|
||||
* `pageZoom`: The scale factor by which the web view scales content relative to its bounds. The default value is `1.0`.
|
||||
* `scrollsToTop`: A Boolean value that controls whether the scroll-to-top gesture is enabled. The default value is `true`.
|
||||
* `selectionGranularity`: The level of granularity with which the user can interactively select content in the web view.
|
||||
* `sharedCookiesEnabled`: Set `true` if shared cookies from `HTTPCookieStorage.shared` should used for every load request in the WebView.
|
||||
|
|
|
@ -113,13 +113,45 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
public Runnable checkContextMenuShouldBeClosedTask;
|
||||
public int newCheckContextMenuShouldBeClosedTaskTask = 100; // ms
|
||||
|
||||
static final String scriptsWrapperJS = "(function(){" +
|
||||
" if (window." + JavaScriptBridgeInterface.name + "._scriptsLoaded == null) {" +
|
||||
static final String pluginScriptsWrapperJS = "(function(){" +
|
||||
" if (window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded == null || !window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded) {" +
|
||||
" $PLACEHOLDER_VALUE" +
|
||||
" window." + JavaScriptBridgeInterface.name + "._scriptsLoaded = true;" +
|
||||
" window." + JavaScriptBridgeInterface.name + "._pluginScriptsLoaded = true;" +
|
||||
" }" +
|
||||
"})();";
|
||||
|
||||
static final String userScriptsAtDocumentStartWrapperJS = "if (window." + JavaScriptBridgeInterface.name + "._userScriptsAtDocumentStartLoaded == null || !window." + JavaScriptBridgeInterface.name + "._userScriptsAtDocumentStartLoaded) {" +
|
||||
" $PLACEHOLDER_VALUE" +
|
||||
" window." + JavaScriptBridgeInterface.name + "._userScriptsAtDocumentStartLoaded = true;" +
|
||||
"}";
|
||||
|
||||
static final String userScriptsAtDocumentEndWrapperJS = "if (window." + JavaScriptBridgeInterface.name + "._userScriptsAtDocumentEndLoaded == null || !window." + JavaScriptBridgeInterface.name + "._userScriptsAtDocumentEndLoaded) {" +
|
||||
" $PLACEHOLDER_VALUE" +
|
||||
" window." + JavaScriptBridgeInterface.name + "._userScriptsAtDocumentEndLoaded = true;" +
|
||||
"}";
|
||||
|
||||
static final String contentWorldWrapperJS = "(function() {" +
|
||||
" var iframe = document.getElementById('$CONTENT_WORLD_NAME');" +
|
||||
" if (iframe == null) {" +
|
||||
" iframe = document.createElement('iframe');" +
|
||||
" iframe.id = '$CONTENT_WORLD_NAME';" +
|
||||
" iframe.style = 'display: none; z-index: 0; position: absolute; width: 0px; height: 0px';" +
|
||||
" document.body.append(iframe);" +
|
||||
" }" +
|
||||
" var script = iframe.contentWindow.document.createElement('script');" +
|
||||
" var sourceEncoded = $JSON_SOURCE_ENCODED;" +
|
||||
" script.innerHTML = sourceEncoded.source;" +
|
||||
" iframe.contentWindow.document.body.append(script);" +
|
||||
"})();";
|
||||
|
||||
static final String documentReadyWrapperJS = "if (document.readyState === 'interactive' || document.readyState === 'complete') { " +
|
||||
" $PLACEHOLDER_VALUE" +
|
||||
"} else {" +
|
||||
" document.addEventListener('DOMContentLoaded', function() {" +
|
||||
" $PLACEHOLDER_VALUE" +
|
||||
" });" +
|
||||
"}";
|
||||
|
||||
static final String consoleLogJS = "(function(console) {" +
|
||||
" var oldLogs = {" +
|
||||
" 'log': console.log," +
|
||||
|
@ -147,11 +179,15 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
"})(window.console);";
|
||||
|
||||
static final String printJS = "window.print = function() {" +
|
||||
" window." + JavaScriptBridgeInterface.name + ".callHandler('onPrint', window.location.href);" +
|
||||
" if (window.top == null || window.top === window) {" +
|
||||
" window." + JavaScriptBridgeInterface.name + ".callHandler('onPrint', window.location.href);" +
|
||||
" } else {" +
|
||||
" window.top.print();" +
|
||||
" }" +
|
||||
"};";
|
||||
|
||||
static final String platformReadyJS = "(function() {" +
|
||||
" if (window." + JavaScriptBridgeInterface.name + "._platformReady == null) {" +
|
||||
" if ((window.top == null || window.top === window) && window." + JavaScriptBridgeInterface.name + "._platformReady == null) {" +
|
||||
" window.dispatchEvent(new Event('flutterInAppWebViewPlatformReady'));" +
|
||||
" window." + JavaScriptBridgeInterface.name + "._platformReady = true;" +
|
||||
" }" +
|
||||
|
@ -171,8 +207,8 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
" observer.observe({entryTypes: ['resource']});" +
|
||||
"})();";
|
||||
|
||||
static final String variableForShouldInterceptAjaxRequestJS = "window._flutter_inappwebview_useShouldInterceptAjaxRequest";
|
||||
static final String enableVariableForShouldInterceptAjaxRequestJS = variableForShouldInterceptAjaxRequestJS + " = $PLACEHOLDER_VALUE;";
|
||||
static final String variableForShouldInterceptAjaxRequestJS = "_flutter_inappwebview_useShouldInterceptAjaxRequest";
|
||||
static final String enableVariableForShouldInterceptAjaxRequestJS = "window." + variableForShouldInterceptAjaxRequestJS + " = $PLACEHOLDER_VALUE;";
|
||||
|
||||
static final String interceptAjaxRequestsJS = "(function(ajax) {" +
|
||||
" var send = ajax.prototype.send;" +
|
||||
|
@ -225,7 +261,8 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
" };" +
|
||||
" function handleEvent(e) {" +
|
||||
" var self = this;" +
|
||||
" if (" + variableForShouldInterceptAjaxRequestJS + " == null || " + variableForShouldInterceptAjaxRequestJS + " == true) {" +
|
||||
" var w = (window.top == null || window.top === window) ? window : window.top;" +
|
||||
" if (w." + variableForShouldInterceptAjaxRequestJS + " == null || w." + variableForShouldInterceptAjaxRequestJS + " == true) {" +
|
||||
" var headers = this.getAllResponseHeaders();" +
|
||||
" var responseHeaders = {};" +
|
||||
" if (headers != null) {" +
|
||||
|
@ -276,12 +313,14 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
" };" +
|
||||
" ajax.prototype.send = function(data) {" +
|
||||
" var self = this;" +
|
||||
" if (" + variableForShouldInterceptAjaxRequestJS + " == null || " + variableForShouldInterceptAjaxRequestJS + " == true) {" +
|
||||
" var w = (window.top == null || window.top === window) ? window : window.top;" +
|
||||
" if (w." + variableForShouldInterceptAjaxRequestJS + " == null || w." + variableForShouldInterceptAjaxRequestJS + " == true) {" +
|
||||
" if (!this._flutter_inappwebview_already_onreadystatechange_wrapped) {" +
|
||||
" this._flutter_inappwebview_already_onreadystatechange_wrapped = true;" +
|
||||
" var onreadystatechange = this.onreadystatechange;" +
|
||||
" this.onreadystatechange = function() {" +
|
||||
" if (" + variableForShouldInterceptAjaxRequestJS + " == null || " + variableForShouldInterceptAjaxRequestJS + " == true) {" +
|
||||
" var w = (window.top == null || window.top === window) ? window : window.top;" +
|
||||
" if (w." + variableForShouldInterceptAjaxRequestJS + " == null || w." + variableForShouldInterceptAjaxRequestJS + " == true) {" +
|
||||
" var headers = this.getAllResponseHeaders();" +
|
||||
" var responseHeaders = {};" +
|
||||
" if (headers != null) {" +
|
||||
|
@ -384,8 +423,8 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
" };" +
|
||||
"})(window.XMLHttpRequest);";
|
||||
|
||||
static final String variableForShouldInterceptFetchRequestsJS = "window._flutter_inappwebview_useShouldInterceptFetchRequest";
|
||||
static final String enableVariableForShouldInterceptFetchRequestsJS = variableForShouldInterceptFetchRequestsJS + " = $PLACEHOLDER_VALUE;";
|
||||
static final String variableForShouldInterceptFetchRequestsJS = "_flutter_inappwebview_useShouldInterceptFetchRequest";
|
||||
static final String enableVariableForShouldInterceptFetchRequestsJS = "window." + variableForShouldInterceptFetchRequestsJS + " = $PLACEHOLDER_VALUE;";
|
||||
|
||||
static final String interceptFetchRequestsJS = "(function(fetch) {" +
|
||||
" if (fetch == null) {" +
|
||||
|
@ -454,7 +493,8 @@ final public class InAppWebView extends InputAwareWebView {
|
|||
" return credentials;" +
|
||||
" }" +
|
||||
" window.fetch = async function(resource, init) {" +
|
||||
" if (window." + variableForShouldInterceptFetchRequestsJS + " == null || window." + variableForShouldInterceptFetchRequestsJS + " == true) {" +
|
||||
" var w = (window.top == null || window.top === window) ? window : window.top;" +
|
||||
" if (w." + variableForShouldInterceptFetchRequestsJS + " == null || w." + variableForShouldInterceptFetchRequestsJS + " == true) {" +
|
||||
" var fetchRequest = {" +
|
||||
" url: null," +
|
||||
" method: null," +
|
||||
|
|
|
@ -29,6 +29,9 @@ import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivit
|
|||
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;
|
||||
|
@ -163,11 +166,10 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
private void loadCustomJavaScriptOnPageStarted(WebView view) {
|
||||
InAppWebView webView = (InAppWebView) view;
|
||||
|
||||
String js = preparePluginUserScripts(webView);
|
||||
js += prepareUserScriptsAtDocumentStart(webView);
|
||||
String jsPluginScripts = preparePluginUserScripts(webView);
|
||||
String jsUserScriptsAtDocumentStart = prepareUserScriptsAtDocumentStart(webView);
|
||||
|
||||
js = InAppWebView.scriptsWrapperJS
|
||||
.replace("$PLACEHOLDER_VALUE", js);
|
||||
String js = wrapPluginAndUserScripts(jsPluginScripts, jsUserScriptsAtDocumentStart, null);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webView.evaluateJavascript(js, (ValueCallback<String>) null);
|
||||
|
@ -180,12 +182,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 js = preparePluginUserScripts(webView);
|
||||
js += prepareUserScriptsAtDocumentStart(webView);
|
||||
js += prepareUserScriptsAtDocumentEnd(webView);
|
||||
String jsPluginScripts = preparePluginUserScripts(webView);
|
||||
String jsUserScriptsAtDocumentStart = prepareUserScriptsAtDocumentStart(webView);
|
||||
String jsUserScriptsAtDocumentEnd = prepareUserScriptsAtDocumentEnd(webView);
|
||||
|
||||
js = InAppWebView.scriptsWrapperJS
|
||||
.replace("$PLACEHOLDER_VALUE", js);
|
||||
String js = wrapPluginAndUserScripts(jsPluginScripts, jsUserScriptsAtDocumentStart, jsUserScriptsAtDocumentEnd);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
webView.evaluateJavascript(js, (ValueCallback<String>) null);
|
||||
|
@ -196,7 +197,7 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
|
||||
private String preparePluginUserScripts(InAppWebView webView) {
|
||||
String js = InAppWebView.consoleLogJS;
|
||||
js += JavaScriptBridgeInterface.flutterInAppBroserJSClass;
|
||||
js += JavaScriptBridgeInterface.callHandlerScriptJS;
|
||||
if (webView.options.useShouldInterceptAjaxRequest) {
|
||||
js += InAppWebView.interceptAjaxRequestsJS;
|
||||
}
|
||||
|
@ -223,8 +224,33 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
Integer injectionTime = (Integer) userScript.get("injectionTime");
|
||||
if (injectionTime == null || injectionTime == 0) {
|
||||
String source = (String) userScript.get("source");
|
||||
String contentWorldName = (String) userScript.get("contentWorld");
|
||||
if (source != null) {
|
||||
js.append("(function(){").append(source).append("})();");
|
||||
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());
|
||||
|
||||
if (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);
|
||||
}
|
||||
|
||||
js.append(sourceWrapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -237,10 +263,28 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
|
||||
for (Map<String, Object> userScript : webView.userScripts) {
|
||||
Integer injectionTime = (Integer) userScript.get("injectionTime");
|
||||
if (injectionTime == 1) {
|
||||
if (injectionTime != null && injectionTime == 1) {
|
||||
String source = (String) userScript.get("source");
|
||||
String contentWorldName = (String) userScript.get("contentWorld");
|
||||
if (source != null) {
|
||||
js.append("(function(){").append(source).append("})();");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -248,6 +292,16 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
return js.toString();
|
||||
}
|
||||
|
||||
private String wrapPluginAndUserScripts(String jsPluginScripts, @Nullable String jsUserScriptsAtDocumentStart, @Nullable String jsUserScriptsAtDocumentEnd) {
|
||||
String jsPluginScriptsWrapped = InAppWebView.pluginScriptsWrapperJS
|
||||
.replace("$PLACEHOLDER_VALUE", jsPluginScripts);
|
||||
String jsUserScriptsAtDocumentStartWrapped = jsUserScriptsAtDocumentStart == null || jsUserScriptsAtDocumentStart.isEmpty() ? "" :
|
||||
InAppWebView.userScriptsAtDocumentStartWrapperJS.replace("$PLACEHOLDER_VALUE", jsUserScriptsAtDocumentStart);
|
||||
String jsUserScriptsAtDocumentEndWrapped = jsUserScriptsAtDocumentEnd == null || jsUserScriptsAtDocumentEnd.isEmpty() ? "" :
|
||||
InAppWebView.userScriptsAtDocumentEndWrapperJS.replace("$PLACEHOLDER_VALUE", jsUserScriptsAtDocumentEnd);
|
||||
return jsPluginScriptsWrapped + "\n" + jsUserScriptsAtDocumentStartWrapped + "\n" + jsUserScriptsAtDocumentEndWrapped;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||
final InAppWebView webView = (InAppWebView) view;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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-03 15:14:14.166914","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-04 01:10:32.198308","version":"1.26.0-18.0.pre.90"}
|
|
@ -84,6 +84,9 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
initialUrl: "https://flutter.dev/",
|
||||
// initialFile: "assets/index.html",
|
||||
initialHeaders: {},
|
||||
initialUserScripts: UnmodifiableListView<UserScript>([
|
||||
|
||||
]),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(
|
||||
useShouldOverrideUrlLoading: false,
|
||||
|
@ -143,7 +146,7 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
});
|
||||
},
|
||||
onConsoleMessage: (controller, consoleMessage) {
|
||||
// print(consoleMessage);
|
||||
print(consoleMessage);
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
@ -8,14 +8,14 @@ import 'package:flutter_inappwebview_example/chrome_safari_browser_example.scree
|
|||
import 'package:flutter_inappwebview_example/headless_in_app_webview.screen.dart';
|
||||
import 'package:flutter_inappwebview_example/in_app_webiew_example.screen.dart';
|
||||
import 'package:flutter_inappwebview_example/in_app_browser_example.screen.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
// import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
// InAppLocalhostServer localhostServer = new InAppLocalhostServer();
|
||||
|
||||
Future main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Permission.camera.request();
|
||||
await Permission.microphone.request();
|
||||
// await Permission.camera.request();
|
||||
// await Permission.microphone.request();
|
||||
if (Platform.isAndroid) {
|
||||
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
||||
}
|
||||
|
|
|
@ -285,8 +285,8 @@ let resourceObserverJS = """
|
|||
})();
|
||||
"""
|
||||
|
||||
let variableForShouldInterceptAjaxRequestJS = "window._flutter_inappwebview_useShouldInterceptAjaxRequest"
|
||||
let enableVariableForShouldInterceptAjaxRequestJS = "\(variableForShouldInterceptAjaxRequestJS) = $PLACEHOLDER_VALUE;"
|
||||
let variableForShouldInterceptAjaxRequestJS = "_flutter_inappwebview_useShouldInterceptAjaxRequest"
|
||||
let enableVariableForShouldInterceptAjaxRequestJS = "window.\(variableForShouldInterceptAjaxRequestJS) = $PLACEHOLDER_VALUE;"
|
||||
|
||||
let interceptAjaxRequestsJS = """
|
||||
(function(ajax) {
|
||||
|
@ -340,7 +340,7 @@ let interceptAjaxRequestsJS = """
|
|||
};
|
||||
function handleEvent(e) {
|
||||
var self = this;
|
||||
if (\(variableForShouldInterceptAjaxRequestJS) == null || \(variableForShouldInterceptAjaxRequestJS) == true) {
|
||||
if (window.\(variableForShouldInterceptAjaxRequestJS) == null || window.\(variableForShouldInterceptAjaxRequestJS) == true) {
|
||||
var headers = this.getAllResponseHeaders();
|
||||
var responseHeaders = {};
|
||||
if (headers != null) {
|
||||
|
@ -391,12 +391,12 @@ let interceptAjaxRequestsJS = """
|
|||
};
|
||||
ajax.prototype.send = function(data) {
|
||||
var self = this;
|
||||
if (\(variableForShouldInterceptAjaxRequestJS) == null || \(variableForShouldInterceptAjaxRequestJS) == true) {
|
||||
if (window.\(variableForShouldInterceptAjaxRequestJS) == null || window.\(variableForShouldInterceptAjaxRequestJS) == true) {
|
||||
if (!this._flutter_inappwebview_already_onreadystatechange_wrapped) {
|
||||
this._flutter_inappwebview_already_onreadystatechange_wrapped = true;
|
||||
var onreadystatechange = this.onreadystatechange;
|
||||
this.onreadystatechange = function() {
|
||||
if (\(variableForShouldInterceptAjaxRequestJS) == null || \(variableForShouldInterceptAjaxRequestJS) == true) {
|
||||
if (window.\(variableForShouldInterceptAjaxRequestJS) == null || window.\(variableForShouldInterceptAjaxRequestJS) == true) {
|
||||
var headers = this.getAllResponseHeaders();
|
||||
var responseHeaders = {};
|
||||
if (headers != null) {
|
||||
|
@ -500,8 +500,8 @@ let interceptAjaxRequestsJS = """
|
|||
})(window.XMLHttpRequest);
|
||||
"""
|
||||
|
||||
let variableForShouldInterceptFetchRequestsJS = "window._flutter_inappwebview_useShouldInterceptFetchRequest"
|
||||
let enableVariableForShouldInterceptFetchRequestsJS = "\(variableForShouldInterceptFetchRequestsJS) = $PLACEHOLDER_VALUE;"
|
||||
let variableForShouldInterceptFetchRequestsJS = "_flutter_inappwebview_useShouldInterceptFetchRequest"
|
||||
let enableVariableForShouldInterceptFetchRequestsJS = "window.\(variableForShouldInterceptFetchRequestsJS) = $PLACEHOLDER_VALUE;"
|
||||
|
||||
let interceptFetchRequestsJS = """
|
||||
(function(fetch) {
|
||||
|
@ -763,13 +763,13 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = null;
|
|||
while (target) {
|
||||
if (target.tagName === 'IMG') {
|
||||
var img = target;
|
||||
window.flutter_inappwebview._lastImageTouched = {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = {
|
||||
src: img.src
|
||||
};
|
||||
var parent = img.parentNode;
|
||||
while (parent) {
|
||||
if (parent.tagName === 'A') {
|
||||
window.flutter_inappwebview._lastAnchorOrImageTouched = {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._lastAnchorOrImageTouched = {
|
||||
title: parent.textContent,
|
||||
url: parent.href,
|
||||
src: img.src
|
||||
|
@ -784,8 +784,8 @@ window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = null;
|
|||
var images = link.getElementsByTagName('img');
|
||||
var img = (images.length > 0) ? images[0] : null;
|
||||
var imgSrc = (img != null) ? img.src : null;
|
||||
window.flutter_inappwebview._lastImageTouched = (img != null) ? {src: img.src} : window.flutter_inappwebview._lastImageTouched;
|
||||
window.flutter_inappwebview._lastAnchorOrImageTouched = {
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched = (img != null) ? {src: img.src} : window.\(JAVASCRIPT_BRIDGE_NAME)._lastImageTouched;
|
||||
window.\(JAVASCRIPT_BRIDGE_NAME)._lastAnchorOrImageTouched = {
|
||||
title: link.textContent,
|
||||
url: link.href,
|
||||
src: imgSrc
|
||||
|
@ -877,6 +877,8 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
static var windowWebViews: [Int64:WebViewTransport] = [:]
|
||||
static var windowAutoincrementId: Int64 = 0;
|
||||
|
||||
var userScriptsContentWorlds: [String] = ["defaultClient", "page"]
|
||||
|
||||
init(frame: CGRect, configuration: WKWebViewConfiguration, IABController: InAppBrowserWebViewController?, contextMenu: [String: Any]?, channel: FlutterMethodChannel?) {
|
||||
super.init(frame: frame, configuration: configuration)
|
||||
self.channel = channel
|
||||
|
@ -1059,7 +1061,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
}
|
||||
|
||||
public func prepare() {
|
||||
|
||||
self.scrollView.addGestureRecognizer(self.longPressRecognizer!)
|
||||
|
||||
addObserver(self,
|
||||
|
@ -1156,6 +1157,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
scrollView.maximumZoomScale = CGFloat(options.maximumZoomScale)
|
||||
scrollView.minimumZoomScale = CGFloat(options.minimumZoomScale)
|
||||
|
||||
if #available(iOS 14.0, *) {
|
||||
mediaType = options.mediaType
|
||||
pageZoom = CGFloat(options.pageZoom)
|
||||
}
|
||||
|
||||
// debugging is always enabled for iOS,
|
||||
// there isn't any option to set about it such as on Android.
|
||||
|
||||
|
@ -1182,13 +1188,17 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
}
|
||||
|
||||
configuration.preferences.javaScriptCanOpenWindowsAutomatically = options.javaScriptCanOpenWindowsAutomatically
|
||||
configuration.preferences.javaScriptEnabled = options.javaScriptEnabled
|
||||
configuration.preferences.minimumFontSize = CGFloat(options.minimumFontSize)
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
configuration.preferences.isFraudulentWebsiteWarningEnabled = options.isFraudulentWebsiteWarningEnabled
|
||||
configuration.defaultWebpagePreferences.preferredContentMode = WKWebpagePreferences.ContentMode(rawValue: options.preferredContentMode)!
|
||||
}
|
||||
|
||||
configuration.preferences.javaScriptEnabled = options.javaScriptEnabled
|
||||
if #available(iOS 14.0, *) {
|
||||
configuration.defaultWebpagePreferences.allowsContentJavaScript = options.javaScriptEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1209,22 +1219,51 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
configuration.userContentController.addUserScript(userScriptWindowId)
|
||||
}
|
||||
|
||||
func addPluginUserScripts() -> Void {
|
||||
if let options = options {
|
||||
@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)
|
||||
|
||||
let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||
configuration.userContentController.addUserScript(originalViewPortMetaTagContentJSScript)
|
||||
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")
|
||||
|
||||
if !options.supportZoom {
|
||||
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'); document.getElementsByTagName('head')[0].appendChild(meta);"
|
||||
let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||
configuration.userContentController.addUserScript(userScript)
|
||||
} else if options.enableViewportScale {
|
||||
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
|
||||
let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||
configuration.userContentController.addUserScript(userScript)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addPluginUserScripts() -> Void {
|
||||
if #available(iOS 14.0, *) {
|
||||
let contentWorlds = userScriptsContentWorlds.map { (contentWorldName) -> WKContentWorld in
|
||||
return getContentWorld(name: contentWorldName)
|
||||
}
|
||||
addSharedPluginUserScriptsBetweenContentWorlds(contentWorlds: contentWorlds)
|
||||
} else {
|
||||
let promisePolyfillJSScript = WKUserScript(source: promisePolyfillJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(promisePolyfillJSScript)
|
||||
|
||||
|
@ -1246,40 +1285,57 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
configuration.userContentController.removeScriptMessageHandler(forName: "consoleWarn")
|
||||
configuration.userContentController.add(self, name: "consoleWarn")
|
||||
|
||||
let findElementsAtPointJSScript = WKUserScript(source: findElementsAtPointJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(findElementsAtPointJSScript)
|
||||
if let options = options {
|
||||
if options.useShouldInterceptAjaxRequest {
|
||||
let interceptAjaxRequestsJSScript = WKUserScript(source: interceptAjaxRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(interceptAjaxRequestsJSScript)
|
||||
}
|
||||
|
||||
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(printJSScript)
|
||||
if options.useShouldInterceptFetchRequest {
|
||||
let interceptFetchRequestsJSScript = WKUserScript(source: interceptFetchRequestsJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let lastTouchedAnchorOrImageJSScript = WKUserScript(source: lastTouchedAnchorOrImageJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(lastTouchedAnchorOrImageJSScript)
|
||||
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)
|
||||
|
||||
let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(findTextHighlightJSScript)
|
||||
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
|
||||
configuration.userContentController.add(self, name: "onFindResultReceived")
|
||||
|
||||
let onWindowFocusEventJSScript = WKUserScript(source: onWindowFocusEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(onWindowFocusEventJSScript)
|
||||
|
||||
let onWindowBlurEventJSScript = WKUserScript(source: onWindowBlurEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(onWindowBlurEventJSScript)
|
||||
|
||||
if let options = options {
|
||||
let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||
configuration.userContentController.addUserScript(originalViewPortMetaTagContentJSScript)
|
||||
|
||||
if !options.supportZoom {
|
||||
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'); document.getElementsByTagName('head')[0].appendChild(meta);"
|
||||
let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||
configuration.userContentController.addUserScript(userScript)
|
||||
} else if options.enableViewportScale {
|
||||
let jscript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta);"
|
||||
let userScript = WKUserScript(source: jscript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
|
||||
configuration.userContentController.addUserScript(userScript)
|
||||
}
|
||||
|
||||
if options.useOnLoadResource {
|
||||
let resourceObserverJSScript = WKUserScript(source: resourceObserverJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(resourceObserverJSScript)
|
||||
}
|
||||
|
||||
let findTextHighlightJSScript = WKUserScript(source: findTextHighlightJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(findTextHighlightJSScript)
|
||||
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
|
||||
configuration.userContentController.add(self, name: "onFindResultReceived")
|
||||
|
||||
let onWindowFocusEventJSScript = WKUserScript(source: onWindowFocusEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(onWindowFocusEventJSScript)
|
||||
|
||||
let onWindowBlurEventJSScript = WKUserScript(source: onWindowBlurEventJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
|
||||
configuration.userContentController.addUserScript(onWindowBlurEventJSScript)
|
||||
|
||||
if options.useShouldInterceptAjaxRequest {
|
||||
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)
|
||||
configuration.userContentController.addUserScript(interceptFetchRequestsJSScript)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1295,10 +1351,21 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
}
|
||||
|
||||
public func appendUserScript(userScript: [String: Any]) -> Void {
|
||||
let wkUserScript = WKUserScript(source: userScript["source"] as! String,
|
||||
injectionTime: WKUserScriptInjectionTime.init(rawValue: userScript["injectionTime"] as! Int) ?? .atDocumentStart,
|
||||
forMainFrameOnly: userScript["iosForMainFrameOnly"] as! Bool)
|
||||
userScripts.append(wkUserScript)
|
||||
var wkUserScript: WKUserScript?
|
||||
if #available(iOS 14.0, *), let contentWorldName = userScript["contentWorld"] as? String {
|
||||
if !userScriptsContentWorlds.contains(contentWorldName) {
|
||||
userScriptsContentWorlds.append(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)
|
||||
}
|
||||
userScripts.append(wkUserScript!)
|
||||
}
|
||||
|
||||
public func appendUserScripts(wkUserScripts: [WKUserScript]) -> Void {
|
||||
|
@ -1331,6 +1398,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
addPluginUserScripts()
|
||||
}
|
||||
|
||||
@available(iOS 14.0, *)
|
||||
func getContentWorld(name: String) -> WKContentWorld {
|
||||
switch name {
|
||||
case "defaultClient":
|
||||
return WKContentWorld.defaultClient
|
||||
case "page":
|
||||
return WKContentWorld.page
|
||||
default:
|
||||
return WKContentWorld.world(name: name)
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 10.0, *)
|
||||
static public func getDataDetectorType(type: String) -> WKDataDetectorTypes {
|
||||
switch type {
|
||||
|
@ -1478,6 +1557,10 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 14.0, *) {
|
||||
configuration.limitsNavigationsToAppBoundDomains = options.limitsNavigationsToAppBoundDomains
|
||||
}
|
||||
}
|
||||
|
||||
return configuration
|
||||
|
@ -1739,10 +1822,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
configuration.preferences.javaScriptCanOpenWindowsAutomatically = newOptions.javaScriptCanOpenWindowsAutomatically
|
||||
}
|
||||
|
||||
if newOptionsMap["javaScriptEnabled"] != nil && options?.javaScriptEnabled != newOptions.javaScriptEnabled {
|
||||
configuration.preferences.javaScriptEnabled = newOptions.javaScriptEnabled
|
||||
}
|
||||
|
||||
if newOptionsMap["minimumFontSize"] != nil && options?.minimumFontSize != newOptions.minimumFontSize {
|
||||
configuration.preferences.minimumFontSize = CGFloat(newOptions.minimumFontSize)
|
||||
}
|
||||
|
@ -1848,6 +1927,27 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
clearCache()
|
||||
}
|
||||
|
||||
if newOptionsMap["javaScriptEnabled"] != nil && options?.javaScriptEnabled != newOptions.javaScriptEnabled {
|
||||
configuration.preferences.javaScriptEnabled = newOptions.javaScriptEnabled
|
||||
}
|
||||
if #available(iOS 14.0, *) {
|
||||
if options?.mediaType != newOptions.mediaType {
|
||||
mediaType = newOptions.mediaType
|
||||
}
|
||||
|
||||
if newOptionsMap["pageZoom"] != nil && options?.pageZoom != newOptions.pageZoom {
|
||||
pageZoom = CGFloat(newOptions.pageZoom)
|
||||
}
|
||||
|
||||
if newOptionsMap["limitsNavigationsToAppBoundDomains"] != nil && options?.limitsNavigationsToAppBoundDomains != newOptions.limitsNavigationsToAppBoundDomains {
|
||||
configuration.limitsNavigationsToAppBoundDomains = newOptions.limitsNavigationsToAppBoundDomains
|
||||
}
|
||||
|
||||
if newOptionsMap["javaScriptEnabled"] != nil && options?.javaScriptEnabled != newOptions.javaScriptEnabled {
|
||||
configuration.defaultWebpagePreferences.allowsContentJavaScript = newOptions.javaScriptEnabled
|
||||
}
|
||||
}
|
||||
|
||||
if #available(iOS 11.0, *), newOptionsMap["contentBlockers"] != nil {
|
||||
configuration.userContentController.removeAllContentRuleLists()
|
||||
let contentBlockers = newOptions.contentBlockers
|
||||
|
@ -1870,11 +1970,9 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
}
|
||||
}
|
||||
|
||||
self.options = newOptions
|
||||
scrollView.isScrollEnabled = !(newOptions.disableVerticalScroll && newOptions.disableHorizontalScroll)
|
||||
|
||||
if let options = self.options {
|
||||
scrollView.isScrollEnabled = !(options.disableVerticalScroll && options.disableHorizontalScroll)
|
||||
}
|
||||
self.options = newOptions
|
||||
}
|
||||
|
||||
func getOptions() -> [String: Any?]? {
|
||||
|
@ -3253,6 +3351,11 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
configuration.userContentController.removeScriptMessageHandler(forName: "onFindResultReceived")
|
||||
if #available(iOS 14.0, *) {
|
||||
configuration.userContentController.removeAllScriptMessageHandlers()
|
||||
for contentWorldName in userScriptsContentWorlds {
|
||||
let contentWorld = getContentWorld(name: contentWorldName)
|
||||
configuration.userContentController.removeAllScriptMessageHandlers(from: contentWorld)
|
||||
configuration.userContentController.removeAllScriptMessageHandlers(from: contentWorld)
|
||||
}
|
||||
}
|
||||
configuration.userContentController.removeAllUserScripts()
|
||||
if #available(iOS 11.0, *) {
|
||||
|
|
|
@ -62,6 +62,9 @@ public class InAppWebViewOptions: Options<InAppWebView> {
|
|||
var minimumZoomScale = 1.0
|
||||
var contentInsetAdjustmentBehavior = 2 // UIScrollView.ContentInsetAdjustmentBehavior.never
|
||||
var isDirectionalLockEnabled = false
|
||||
var mediaType: String? = nil
|
||||
var pageZoom = 1.0
|
||||
var limitsNavigationsToAppBoundDomains = false
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
|
@ -78,7 +81,6 @@ public class InAppWebViewOptions: Options<InAppWebView> {
|
|||
realOptions["allowsLinkPreview"] = webView.allowsLinkPreview
|
||||
realOptions["allowsPictureInPictureMediaPlayback"] = configuration.allowsPictureInPictureMediaPlayback
|
||||
}
|
||||
realOptions["javaScriptEnabled"] = configuration.preferences.javaScriptEnabled
|
||||
realOptions["javaScriptCanOpenWindowsAutomatically"] = configuration.preferences.javaScriptCanOpenWindowsAutomatically
|
||||
if #available(iOS 10.0, *) {
|
||||
realOptions["mediaPlaybackRequiresUserGesture"] = configuration.mediaTypesRequiringUserActionForPlayback == .all
|
||||
|
@ -111,6 +113,13 @@ public class InAppWebViewOptions: Options<InAppWebView> {
|
|||
realOptions["allowUniversalAccessFromFileURLs"] = configuration.value(forKey: "allowUniversalAccessFromFileURLs")
|
||||
realOptions["allowFileAccessFromFileURLs"] = configuration.preferences.value(forKey: "allowFileAccessFromFileURLs")
|
||||
realOptions["isDirectionalLockEnabled"] = webView.scrollView.isDirectionalLockEnabled
|
||||
realOptions["javaScriptEnabled"] = configuration.preferences.javaScriptEnabled
|
||||
if #available(iOS 14.0, *) {
|
||||
realOptions["mediaType"] = webView.mediaType
|
||||
realOptions["pageZoom"] = Float(webView.pageZoom)
|
||||
realOptions["limitsNavigationsToAppBoundDomains"] = configuration.limitsNavigationsToAppBoundDomains
|
||||
realOptions["javaScriptEnabled"] = configuration.defaultWebpagePreferences.allowsContentJavaScript
|
||||
}
|
||||
}
|
||||
return realOptions
|
||||
}
|
||||
|
|
|
@ -26,13 +26,22 @@ class InAppWebViewStatic: NSObject, FlutterPlugin {
|
|||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
//let arguments = call.arguments as? NSDictionary
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "getDefaultUserAgent":
|
||||
InAppWebViewStatic.getDefaultUserAgent(completionHandler: { (value) in
|
||||
result(value)
|
||||
})
|
||||
break
|
||||
case "handlesURLScheme":
|
||||
let urlScheme = arguments!["urlScheme"] as! String
|
||||
if #available(iOS 11.0, *) {
|
||||
result(WKWebView.handlesURLScheme(urlScheme))
|
||||
} else {
|
||||
result(false)
|
||||
}
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
|
|
|
@ -2275,4 +2275,18 @@ class IOSInAppWebViewController {
|
|||
return await _controller._channel
|
||||
.invokeMethod('hasOnlySecureContent', args);
|
||||
}
|
||||
|
||||
///Returns a Boolean value that indicates whether WebKit natively supports resources with the specified URL scheme.
|
||||
///
|
||||
///[urlScheme] represents the URL scheme associated with the resource.
|
||||
///
|
||||
///**NOTE**: available only on iOS 11.0+.
|
||||
///
|
||||
///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkwebview/2875370-handlesurlscheme
|
||||
static Future<bool> handlesURLScheme(String urlScheme) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('urlScheme', () => urlScheme);
|
||||
return await InAppWebViewController._staticChannel
|
||||
.invokeMethod('handlesURLScheme', args);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4571,10 +4571,20 @@ class UserScript {
|
|||
///The default value is `true`. Available only on iOS.
|
||||
bool iosForMainFrameOnly;
|
||||
|
||||
UserScript({required this.source, required this.injectionTime, this.iosForMainFrameOnly = true});
|
||||
///**NOTE for iOS 14.0+**: The namespace in which to evaluate the script.
|
||||
///This parameter doesn’t apply to changes your script makes 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 [WKContentWorld](https://developer.apple.com/documentation/webkit/wkcontentworld).
|
||||
ContentWorld? contentWorld;
|
||||
|
||||
UserScript({required this.source, required this.injectionTime, this.iosForMainFrameOnly = true, this.contentWorld});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {"source": source, "injectionTime": injectionTime.toValue(), "iosForMainFrameOnly": iosForMainFrameOnly};
|
||||
return {"source": source,
|
||||
"injectionTime": injectionTime.toValue(),
|
||||
"iosForMainFrameOnly": iosForMainFrameOnly,
|
||||
"contentWorld": contentWorld?.name
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
|
@ -4586,3 +4596,28 @@ class UserScript {
|
|||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
///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 Android**: it will create and append an `<iframe>` HTML element with `id` equals to [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>`.
|
||||
class ContentWorld {
|
||||
///The name of a custom content world.
|
||||
final String name;
|
||||
|
||||
///Returns the custom content world with the specified name.
|
||||
ContentWorld.world({required this.name});
|
||||
|
||||
///The default world for clients.
|
||||
static ContentWorld defaultClient = ContentWorld.world(name: "defaultClient");
|
||||
|
||||
///The content world for the current webpage’s content.
|
||||
///This property contains the content world for scripts that the current webpage executes.
|
||||
///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.
|
||||
static ContentWorld page = ContentWorld.world(name: "page");
|
||||
}
|
||||
|
|
|
@ -844,6 +844,28 @@ class IOSInAppWebViewOptions
|
|||
///The default value is `false`.
|
||||
bool isDirectionalLockEnabled;
|
||||
|
||||
///The media type for the contents of the web view.
|
||||
///When the value of this property is `null`, the web view derives the current media type from the CSS media property of its content.
|
||||
///If you assign a value other than `null` to this property, the web view uses the value you provide instead.
|
||||
///The default value of this property is `null`.
|
||||
///
|
||||
///**NOTE**: available on iOS 14.0+.
|
||||
String? mediaType;
|
||||
|
||||
///The scale factor by which the web view scales content relative to its bounds.
|
||||
///The default value of this property is `1.0`, which displays the content without any scaling.
|
||||
///Changing the value of this property is equivalent to setting the CSS `zoom` property on all page content.
|
||||
///
|
||||
///**NOTE**: available on iOS 14.0+.
|
||||
double pageZoom;
|
||||
|
||||
///A Boolean value that indicates whether the web view limits navigation to pages within the app’s domain.
|
||||
///Check [App-Bound Domains](https://webkit.org/blog/10882/app-bound-domains/) for more details.
|
||||
///The default value is `false`.
|
||||
///
|
||||
///**NOTE**: available on iOS 14.0+.
|
||||
bool limitsNavigationsToAppBoundDomains;
|
||||
|
||||
IOSInAppWebViewOptions(
|
||||
{this.disallowOverScroll = false,
|
||||
this.enableViewportScale = false,
|
||||
|
@ -869,7 +891,10 @@ class IOSInAppWebViewOptions
|
|||
this.minimumZoomScale = 1.0,
|
||||
this.contentInsetAdjustmentBehavior =
|
||||
IOSUIScrollViewContentInsetAdjustmentBehavior.NEVER,
|
||||
this.isDirectionalLockEnabled = false});
|
||||
this.isDirectionalLockEnabled = false,
|
||||
this.mediaType,
|
||||
this.pageZoom = 1.0,
|
||||
this.limitsNavigationsToAppBoundDomains = false});
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toMap() {
|
||||
|
@ -906,6 +931,9 @@ class IOSInAppWebViewOptions
|
|||
"minimumZoomScale": minimumZoomScale,
|
||||
"contentInsetAdjustmentBehavior": contentInsetAdjustmentBehavior.toValue(),
|
||||
"isDirectionalLockEnabled": isDirectionalLockEnabled,
|
||||
"mediaType": mediaType,
|
||||
"pageZoom": pageZoom,
|
||||
"limitsNavigationsToAppBoundDomains": limitsNavigationsToAppBoundDomains,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -956,6 +984,9 @@ class IOSInAppWebViewOptions
|
|||
IOSUIScrollViewContentInsetAdjustmentBehavior.fromValue(
|
||||
map["contentInsetAdjustmentBehavior"])!;
|
||||
options.isDirectionalLockEnabled = map["isDirectionalLockEnabled"];
|
||||
options.mediaType = map["mediaType"];
|
||||
options.pageZoom = map["pageZoom"];
|
||||
options.limitsNavigationsToAppBoundDomains = map["limitsNavigationsToAppBoundDomains"];
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue