Commit a023d34f authored by Lorenzo Pichilli's avatar Lorenzo Pichilli

Added mediaType, pageZoom, limitsNavigationsToAppBoundDomains iOS-specific...

Added mediaType, pageZoom, limitsNavigationsToAppBoundDomains iOS-specific webview options, Added handlesURLScheme iOS-specific webview method, Added ContentWorld class, minor bug fixes
parent 37546a1d
......@@ -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,16 +263,44 @@ 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);
}
}
}
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) {
......
......@@ -28,13 +28,25 @@ public class JavaScriptBridgeInterface {
" !function(t,e){\"object\"==typeof exports&&\"undefined\"!=typeof module?e(exports):\"function\"==typeof define&&define.amd?define([\"exports\"],e):e(t.RSVP={})}(this,function(t){\"use strict\";function e(t){var e=t._promiseCallbacks;return e||(e=t._promiseCallbacks={}),e}var r={mixin:function(t){return t.on=this.on,t.off=this.off,t.trigger=this.trigger,t._promiseCallbacks=void 0,t},on:function(t,r){if(\"function\"!=typeof r)throw new TypeError(\"Callback must be a function\");var n=e(this),o=n[t];o||(o=n[t]=[]),-1===o.indexOf(r)&&o.push(r)},off:function(t,r){var n=e(this);if(r){var o=n[t],i=o.indexOf(r);-1!==i&&o.splice(i,1)}else n[t]=[]},trigger:function(t,r,n){var o=e(this)[t];if(o)for(var i=0;i<o.length;i++)(0,o[i])(r,n)}},n={instrument:!1};function o(t,e){if(2!==arguments.length)return n[t];n[t]=e}r.mixin(n);var i=[];function s(t,e,r){1===i.push({name:t,payload:{key:e._guidKey,id:e._id,eventName:t,detail:e._result,childId:r&&r._id,label:e._label,timeStamp:Date.now(),error:n[\"instrument-with-stack\"]?new Error(e._label):null}})&&setTimeout(function(){for(var t=0;t<i.length;t++){var e=i[t],r=e.payload;r.guid=r.key+r.id,r.childGuid=r.key+r.childId,r.error&&(r.stack=r.error.stack),n.trigger(e.name,e.payload)}i.length=0},50)}function u(t,e){if(t&&\"object\"==typeof t&&t.constructor===this)return t;var r=new this(c,e);return m(r,t),r}function c(){}var a=void 0,f=1,l=2,h={error:null};function p(t){try{return t.then}catch(t){return h.error=t,h}}var y=void 0;function _(){try{var t=y;return y=null,t.apply(this,arguments)}catch(t){return h.error=t,h}}function v(t){return y=t,_}function d(t,e,r){if(e.constructor===t.constructor&&r===A&&t.constructor.resolve===u)!function(t,e){e._state===f?b(t,e._result):e._state===l?(e._onError=null,g(t,e._result)):j(e,void 0,function(r){e===r?b(t,r):m(t,r)},function(e){return g(t,e)})}(t,e);else if(r===h){var o=h.error;h.error=null,g(t,o)}else\"function\"==typeof r?function(t,e,r){n.async(function(t){var n=!1,o=v(r).call(e,function(r){n||(n=!0,e===r?b(t,r):m(t,r))},function(e){n||(n=!0,g(t,e))},\"Settle: \"+(t._label||\" unknown promise\"));if(!n&&o===h){n=!0;var i=h.error;h.error=null,g(t,i)}},t)}(t,e,r):b(t,e)}function m(t,e){var r,n;t===e?b(t,e):(n=typeof(r=e),null===r||\"object\"!==n&&\"function\"!==n?b(t,e):d(t,e,p(e)))}function w(t){t._onError&&t._onError(t._result),O(t)}function b(t,e){t._state===a&&(t._result=e,t._state=f,0===t._subscribers.length?n.instrument&&s(\"fulfilled\",t):n.async(O,t))}function g(t,e){t._state===a&&(t._state=l,t._result=e,n.async(w,t))}function j(t,e,r,o){var i=t._subscribers,s=i.length;t._onError=null,i[s]=e,i[s+f]=r,i[s+l]=o,0===s&&t._state&&n.async(O,t)}function O(t){var e=t._subscribers,r=t._state;if(n.instrument&&s(r===f?\"fulfilled\":\"rejected\",t),0!==e.length){for(var o=void 0,i=void 0,u=t._result,c=0;c<e.length;c+=3)o=e[c],i=e[c+r],o?E(r,o,i,u):i(u);t._subscribers.length=0}}function E(t,e,r,n){var o=\"function\"==typeof r,i=void 0;if(i=o?v(r)(n):n,e._state!==a);else if(i===e)g(e,new TypeError(\"A promises callback cannot return that same promise.\"));else if(i===h){var s=h.error;h.error=null,g(e,s)}else o?m(e,i):t===f?b(e,i):t===l&&g(e,i)}function A(t,e,r){var o=this._state;if(o===f&&!t||o===l&&!e)return n.instrument&&s(\"chained\",this,this),this;this._onError=null;var i=new this.constructor(c,r),u=this._result;if(n.instrument&&s(\"chained\",this,i),o===a)j(this,i,t,e);else{var h=o===f?t:e;n.async(function(){return E(o,i,h,u)})}return i}var T=function(){function t(t,e,r,n){this._instanceConstructor=t,this.promise=new t(c,n),this._abortOnReject=r,this._isUsingOwnPromise=t===k,this._isUsingOwnResolve=t.resolve===u,this._init.apply(this,arguments)}return t.prototype._init=function(t,e){var r=e.length||0;this.length=r,this._remaining=r,this._result=new Array(r),this._enumerate(e)},t.prototype._enumerate=function(t){for(var e=this.length,r=this.promise,n=0;r._state===a&&n<e;n++)this._eachEntry(t[n],n,!0);this._checkFullfillment()},t.prototype._checkFullfillment=function(){if(0===this._remaining){var t=this._result;b(this.promise,t),this._result=null}},t.prototype._settleMaybeThenable=function(t,e,r){var n=this._instanceConstructor;if(this._isUsingOwnResolve){var o=p(t);if(o===A&&t._state!==a)t._onError=null,this._settledAt(t._state,e,t._result,r);else if(\"function\"!=typeof o)this._settledAt(f,e,t,r);else if(this._isUsingOwnPromise){var i=new n(c);d(i,t,o),this._willSettleAt(i,e,r)}else this._willSettleAt(new n(function(e){return e(t)}),e,r)}else this._willSettleAt(n.resolve(t),e,r)},t.prototype._eachEntry=function(t,e,r){null!==t&&\"object\"==typeof t?this._settleMaybeThenable(t,e,r):this._setResultAt(f,e,t,r)},t.prototype._settledAt=function(t,e,r,n){var o=this.promise;o._state===a&&(this._abortOnReject&&t===l?g(o,r):(this._setResultAt(t,e,r,n),this._checkFullfillment()))},t.prototype._setResultAt=function(t,e,r,n){this._remaining--,this._result[e]=r},t.prototype._willSettleAt=function(t,e,r){var n=this;j(t,void 0,function(t){return n._settledAt(f,e,t,r)},function(t){return n._settledAt(l,e,t,r)})},t}();function P(t,e,r){this._remaining--,this._result[e]=t===f?{state:\"fulfilled\",value:r}:{state:\"rejected\",reason:r}}var S=\"rsvp_\"+Date.now()+\"-\",R=0;var k=function(){function t(e,r){this._id=R++,this._label=r,this._state=void 0,this._result=void 0,this._subscribers=[],n.instrument&&s(\"created\",this),c!==e&&(\"function\"!=typeof e&&function(){throw new TypeError(\"You must pass a resolver function as the first argument to the promise constructor\")}(),this instanceof t?function(t,e){var r=!1;try{e(function(e){r||(r=!0,m(t,e))},function(e){r||(r=!0,g(t,e))})}catch(e){g(t,e)}}(this,e):function(){throw new TypeError(\"Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.\")}())}return t.prototype._onError=function(t){var e=this;n.after(function(){e._onError&&n.trigger(\"error\",t,e._label)})},t.prototype.catch=function(t,e){return this.then(void 0,t,e)},t.prototype.finally=function(t,e){var r=this.constructor;return\"function\"==typeof t?this.then(function(e){return r.resolve(t()).then(function(){return e})},function(e){return r.resolve(t()).then(function(){throw e})}):this.then(t,t)},t}();function x(t,e){return{then:function(r,n){return t.call(e,r,n)}}}function M(t,e){var r=function(){for(var r=arguments.length,n=new Array(r+1),o=!1,i=0;i<r;++i){var s=arguments[i];if(!o){if((o=F(s))===h){var u=h.error;h.error=null;var a=new k(c);return g(a,u),a}o&&!0!==o&&(s=x(o,s))}n[i]=s}var f=new k(c);return n[r]=function(t,r){t?g(f,t):void 0===e?m(f,r):!0===e?m(f,function(t){for(var e=t.length,r=new Array(e-1),n=1;n<e;n++)r[n-1]=t[n];return r}(arguments)):Array.isArray(e)?m(f,function(t,e){for(var r={},n=t.length,o=new Array(n),i=0;i<n;i++)o[i]=t[i];for(var s=0;s<e.length;s++)r[e[s]]=o[s+1];return r}(arguments,e)):m(f,r)},o?function(t,e,r,n){return k.all(e).then(function(e){return C(t,e,r,n)})}(f,n,t,this):C(f,n,t,this)};return r.__proto__=t,r}function C(t,e,r,n){if(v(r).apply(n,e)===h){var o=h.error;h.error=null,g(t,o)}return t}function F(t){return null!==t&&\"object\"==typeof t&&(t.constructor===k||p(t))}function I(t,e){return k.all(t,e)}k.cast=u,k.all=function(t,e){return Array.isArray(t)?new T(this,t,!0,e).promise:this.reject(new TypeError(\"Promise.all must be called with an array\"),e)},k.race=function(t,e){var r=new this(c,e);if(!Array.isArray(t))return g(r,new TypeError(\"Promise.race must be called with an array\")),r;for(var n=0;r._state===a&&n<t.length;n++)j(this.resolve(t[n]),void 0,function(t){return m(r,t)},function(t){return g(r,t)});return r},k.resolve=u,k.reject=function(t,e){var r=new this(c,e);return g(r,t),r},k.prototype._guidKey=S,k.prototype.then=A;var N=function(t){function e(e,r,n){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,!1,n))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e}(T);function U(t,e){return Array.isArray(t)?new N(k,t,e).promise:k.reject(new TypeError(\"Promise.allSettled must be called with an array\"),e)}function D(t,e){return k.race(t,e)}N.prototype._setResultAt=P;var K=function(t){function e(e,r){var n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],o=arguments[3];return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,n,o))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._init=function(t,e){this._result={},this._enumerate(e)},e.prototype._enumerate=function(t){var e=Object.keys(t),r=e.length,n=this.promise;this._remaining=r;for(var o=void 0,i=void 0,s=0;n._state===a&&s<r;s++)i=t[o=e[s]],this._eachEntry(i,o,!0);this._checkFullfillment()},e}(T);function q(t,e){return k.resolve(t,e).then(function(t){if(null===t||\"object\"!=typeof t)throw new TypeError(\"Promise.hash must be called with an object\");return new K(k,t,e).promise})}var G=function(t){function e(e,r,n){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,!1,n))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e}(K);function L(t,e){return k.resolve(t,e).then(function(t){if(null===t||\"object\"!=typeof t)throw new TypeError(\"hashSettled must be called with an object\");return new G(k,t,!1,e).promise})}function V(t){throw setTimeout(function(){throw t}),t}function W(t){var e={resolve:void 0,reject:void 0};return e.promise=new k(function(t,r){e.resolve=t,e.reject=r},t),e}G.prototype._setResultAt=P;var Y=function(t){function e(e,r,n,o){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.call(this,e,r,!0,o,n))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._init=function(t,e,r,n,o){var i=e.length||0;this.length=i,this._remaining=i,this._result=new Array(i),this._mapFn=o,this._enumerate(e)},e.prototype._setResultAt=function(t,e,r,n){if(n){var o=v(this._mapFn)(r,e);o===h?this._settledAt(l,e,o.error,!1):this._eachEntry(o,e,!1)}else this._remaining--,this._result[e]=r},e}(T);function $(t,e,r){return\"function\"!=typeof e?k.reject(new TypeError(\"map expects a function as a second argument\"),r):k.resolve(t,r).then(function(t){if(!Array.isArray(t))throw new TypeError(\"map must be called with an array\");return new Y(k,t,e,r).promise})}function z(t,e){return k.resolve(t,e)}function B(t,e){return k.reject(t,e)}var H={},J=function(t){function e(){return function(t,e){if(!t)throw new ReferenceError(\"this hasn't been initialised - super() hasn't been called\");return!e||\"object\"!=typeof e&&\"function\"!=typeof e?t:e}(this,t.apply(this,arguments))}return function(t,e){if(\"function\"!=typeof e&&null!==e)throw new TypeError(\"Super expression must either be null or a function, not \"+typeof e);t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,enumerable:!1,writable:!0,configurable:!0}}),e&&(Object.setPrototypeOf?Object.setPrototypeOf(t,e):t.__proto__=e)}(e,t),e.prototype._checkFullfillment=function(){if(0===this._remaining&&null!==this._result){var t=this._result.filter(function(t){return t!==H});b(this.promise,t),this._result=null}},e.prototype._setResultAt=function(t,e,r,n){if(n){this._result[e]=r;var o=v(this._mapFn)(r,e);o===h?this._settledAt(l,e,o.error,!1):this._eachEntry(o,e,!1)}else this._remaining--,r||(this._result[e]=H)},e}(Y);function Q(t,e,r){return\"function\"!=typeof e?k.reject(new TypeError(\"filter expects function as a second argument\"),r):k.resolve(t,r).then(function(t){if(!Array.isArray(t))throw new TypeError(\"filter must be called with an array\");return new J(k,t,e,r).promise})}var X=0,Z=void 0;function tt(t,e){ut[X]=t,ut[X+1]=e,2===(X+=2)&&_t()}var et=\"undefined\"!=typeof window?window:void 0,rt=et||{},nt=rt.MutationObserver||rt.WebKitMutationObserver,ot=\"undefined\"==typeof self&&\"undefined\"!=typeof process&&\"[object process]\"==={}.toString.call(process),it=\"undefined\"!=typeof Uint8ClampedArray&&\"undefined\"!=typeof importScripts&&\"undefined\"!=typeof MessageChannel;function st(){return function(){return setTimeout(ct,1)}}var ut=new Array(1e3);function ct(){for(var t=0;t<X;t+=2){(0,ut[t])(ut[t+1]),ut[t]=void 0,ut[t+1]=void 0}X=0}var at,ft,lt,ht,pt,yt,_t=void 0;ot?(pt=process.nextTick,yt=process.versions.node.match(/^(?:(\\\\d+)\\\\.)?(?:(\\\\d+)\\\\.)?(\\\\*|\\\\d+)$/),Array.isArray(yt)&&\"0\"===yt[1]&&\"10\"===yt[2]&&(pt=setImmediate),_t=function(){return pt(ct)}):nt?(ft=0,lt=new nt(ct),ht=document.createTextNode(\"\"),lt.observe(ht,{characterData:!0}),_t=function(){return ht.data=ft=++ft%2}):it?((at=new MessageChannel).port1.onmessage=ct,_t=function(){return at.port2.postMessage(0)}):_t=void 0===et&&\"function\"==typeof require?function(){try{var t=Function(\"return this\")().require(\"vertx\");return void 0!==(Z=t.runOnLoop||t.runOnContext)?function(){Z(ct)}:st()}catch(t){return st()}}():st(),n.async=tt,n.after=function(t){return setTimeout(t,0)};var vt=z,dt=function(t,e){return n.async(t,e)};function mt(){n.on.apply(n,arguments)}function wt(){n.off.apply(n,arguments)}if(\"undefined\"!=typeof window&&\"object\"==typeof window.__PROMISE_INSTRUMENTATION__){var bt=window.__PROMISE_INSTRUMENTATION__;for(var gt in o(\"instrument\",!0),bt)bt.hasOwnProperty(gt)&&mt(gt,bt[gt])}var jt={asap:tt,cast:vt,Promise:k,EventTarget:r,all:I,allSettled:U,race:D,hash:q,hashSettled:L,rethrow:V,defer:W,denodeify:M,configure:o,on:mt,off:wt,resolve:z,reject:B,map:$,async:dt,filter:Q};t.default=jt,t.asap=tt,t.cast=vt,t.Promise=k,t.EventTarget=r,t.all=I,t.allSettled=U,t.race=D,t.hash=q,t.hashSettled=L,t.rethrow=V,t.defer=W,t.denodeify=M,t.configure=o,t.on=mt,t.off=wt,t.resolve=z,t.reject=B,t.map=$,t.async=dt,t.filter=Q,Object.defineProperty(t,\"__esModule\",{value:!0})});" +
"}";
public static final String flutterInAppBroserJSClass = promisePolyfillJS + " " + "window." + name + ".callHandler = function() {" +
"var _callHandlerID = setTimeout(function(){});" +
"window." + name + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
"return new Promise(function(resolve, reject) {" +
" window." + name + "[_callHandlerID] = resolve;" +
"});" +
"};";
public static final String callHandlerScriptJS = promisePolyfillJS + " " +
"if (window.top == null || window.top === window) {" +
" window." + name + ".callHandler = function() {" +
" var _callHandlerID = setTimeout(function(){});" +
" window." + name + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
" return new Promise(function(resolve, reject) {" +
" window." + name + "[_callHandlerID] = resolve;" +
" });" +
" };"+
"} else {" +
" window." + name + " = {};" +
" window." + name + ".callHandler = function() {" +
" var _callHandlerID = setTimeout(function(){});" +
" window.top." + name + "._callHandler(arguments[0], _callHandlerID, JSON.stringify(Array.prototype.slice.call(arguments, 1)));" +
" return new Promise(function(resolve, reject) {" +
" window.top." + name + "[_callHandlerID] = resolve;" +
" });" +
" };"+
"}";
public JavaScriptBridgeInterface(Object obj) {
if (obj instanceof InAppBrowserActivity)
......
{"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"}
\ No newline at end of file
{"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"}
\ No newline at end of file
......@@ -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 {
let originalViewPortMetaTagContentJSScript = WKUserScript(source: originalViewPortMetaTagContentJS, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
configuration.userContentController.addUserScript(originalViewPortMetaTagContentJSScript)
@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)
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 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)
}
}
}
}
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)
let printJSScript = WKUserScript(source: printJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(printJSScript)
if let options = options {
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)
}
}
}
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)
let lastTouchedAnchorOrImageJSScript = WKUserScript(source: lastTouchedAnchorOrImageJS, injectionTime: .atDocumentStart, forMainFrameOnly: false)
configuration.userContentController.addUserScript(lastTouchedAnchorOrImageJSScript)
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() {
......@@ -4585,4 +4595,29 @@ class UserScript {
String toString() {
return toMap().toString();
}
}
\ No newline at end of file
}
///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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment