diff --git a/CHANGELOG.md b/CHANGELOG.md index cd077ecee..9b930165c 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,18 +5,21 @@ - Added Android keyboard workaround to hide the keyboard when clicking other HTML elements, losing the focus on the previous input - Added `onEnterFullscreen`, `onExitFullscreen` webview events [#275](https://github.com/pichillilorenzo/flutter_inappwebview/issues/275) - Added Android support to use camera on HTML inputs that requires it, such as `` [#353](https://github.com/pichillilorenzo/flutter_inappwebview/issues/353) -- Added `overScrollMode`, `networkAvailable`, `scrollBarStyle`, `verticalScrollbarPosition`, `scrollBarDefaultDelayBeforeFade`, `scrollbarFadingEnabled`, `scrollBarFadeDuration`, `rendererPriorityPolicy` webview options on Android +- Added `overScrollMode`, `networkAvailable`, `scrollBarStyle`, `verticalScrollbarPosition`, `scrollBarDefaultDelayBeforeFade`, `scrollbarFadingEnabled`, `scrollBarFadeDuration`, `rendererPriorityPolicy`, `useShouldInterceptRequest`, `useOnRenderProcessGone` webview options on Android - Added `pageDown`, `pageUp`, `saveWebArchive`, `zoomIn`, `zoomOut` webview methods on Android - Added `getCurrentWebViewPackage` static webview method on Android -- Added `shouldInterceptRequest`, `onRenderProcessUnresponsive`, `onRenderProcessResponsive`, `onRenderProcessGone`, `onFormResubmission`, `onPageCommitVisible`, `onScaleChanged` Android events +- Added `onPageCommitVisible` webview event +- Added `androidShouldInterceptRequest`, `androidOnRenderProcessUnresponsive`, `androidOnRenderProcessResponsive`, `androidOnRenderProcessGone`, `androidOnFormResubmission`, `androidOnScaleChanged` Android events - Fixed `Print preview is not working? java.lang.IllegalStateException: Can print only from an activity` [#128](https://github.com/pichillilorenzo/flutter_inappwebview/issues/128) - Fixed `onJsAlert`, `onJsConfirm`, `onJsPrompt` for `InAppBrowser` on Android - Fixed `onActivityResult` for `InAppBrowser` on Android - Fixed `InAppBrowser.openWithSystemBrowser crash on iOS` [#358](https://github.com/pichillilorenzo/flutter_inappwebview/issues/358) +- Fixed `Attempt to invoke virtual method 'java.util.Set java.util.HashMap.entrySet()' on a null object reference` [#367](https://github.com/pichillilorenzo/flutter_inappwebview/issues/367) ### BREAKING CHANGES - Android `clearClientCertPreferences`, `getSafeBrowsingPrivacyPolicyUrl`, `setSafeBrowsingWhitelist` webview methods are static now +- Removed iOS event `onDidCommit`; it has been renamed to `onPageCommitVisible` and made cross-platform ## 3.2.0 diff --git a/README.md b/README.md index cfa23b799..823596c1e 100755 --- a/README.md +++ b/README.md @@ -407,13 +407,19 @@ Screenshots: Android-specific methods can be called using the `InAppWebViewController.android` attribute. * `startSafeBrowsing`: Starts Safe Browsing initialization. -* `setSafeBrowsingWhitelist({@required List hosts})`: Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews. -* `getSafeBrowsingPrivacyPolicyUrl`: Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`. * `clearSslPreferences`: Clears the SSL preferences table stored in response to proceeding with SSL certificate errors. -* `clearClientCertPreferences`: Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests. * `pause`: Does a best-effort attempt to pause any processing that can be paused safely, such as animations and geolocation. Note that this call does not pause JavaScript. * `resume`: Resumes a WebView after a previous call to `pause()`. * `getOriginalUrl`: Gets the URL that was originally requested for the current page. +* `pageDown({@required bool bottom})`: Scrolls the contents of this WebView down by half the page size. +* `pageUp({@required bool top})`: Scrolls the contents of this WebView up by half the view size. +* `saveWebArchive({@required String basename, @required bool autoname})`: Saves the current view as a web archive. +* `zoomIn`: Performs zoom in in this WebView. +* `zoomOut`: Performs zoom out in this WebView. +* `static clearClientCertPreferences`: Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests. +* `static getSafeBrowsingPrivacyPolicyUrl`: Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`. +* `static setSafeBrowsingWhitelist({@required List hosts})`: Sets the list of hosts (domain names/IP addresses) that are exempt from SafeBrowsing checks. The list is global for all the WebViews. +* `static getCurrentWebViewPackage()`: Gets the current Android WebView package info. ##### `InAppWebViewController` iOS-specific methods @@ -474,6 +480,8 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly: * `useShouldOverrideUrlLoading`: Set to `true` to be able to listen at the `shouldOverrideUrlLoading` event. The default value is `false`. * `useOnLoadResource`: Set to `true` to be able to listen at the `onLoadResource` event. The default value is `false`. * `useOnDownloadStart`: Set to `true` to be able to listen at the `onDownloadStart` event. The default value is `false`. +* `useShouldInterceptAjaxRequest`: Set to `true` to be able to listen at the `shouldInterceptAjaxRequest` event. The default value is `false`. +* `useShouldInterceptFetchRequest`: Set to `true` to be able to listen at the `shouldInterceptFetchRequest` event. The default value is `false`. * `clearCache`: Set to `true` to have all the browser's cache cleared before the new window is opened. The default value is `false`. * `userAgent`: Sets the user-agent for the WebView. * `applicationNameForUserAgent`: Append to the existing user-agent. Setting userAgent will override this. @@ -487,8 +495,6 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly: * `resourceCustomSchemes`: List of custom schemes that the WebView must handle. Use the `onLoadResourceCustomScheme` event to intercept resource requests with custom scheme. * `contentBlockers`: List of `ContentBlocker` that are a set of rules used to block content in the browser window. * `preferredContentMode`: Sets the content mode that the WebView needs to use when loading and rendering a webpage. The default value is `InAppWebViewUserPreferredContentMode.RECOMMENDED`. -* `useShouldInterceptAjaxRequest`: Set to `true` to be able to listen at the `shouldInterceptAjaxRequest` event. The default value is `false`. -* `useShouldInterceptFetchRequest`: Set to `true` to be able to listen at the `shouldInterceptFetchRequest` event. The default value is `false`. * `incognito`: Set to `true` to open a browser window with incognito mode. The default value is `false`. * `cacheEnabled`: Sets whether WebView should use browser caching. The default value is `true`. * `transparentBackground`: Set to `true` to make the background of the WebView transparent. If your app has a dark theme, this can prevent a white flash on initialization. The default value is `false`. @@ -498,6 +504,8 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly: ##### `InAppWebView` Android-specific options +* `useShouldInterceptRequest`: Set to `true` to be able to listen at the `androidShouldInterceptRequest` event. The default value is `false`. +* `useOnRenderProcessGone`: Set to `true` to be able to listen at the `androidOnRenderProcessGone` event. The default value is `false`. * `textZoom`: Sets the text zoom of the page in percent. The default value is `100`. * `clearSessionCache`: Set to `true` to have the session cookie cache cleared before the new window is opened. * `builtInZoomControls`: Set to `true` if the WebView should use its built-in zoom mechanisms. The default value is `false`. @@ -540,6 +548,13 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly: * `hardwareAcceleration`: Boolean value to enable Hardware Acceleration in the WebView. * `supportMultipleWindows`: Sets whether the WebView whether supports multiple windows. * `regexToCancelSubFramesLoading`: Regular expression used by `shouldOverrideUrlLoading` event to cancel navigation for frames that are not the main frame. If the url request of a subframe matches the regular expression, then the request of that subframe is canceled. +* `overScrollMode`: Sets the WebView's over-scroll mode. The default value is `AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS`. +* `scrollBarStyle`: Specify the style of the scrollbars. The scrollbars can be overlaid or inset. The default value is `AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY`. +* `verticalScrollbarPosition`: Set the position of the vertical scroll bar. The default value is `AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT`. +* `scrollBarDefaultDelayBeforeFade`: Defines the delay in milliseconds that a scrollbar waits before fade out. +* `scrollbarFadingEnabled`: Define whether scrollbars will fade when the view is not scrolling. The default value is `true`. +* `scrollBarFadeDuration`: Define the scrollbar fade duration in milliseconds. +* `rendererPriorityPolicy`: Set the renderer priority policy for this WebView. ##### `InAppWebView` iOS-specific options @@ -599,12 +614,18 @@ Event names that starts with `android` or `ios` are events platform-specific. * `onLongPressHitTestResult`: Event fired when an HTML element of the webview has been clicked and held. * `onEnterFullscreen`: Event fired when the current page has entered full screen mode. * `onExitFullscreen`: Event fired when the current page has exited full screen mode. +* `onPageCommitVisible`: Called when the web view begins to receive web content. * `androidOnSafeBrowsingHit`: Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing (available only on Android). * `androidOnPermissionRequest`: Event fired when the webview is requesting permission to access the specified resources and the permission currently isn't granted or denied (available only on Android). * `androidOnGeolocationPermissionsShowPrompt`: Event that notifies the host application that web content from the specified origin is attempting to use the Geolocation API, but no permission state is currently set for that origin (available only on Android). -* `androidOnGeolocationPermissionsHidePrompt`: Notify the host application that a request for Geolocation permissions, made with a previous call to `androidOnGeolocationPermissionsShowPrompt` has been canceled. (available only on Android). +* `androidOnGeolocationPermissionsHidePrompt`: Notify the host application that a request for Geolocation permissions, made with a previous call to `androidOnGeolocationPermissionsShowPrompt` has been canceled (available only on Android). +* `androidShouldInterceptRequest`: Notify the host application of a resource request and allow the application to return the data (available only on Android). +* `androidOnRenderProcessGone`: Event fired when the given WebView's render process has exited (available only on Android). +* `androidOnRenderProcessResponsive`: Event called once when an unresponsive renderer currently associated with the WebView becomes responsive (available only on Android). +* `androidOnRenderProcessUnresponsive`: Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript (available only on Android). +* `androidOnFormResubmission`: As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data (available only on Android). +* `androidOnScaleChanged`: Event fired when the scale applied to the WebView has changed (available only on Android). * `iosOnWebContentProcessDidTerminate`: Invoked when the web view's web content process is terminated (available only on iOS). -* `iosOnDidCommit`: Called when the web view begins to receive web content (available only on iOS). * `iosOnDidReceiveServerRedirectForProvisionalNavigation`: Called when a web view receives a server redirect (available only on iOS). ### `ContextMenu` class diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java index d950fb787..f6708ed8d 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppBrowser/InAppBrowserActivity.java @@ -1,10 +1,7 @@ package com.pichillilorenzo.flutter_inappwebview.InAppBrowser; import android.content.Intent; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Picture; import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; @@ -32,8 +29,6 @@ import com.pichillilorenzo.flutter_inappwebview.R; import com.pichillilorenzo.flutter_inappwebview.Shared; -import java.io.ByteArrayOutputStream; -import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -345,6 +340,31 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result resul case "getHitTestResult": result.success(getHitTestResult()); break; + case "pageDown": + { + boolean bottom = (boolean) call.argument("bottom"); + result.success(pageDown(bottom)); + } + break; + case "pageUp": + { + boolean top = (boolean) call.argument("top"); + result.success(pageUp(top)); + } + break; + case "saveWebArchive": + { + String basename = (String) call.argument("basename"); + boolean autoname = (boolean) call.argument("autoname"); + saveWebArchive(basename, autoname, result); + } + break; + case "zoomIn": + result.success(zoomIn()); + break; + case "zoomOut": + result.success(zoomOut()); + break; default: result.notImplemented(); } @@ -852,6 +872,43 @@ public Map getHitTestResult() { return null; } + public boolean pageDown(boolean bottom) { + if (webView != null) + return webView.pageDown(bottom); + return false; + } + + public boolean pageUp(boolean top) { + if (webView != null) + return webView.pageUp(top); + return false; + } + + public void saveWebArchive(String basename, boolean autoname, final MethodChannel.Result result) { + if (webView != null) { + webView.saveWebArchive(basename, autoname, new ValueCallback() { + @Override + public void onReceiveValue(String value) { + result.success(value); + } + }); + } else { + result.success(null); + } + } + + public boolean zoomIn() { + if (webView != null) + return webView.zoomIn(); + return false; + } + + public boolean zoomOut() { + if (webView != null) + return webView.zoomOut(); + return false; + } + public void dispose() { channel.setMethodCallHandler(null); activityResultListeners.clear(); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java index 439fbc4d6..978bd12aa 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/FlutterWebView.java @@ -396,9 +396,6 @@ public void onReceiveValue(Boolean success) { result.success(null); } break; - - - case "pageDown": if (webView != null) { boolean bottom = (boolean) call.argument("bottom"); diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java index 66f5e62af..0f243feb8 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebView.java @@ -777,7 +777,8 @@ else if (options.clearSessionCache) setRendererPriorityPolicy( (int) options.rendererPriorityPolicy.get("rendererRequestedPriority"), (boolean) options.rendererPriorityPolicy.get("waivedWhenNotVisible")); - } else if (options.rendererPriorityPolicy == null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + } else if ((options.rendererPriorityPolicy == null || (options.rendererPriorityPolicy != null && options.rendererPriorityPolicy.isEmpty())) && + Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { options.rendererPriorityPolicy.put("rendererRequestedPriority", getRendererRequestedPriority()); options.rendererPriorityPolicy.put("waivedWhenNotVisible", getRendererPriorityWaivedWhenNotVisible()); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewClient.java index e792af3c5..5b31a61a1 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewClient.java @@ -24,18 +24,14 @@ import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; -import androidx.webkit.WebViewFeature; import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.Credential; import com.pichillilorenzo.flutter_inappwebview.CredentialDatabase.CredentialDatabase; import com.pichillilorenzo.flutter_inappwebview.InAppBrowser.InAppBrowserActivity; import com.pichillilorenzo.flutter_inappwebview.JavaScriptBridgeInterface; -import com.pichillilorenzo.flutter_inappwebview.Shared; import com.pichillilorenzo.flutter_inappwebview.Util; import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; @@ -746,7 +742,7 @@ public WebResourceResponse onShouldInterceptRequest(Object request) { headers = webResourceRequest.getRequestHeaders(); hasGesture = webResourceRequest.hasGesture(); isForMainFrame = webResourceRequest.isForMainFrame(); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { isRedirect = webResourceRequest.isRedirect(); } } @@ -775,8 +771,20 @@ public WebResourceResponse onShouldInterceptRequest(Object request) { } else if (flutterResult.result != null) { Map res = (Map) flutterResult.result; + String contentType = (String) res.get("contentType"); + String contentEncoding = (String) res.get("contentEncoding"); byte[] data = (byte[]) res.get("data"); - return new WebResourceResponse(res.get("content-type").toString(), res.get("content-encoding").toString(), new ByteArrayInputStream(data)); + Map responseHeaders = (Map) res.get("headers"); + Integer statusCode = (Integer) res.get("statusCode"); + String reasonPhrase = (String) res.get("reasonPhrase"); + + ByteArrayInputStream inputStream = (data != null) ? new ByteArrayInputStream(data) : null; + + if ((responseHeaders == null && statusCode == null && reasonPhrase == null) || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + return new WebResourceResponse(contentType, contentEncoding, inputStream); + } else { + return new WebResourceResponse(contentType, contentEncoding, statusCode, reasonPhrase, responseHeaders, inputStream); + } } return null; @@ -793,17 +801,19 @@ public void onFormResubmission (final WebView view, final Message dontResend, fi @Override public void success(@Nullable Object response) { - Map responseMap = (Map) response; - Integer action = (Integer) responseMap.get("action"); - if (action != null) { - switch (action) { - case 1: - resend.sendToTarget(); - return; - case 0: - default: - dontResend.sendToTarget(); - return; + if (response != null) { + Map responseMap = (Map) response; + Integer action = (Integer) responseMap.get("action"); + if (action != null) { + switch (action) { + case 0: + resend.sendToTarget(); + return; + case 1: + default: + dontResend.sendToTarget(); + return; + } } } @@ -835,16 +845,22 @@ public void onPageCommitVisible(WebView view, String url) { @RequiresApi(api = Build.VERSION_CODES.O) @Override public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) { - Boolean didCrash = detail.didCrash(); - Integer rendererPriorityAtExit = detail.rendererPriorityAtExit(); + final InAppWebView webView = (InAppWebView) view; - Map obj = new HashMap<>(); - if (inAppBrowserActivity != null) - obj.put("uuid", inAppBrowserActivity.uuid); - obj.put("didCrash", didCrash); - obj.put("rendererPriorityAtExit", rendererPriorityAtExit); + if (webView.options.useOnRenderProcessGone) { + Boolean didCrash = detail.didCrash(); + Integer rendererPriorityAtExit = detail.rendererPriorityAtExit(); - channel.invokeMethod("onRenderProcessGone", obj); + Map obj = new HashMap<>(); + if (inAppBrowserActivity != null) + obj.put("uuid", inAppBrowserActivity.uuid); + obj.put("didCrash", didCrash); + obj.put("rendererPriorityAtExit", rendererPriorityAtExit); + + channel.invokeMethod("onRenderProcessGone", obj); + + return true; + } return super.onRenderProcessGone(view, detail); } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewOptions.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewOptions.java index db51846da..b7884bb46 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewOptions.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewOptions.java @@ -87,7 +87,6 @@ public class InAppWebViewOptions implements Options { public Boolean hardwareAcceleration = true; public Boolean supportMultipleWindows = false; public String regexToCancelSubFramesLoading; - public Integer overScrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS; public Boolean networkAvailable = null; public Integer scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY; @@ -97,6 +96,7 @@ public class InAppWebViewOptions implements Options { public Integer scrollBarFadeDuration = null; public Map rendererPriorityPolicy = new HashMap<>(); public Boolean useShouldInterceptRequest = false; + public Boolean useOnRenderProcessGone = false; @Override public InAppWebViewOptions parse(HashMap options) { @@ -333,6 +333,9 @@ public InAppWebViewOptions parse(HashMap options) { case "useShouldInterceptRequest": useShouldInterceptRequest = (Boolean) value; break; + case "useOnRenderProcessGone": + useOnRenderProcessGone = (Boolean) value; + break; } } @@ -417,6 +420,7 @@ public HashMap getHashMap() { options.put("scrollBarFadeDuration", scrollBarFadeDuration); options.put("rendererPriorityPolicy", rendererPriorityPolicy); options.put("useShouldInterceptRequest", useShouldInterceptRequest); + options.put("useOnRenderProcessGone", useOnRenderProcessGone); return options; } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewRenderProcessClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewRenderProcessClient.java index 85325638e..76d06fb57 100644 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewRenderProcessClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebView/InAppWebViewRenderProcessClient.java @@ -37,6 +37,7 @@ public void onRenderProcessUnresponsive(@NonNull WebView view, @Nullable final W Map obj = new HashMap<>(); if (inAppBrowserActivity != null) obj.put("uuid", inAppBrowserActivity.uuid); + obj.put("url", view.getUrl()); channel.invokeMethod("onRenderProcessUnresponsive", obj, new MethodChannel.Result() { @Override @@ -45,7 +46,7 @@ public void success(@Nullable Object response) { Integer action = (Integer) responseMap.get("action"); if (action != null && renderer != null) { switch (action) { - case 1: + case 0: if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_TERMINATE)) renderer.terminate(); break; @@ -70,6 +71,7 @@ public void onRenderProcessResponsive(@NonNull WebView view, @Nullable final Web Map obj = new HashMap<>(); if (inAppBrowserActivity != null) obj.put("uuid", inAppBrowserActivity.uuid); + obj.put("url", view.getUrl()); channel.invokeMethod("onRenderProcessResponsive", obj, new MethodChannel.Result() { @Override @@ -78,7 +80,7 @@ public void success(@Nullable Object response) { Integer action = (Integer) responseMap.get("action"); if (action != null && renderer != null) { switch (action) { - case 1: + case 0: if (WebViewFeature.isFeatureSupported(WebViewFeature.WEB_VIEW_RENDERER_TERMINATE)) renderer.terminate(); break; diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewStatic.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewStatic.java index 6f0311c9d..a9744e286 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewStatic.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/InAppWebViewStatic.java @@ -33,8 +33,6 @@ public void onMethodCall(MethodCall call, final MethodChannel.Result result) { case "getDefaultUserAgent": result.success(WebSettings.getDefaultUserAgent(Shared.applicationContext)); break; - - case "clearClientCertPreferences": if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { WebView.clearClientCertPreferences(new Runnable() { @@ -66,15 +64,12 @@ public void onReceiveValue(Boolean value) { else result.success(false); break; - - - case "getCurrentWebViewPackage": if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { result.success( convertWebViewPackageToMap(WebViewCompat.getCurrentWebViewPackage(Shared.activity))); } else { - result.success(false); + result.success(null); } break; default: diff --git a/example/.flutter-plugins-dependencies b/example/.flutter-plugins-dependencies index 60c34f762..cc2305d41 100755 --- a/example/.flutter-plugins-dependencies +++ b/example/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-26 00:10:53.444505","version":"1.17.1"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"android":[{"name":"e2e","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/e2e-0.2.4+4/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.0.0+hotfix.6/","dependencies":[]}],"macos":[],"linux":[],"windows":[],"web":[]},"dependencyGraph":[{"name":"e2e","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"permission_handler","dependencies":[]}],"date_created":"2020-05-29 00:17:33.621880","version":"1.17.1"} \ No newline at end of file diff --git a/example/lib/headless_in_app_webview.screen.dart b/example/lib/headless_in_app_webview.screen.dart index 0fd88798d..6b33cef30 100755 --- a/example/lib/headless_in_app_webview.screen.dart +++ b/example/lib/headless_in_app_webview.screen.dart @@ -1,5 +1,3 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index 190cef659..46bf16f5d 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -1,9 +1,6 @@ import 'dart:io'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'main.dart'; diff --git a/example/lib/main.dart b/example/lib/main.dart index fedc8d6ac..82f759414 100755 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -6,7 +6,7 @@ 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(); diff --git a/flutter_inappwebview.iml b/flutter_inappwebview.iml index c8905c934..111d60ace 100755 --- a/flutter_inappwebview.iml +++ b/flutter_inappwebview.iml @@ -26,7 +26,6 @@ - diff --git a/ios/Classes/InAppWebView.swift b/ios/Classes/InAppWebView.swift index 682fe49f5..ea06081a4 100755 --- a/ios/Classes/InAppWebView.swift +++ b/ios/Classes/InAppWebView.swift @@ -2204,7 +2204,7 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) { - onDidCommit() + onPageCommitVisible(url: url?.absoluteString) } public func webView(_ webView: WKWebView, @@ -2486,8 +2486,11 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi channel?.invokeMethod("onWebContentProcessDidTerminate", arguments: []) } - public func onDidCommit() { - channel?.invokeMethod("onDidCommit", arguments: []) + public func onPageCommitVisible(url: String?) { + let arguments: [String: Any?] = [ + "url": url + ] + channel?.invokeMethod("onPageCommitVisible", arguments: arguments) } public func onDidReceiveServerRedirectForProvisionalNavigation() { diff --git a/lib/src/headless_in_app_webview.dart b/lib/src/headless_in_app_webview.dart index c6eb54663..95600195d 100644 --- a/lib/src/headless_in_app_webview.dart +++ b/lib/src/headless_in_app_webview.dart @@ -47,12 +47,18 @@ class HeadlessInAppWebView implements WebView { this.onLongPressHitTestResult, this.onEnterFullscreen, this.onExitFullscreen, + this.onPageCommitVisible, this.androidOnSafeBrowsingHit, this.androidOnPermissionRequest, this.androidOnGeolocationPermissionsShowPrompt, this.androidOnGeolocationPermissionsHidePrompt, + this.androidShouldInterceptRequest, + this.androidOnRenderProcessGone, + this.androidOnRenderProcessResponsive, + this.androidOnRenderProcessUnresponsive, + this.androidOnFormResubmission, + this.androidOnScaleChanged, this.iosOnWebContentProcessDidTerminate, - this.iosOnDidCommit, this.iosOnDidReceiveServerRedirectForProvisionalNavigation, this.initialUrl, this.initialFile, @@ -88,7 +94,7 @@ class HeadlessInAppWebView implements WebView { 'initialFile': this.initialFile, 'initialData': this.initialData?.toMap(), 'initialHeaders': this.initialHeaders, - 'initialOptions': this.initialOptions?.toMap(), + 'initialOptions': this.initialOptions?.toMap() ?? {}, 'contextMenu': this.contextMenu?.toMap() ?? {} }); await _sharedChannel.invokeMethod('createHeadlessWebView', args); @@ -143,7 +149,7 @@ class HeadlessInAppWebView implements WebView { final String initialUrl; @override - final Future Function(InAppWebViewController controller) iosOnDidCommit; + final Future Function(InAppWebViewController controller, String url) onPageCommitVisible; @override final Future Function(InAppWebViewController controller) @@ -276,4 +282,28 @@ class HeadlessInAppWebView implements WebView { @override final void Function(InAppWebViewController controller) onExitFullscreen; + + @override + final Future Function(InAppWebViewController controller, WebResourceRequest request) + androidShouldInterceptRequest; + + @override + final Future Function(InAppWebViewController controller, String url) + androidOnRenderProcessUnresponsive; + + @override + final Future Function(InAppWebViewController controller, String url) + androidOnRenderProcessResponsive; + + @override + final Future Function(InAppWebViewController controller, RenderProcessGoneDetail detail) + androidOnRenderProcessGone; + + @override + final Future Function(InAppWebViewController controller, String url) + androidOnFormResubmission; + + @override + final Future Function(InAppWebViewController controller, double oldScale, double newScale) + androidOnScaleChanged; } \ No newline at end of file diff --git a/lib/src/in_app_browser.dart b/lib/src/in_app_browser.dart index d0dd96c20..8198ff5d1 100755 --- a/lib/src/in_app_browser.dart +++ b/lib/src/in_app_browser.dart @@ -249,12 +249,21 @@ class InAppBrowser { void onExit() {} ///Event fired when the [InAppBrowser] starts to load an [url]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview void onLoadStart(String url) {} ///Event fired when the [InAppBrowser] finishes loading an [url]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview void onLoadStop(String url) {} ///Event fired when the [InAppBrowser] encounters an error loading an [url]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview void onLoadError(String url, int code, String message) {} ///Event fired when the [InAppBrowser] main page receives an HTTP error. @@ -266,12 +275,19 @@ class InAppBrowser { ///[description] represents the description of the HTTP error. On iOS, it is always an empty string. /// ///**NOTE**: available on Android 23+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview void onLoadHttpError(String url, int statusCode, String description) {} ///Event fired when the current [progress] (range 0-100) of loading a page is changed. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int) void onProgressChanged(int progress) {} ///Event fired when the [InAppBrowser] webview receives a [ConsoleMessage]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onConsoleMessage(android.webkit.ConsoleMessage) void onConsoleMessage(ConsoleMessage consoleMessage) {} ///Give the host application a chance to take control when a URL is about to be loaded in the current WebView. This event is not called on the initial load of the WebView. @@ -285,6 +301,9 @@ class InAppBrowser { ///[shouldOverrideUrlLoadingRequest] represents the navigation request. /// ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview // ignore: missing_return Future shouldOverrideUrlLoading(ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) {} @@ -298,6 +317,9 @@ class InAppBrowser { ///[x] represents the current horizontal scroll origin in pixels. /// ///[y] represents the current vertical scroll origin in pixels. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int) + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll void onScrollChanged(int x, int y) {} ///Event fired when [InAppBrowser] recognizes and starts a downloadable file. @@ -305,6 +327,9 @@ class InAppBrowser { ///[url] represents the url of the file. /// ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnDownloadStart] option to `true`. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview void onDownloadStart(String url) {} ///Event fired when the [InAppBrowser] webview finds the `custom-scheme` while loading a resource. Here you can handle the url request and return a [CustomSchemeResponse] to load a specific resource encoded to `base64`. @@ -312,6 +337,8 @@ class InAppBrowser { ///[scheme] represents the scheme of the url. /// ///[url] represents the url of the request. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler // ignore: missing_return Future onLoadResourceCustomScheme( String scheme, String url) {} @@ -322,12 +349,18 @@ class InAppBrowser { ///[onCreateWindowRequest] represents the request. /// ///**NOTE**: on Android you need to set [AndroidInAppWebViewOptions.supportMultipleWindows] option to `true`. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview void onCreateWindow(OnCreateWindowRequest onCreateWindowRequest) {} ///Event fired when javascript calls the `alert()` method to display an alert dialog. ///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. /// ///[message] represents the message to be displayed in the alert dialog. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview // ignore: missing_return Future onJsAlert(String message) {} @@ -335,6 +368,9 @@ class InAppBrowser { ///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. /// ///[message] represents the message to be displayed in the alert dialog. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview // ignore: missing_return Future onJsConfirm(String message) {} @@ -343,12 +379,18 @@ class InAppBrowser { /// ///[message] represents the message to be displayed in the alert dialog. ///[defaultValue] represents the default value displayed in the prompt dialog. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview // ignore: missing_return Future onJsPrompt(String message, String defaultValue) {} ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. /// ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview // ignore: missing_return Future onReceivedHttpAuthRequest( HttpAuthChallenge challenge) {} @@ -357,6 +399,9 @@ class InAppBrowser { ///The host application must return either [ServerTrustAuthResponse] instance with [ServerTrustAuthResponseAction.CANCEL] or [ServerTrustAuthResponseAction.PROCEED]. /// ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview // ignore: missing_return Future onReceivedServerTrustAuthRequest( ServerTrustChallenge challenge) {} @@ -367,6 +412,9 @@ class InAppBrowser { ///Note that, multiple layers in chromium network stack might be caching the responses. /// ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview // ignore: missing_return Future onReceivedClientCertRequest( ClientCertChallenge challenge) {} @@ -379,6 +427,8 @@ class InAppBrowser { ///[numberOfMatches] represents how many matches have been found. /// ///[isDoneCounting] whether the find operation has actually completed. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setFindListener(android.webkit.WebView.FindListener) void onFindResultReceived( int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) {} @@ -426,6 +476,8 @@ class InAppBrowser { ///[url] represents the url being visited. /// ///[androidIsReload] indicates if this url is being reloaded. Available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean) void onUpdateVisitedHistory(String url, bool androidIsReload) {} ///Event fired when `window.print()` is called from JavaScript side. @@ -438,14 +490,34 @@ class InAppBrowser { ///Event fired when an HTML element of the webview has been clicked and held. /// ///[hitTestResult] represents the hit result for hitting an HTML elements. + /// + ///**Official Android API**: https://developer.android.com/reference/android/view/View#setOnLongClickListener(android.view.View.OnLongClickListener) + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer void onLongPressHitTestResult(InAppWebViewHitTestResult hitTestResult) {} ///Event fired when the current page has entered full screen mode. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback) + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification void onEnterFullscreen() {} ///Event fired when the current page has exited full screen mode. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView() + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification void onExitFullscreen() {} + ///Called when the web view begins to receive web content. + /// + ///This event occurs early in the document loading process, and as such + ///you should expect that linked resources (for example, CSS and images) may not be available. + /// + ///[url] represents the URL corresponding to the page navigation that triggered this callback. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview + void onPageCommitVisible(String url) {} + ///Event fired when the WebView notifies that a loading URL has been flagged by Safe Browsing. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible. /// @@ -454,6 +526,8 @@ class InAppBrowser { ///[threatType] represents the reason the resource was caught by Safe Browsing, corresponding to a [SafeBrowsingThreat]. /// ///**NOTE**: available only on Android 27+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse) // ignore: missing_return Future androidOnSafeBrowsingHit( String url, SafeBrowsingThreat threatType) {} @@ -465,6 +539,8 @@ class InAppBrowser { ///[resources] represents the array of resources the web content wants to access. /// ///**NOTE**: available only on Android 23+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest) // ignore: missing_return Future androidOnPermissionRequest( String origin, List resources) {} @@ -476,6 +552,8 @@ class InAppBrowser { ///[origin] represents the origin of the web content attempting to use the Geolocation API. /// ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback) Future // ignore: missing_return androidOnGeolocationPermissionsShowPrompt(String origin) {} @@ -484,21 +562,111 @@ class InAppBrowser { ///Any related UI should therefore be hidden. /// ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt() void androidOnGeolocationPermissionsHidePrompt() {} - ///Invoked when the web view's web content process is terminated. + ///Notify the host application of a resource request and allow the application to return the data. + ///If the return value is `null`, the WebView will continue to load the resource as usual. + ///Otherwise, the return response and data will be used. /// - ///**NOTE**: available only on iOS. - void iosOnWebContentProcessDidTerminate() {} + ///This callback is invoked for a variety of URL schemes (e.g., `http(s):`, `data:`, `file:`, etc.), + ///not only those schemes which send requests over the network. + ///This is not called for `javascript:` URLs, `blob:` URLs, or for assets accessed via `file:///android_asset/` or `file:///android_res/` URLs. + /// + ///In the case of redirects, this is only called for the initial resource URL, not any subsequent redirect URLs. + /// + ///[request] Object containing the details of the request. + /// + ///**NOTE**: available only on Android. + /// + ///**Official Android API**: + ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest) + ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20java.lang.String) + Future + // ignore: missing_return + androidShouldInterceptRequest(WebResourceRequest request) {} - ///Called when the web view begins to receive web content. + ///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript. + /// + ///If a WebView fails to process an input event, or successfully navigate to a new URL within a reasonable time frame, the renderer is considered to be unresponsive, and this callback will be called. + /// + ///This callback will continue to be called at regular intervals as long as the renderer remains unresponsive. + ///If the renderer becomes responsive again, [androidOnRenderProcessResponsive] will be called once, + ///and this method will not subsequently be called unless another period of unresponsiveness is detected. + /// + ///The minimum interval between successive calls to `androidOnRenderProcessUnresponsive` is 5 seconds. + /// + ///No action is taken by WebView as a result of this method call. + ///Applications may choose to terminate the associated renderer via the object that is passed to this callback, + ///if in multiprocess mode, however this must be accompanied by correctly handling [androidOnRenderProcessGone] for this WebView, + ///and all other WebViews associated with the same renderer. Failure to do so will result in application termination. + /// + ///**NOTE**: available only on Android 29+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) + Future + // ignore: missing_return + androidOnRenderProcessUnresponsive(String url) {} + + ///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive. + /// + ///After a WebView renderer becomes unresponsive, which is notified to the application by [androidOnRenderProcessUnresponsive], + ///it is possible for the blocking renderer task to complete, returning the renderer to a responsive state. + ///In that case, this method is called once to indicate responsiveness. + /// + ///No action is taken by WebView as a result of this method call. + /// + ///**NOTE**: available only on Android 29+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) + Future + // ignore: missing_return + androidOnRenderProcessResponsive(String url) {} + + ///Event fired when the given WebView's render process has exited. + ///The application's implementation of this callback should only attempt to clean up the WebView. + ///The WebView should be removed from the view hierarchy, all references to it should be cleaned up. + /// + ///[detail] the reason why it exited. + /// + ///**NOTE**: available only on Android 26+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail) + void androidOnRenderProcessGone(RenderProcessGoneDetail detail) {} + + ///As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data. + /// + ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message) + Future + // ignore: missing_return + androidOnFormResubmission(String url) {} + + ///Event fired when the scale applied to the WebView has changed. + /// + ///[oldScale] The old scale factor. + /// + ///[newScale] The new scale factor. + /// + ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float) + void androidOnScaleChanged(double oldScale, double newScale) {} + + ///Invoked when the web view's web content process is terminated. /// ///**NOTE**: available only on iOS. - void iosOnDidCommit() {} + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi + void iosOnWebContentProcessDidTerminate() {} ///Called when a web view receives a server redirect. /// ///**NOTE**: available only on iOS. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview void iosOnDidReceiveServerRedirectForProvisionalNavigation() {} void throwIsAlreadyOpened({String message = ''}) { diff --git a/lib/src/in_app_webview.dart b/lib/src/in_app_webview.dart index 9c0388d29..9af844289 100755 --- a/lib/src/in_app_webview.dart +++ b/lib/src/in_app_webview.dart @@ -39,7 +39,7 @@ class InAppWebView extends StatefulWidget implements WebView { this.initialFile, this.initialData, this.initialHeaders = const {}, - @required this.initialOptions, + this.initialOptions, this.contextMenu, this.onWebViewCreated, this.onLoadStart, @@ -70,12 +70,18 @@ class InAppWebView extends StatefulWidget implements WebView { this.onLongPressHitTestResult, this.onEnterFullscreen, this.onExitFullscreen, + this.onPageCommitVisible, this.androidOnSafeBrowsingHit, this.androidOnPermissionRequest, this.androidOnGeolocationPermissionsShowPrompt, this.androidOnGeolocationPermissionsHidePrompt, + this.androidShouldInterceptRequest, + this.androidOnRenderProcessGone, + this.androidOnRenderProcessResponsive, + this.androidOnRenderProcessUnresponsive, + this.androidOnFormResubmission, + this.androidOnScaleChanged, this.iosOnWebContentProcessDidTerminate, - this.iosOnDidCommit, this.iosOnDidReceiveServerRedirectForProvisionalNavigation, this.gestureRecognizers, }) : super(key: key); @@ -121,7 +127,7 @@ class InAppWebView extends StatefulWidget implements WebView { final ContextMenu contextMenu; @override - final Future Function(InAppWebViewController controller) iosOnDidCommit; + final Future Function(InAppWebViewController controller, String url) onPageCommitVisible; @override final Future Function(InAppWebViewController controller) @@ -254,6 +260,30 @@ class InAppWebView extends StatefulWidget implements WebView { @override final void Function(InAppWebViewController controller) onExitFullscreen; + + @override + final Future Function(InAppWebViewController controller, WebResourceRequest request) + androidShouldInterceptRequest; + + @override + final Future Function(InAppWebViewController controller, String url) + androidOnRenderProcessUnresponsive; + + @override + final Future Function(InAppWebViewController controller, String url) + androidOnRenderProcessResponsive; + + @override + final Future Function(InAppWebViewController controller, RenderProcessGoneDetail detail) + androidOnRenderProcessGone; + + @override + final Future Function(InAppWebViewController controller, String url) + androidOnFormResubmission; + + @override + final Future Function(InAppWebViewController controller, double oldScale, double newScale) + androidOnScaleChanged; } class _InAppWebViewState extends State { diff --git a/lib/src/in_app_webview_controller.dart b/lib/src/in_app_webview_controller.dart index eac1da2cb..1a6bee077 100644 --- a/lib/src/in_app_webview_controller.dart +++ b/lib/src/in_app_webview_controller.dart @@ -25,9 +25,9 @@ class InAppWebViewController { WebView _webview; MethodChannel _channel; static MethodChannel _staticChannel = - MethodChannel('com.pichillilorenzo/flutter_inappwebview_static'); + MethodChannel('com.pichillilorenzo/flutter_inappwebview_static'); Map javaScriptHandlersMap = - HashMap(); + HashMap(); // ignore: unused_field bool _isOpened = false; @@ -107,38 +107,38 @@ class InAppWebViewController { String url = call.arguments["url"]; String method = call.arguments["method"]; Map headers = - call.arguments["headers"]?.cast(); + call.arguments["headers"]?.cast(); bool isForMainFrame = call.arguments["isForMainFrame"]; bool androidHasGesture = call.arguments["androidHasGesture"]; bool androidIsRedirect = call.arguments["androidIsRedirect"]; int iosWKNavigationType = call.arguments["iosWKNavigationType"]; ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest = - ShouldOverrideUrlLoadingRequest( - url: url, - method: method, - headers: headers, - isForMainFrame: isForMainFrame, - androidHasGesture: androidHasGesture, - androidIsRedirect: androidIsRedirect, - iosWKNavigationType: - IOSWKNavigationType.fromValue(iosWKNavigationType)); + ShouldOverrideUrlLoadingRequest( + url: url, + method: method, + headers: headers, + isForMainFrame: isForMainFrame, + androidHasGesture: androidHasGesture, + androidIsRedirect: androidIsRedirect, + iosWKNavigationType: + IOSWKNavigationType.fromValue(iosWKNavigationType)); if (_webview != null && _webview.shouldOverrideUrlLoading != null) return (await _webview.shouldOverrideUrlLoading( - this, shouldOverrideUrlLoadingRequest)) + this, shouldOverrideUrlLoadingRequest)) ?.toMap(); else if (_inAppBrowser != null) return (await _inAppBrowser - .shouldOverrideUrlLoading(shouldOverrideUrlLoadingRequest)) + .shouldOverrideUrlLoading(shouldOverrideUrlLoadingRequest)) ?.toMap(); break; case "onConsoleMessage": String message = call.arguments["message"]; ConsoleMessageLevel messageLevel = - ConsoleMessageLevel.fromValue(call.arguments["messageLevel"]); + ConsoleMessageLevel.fromValue(call.arguments["messageLevel"]); ConsoleMessage consoleMessage = - ConsoleMessage(message: message, messageLevel: messageLevel); + ConsoleMessage(message: message, messageLevel: messageLevel); if (_webview != null && _webview.onConsoleMessage != null) _webview.onConsoleMessage(this, consoleMessage); else if (_inAppBrowser != null) @@ -163,7 +163,7 @@ class InAppWebViewController { if (_webview != null && _webview.onLoadResourceCustomScheme != null) { try { var response = - await _webview.onLoadResourceCustomScheme(this, scheme, url); + await _webview.onLoadResourceCustomScheme(this, scheme, url); return (response != null) ? response.toJson() : null; } catch (error) { print(error); @@ -172,7 +172,7 @@ class InAppWebViewController { } else if (_inAppBrowser != null) { try { var response = - await _inAppBrowser.onLoadResourceCustomScheme(scheme, url); + await _inAppBrowser.onLoadResourceCustomScheme(scheme, url); return (response != null) ? response.toJson() : null; } catch (error) { print(error); @@ -191,7 +191,7 @@ class InAppWebViewController { androidIsDialog: androidIsDialog, androidIsUserGesture: androidIsUserGesture, iosWKNavigationType: - IOSWKNavigationType.fromValue(iosWKNavigationType)); + IOSWKNavigationType.fromValue(iosWKNavigationType)); if (_webview != null && _webview.onCreateWindow != null) _webview.onCreateWindow(this, onCreateWindowRequest); @@ -203,11 +203,11 @@ class InAppWebViewController { if (_webview != null && _webview.androidOnGeolocationPermissionsShowPrompt != null) return (await _webview.androidOnGeolocationPermissionsShowPrompt( - this, origin)) + this, origin)) ?.toMap(); else if (_inAppBrowser != null) return (await _inAppBrowser - .androidOnGeolocationPermissionsShowPrompt(origin)) + .androidOnGeolocationPermissionsShowPrompt(origin)) ?.toMap(); break; case "onGeolocationPermissionsHidePrompt": @@ -217,6 +217,77 @@ class InAppWebViewController { else if (_inAppBrowser != null) await _inAppBrowser.androidOnGeolocationPermissionsHidePrompt(); break; + case "shouldInterceptRequest": + String url = call.arguments["url"]; + String method = call.arguments["method"]; + Map headers = + call.arguments["headers"]?.cast(); + bool isForMainFrame = call.arguments["isForMainFrame"]; + bool hasGesture = call.arguments["hasGesture"]; + bool isRedirect = call.arguments["isRedirect"]; + + var request = new WebResourceRequest( + url: url, + method: method, + headers: headers, + isForMainFrame: isForMainFrame, + hasGesture: hasGesture, + isRedirect: isRedirect); + + if (_webview != null && _webview.androidShouldInterceptRequest != null) + return (await _webview.androidShouldInterceptRequest(this, request)) + ?.toMap(); + else if (_inAppBrowser != null) + return (await _inAppBrowser.androidShouldInterceptRequest(request)) + ?.toMap(); + break; + case "onRenderProcessUnresponsive": + String url = call.arguments["url"]; + if (_webview != null && + _webview.androidOnRenderProcessUnresponsive != null) + return (await _webview.androidOnRenderProcessUnresponsive(this, url)) + ?.toMap(); + else if (_inAppBrowser != null) + return (await _inAppBrowser.androidOnRenderProcessUnresponsive(url)) + ?.toMap(); + break; + case "onRenderProcessResponsive": + String url = call.arguments["url"]; + if (_webview != null && + _webview.androidOnRenderProcessResponsive != null) + return (await _webview.androidOnRenderProcessResponsive(this, url)) + ?.toMap(); + else if (_inAppBrowser != null) + return (await _inAppBrowser.androidOnRenderProcessResponsive(url)) + ?.toMap(); + break; + case "onRenderProcessGone": + bool didCrash = call.arguments["didCrash"]; + RendererPriority rendererPriorityAtExit = RendererPriority.fromValue( + call.arguments["rendererPriorityAtExit"]); + var detail = RenderProcessGoneDetail( + didCrash: didCrash, rendererPriorityAtExit: rendererPriorityAtExit); + + if (_webview != null && _webview.androidOnRenderProcessGone != null) + _webview.androidOnRenderProcessGone(this, detail); + else if (_inAppBrowser != null) + _inAppBrowser.androidOnRenderProcessGone(detail); + break; + case "onFormResubmission": + String url = call.arguments["url"]; + if (_webview != null && _webview.androidOnFormResubmission != null) + return (await _webview.androidOnFormResubmission(this, url))?.toMap(); + else if (_inAppBrowser != null) + return (await _inAppBrowser.androidOnFormResubmission(url))?.toMap(); + break; + case "onScaleChanged": + double oldScale = call.arguments["oldScale"]; + double newScale = call.arguments["newScale"]; + if (_webview != null && _webview.androidOnScaleChanged != null) + _webview.androidOnScaleChanged(this, oldScale, newScale); + else if (_inAppBrowser != null) + _inAppBrowser.androidOnScaleChanged(oldScale, newScale); + break; case "onJsAlert": String message = call.arguments["message"]; if (_webview != null && _webview.onJsAlert != null) @@ -244,9 +315,10 @@ class InAppWebViewController { case "onSafeBrowsingHit": String url = call.arguments["url"]; SafeBrowsingThreat threatType = - SafeBrowsingThreat.fromValue(call.arguments["threatType"]); + SafeBrowsingThreat.fromValue(call.arguments["threatType"]); if (_webview != null && _webview.androidOnSafeBrowsingHit != null) - return (await _webview.androidOnSafeBrowsingHit(this, url, threatType)) + return (await _webview.androidOnSafeBrowsingHit( + this, url, threatType)) ?.toMap(); else if (_inAppBrowser != null) return (await _inAppBrowser.androidOnSafeBrowsingHit(url, threatType)) @@ -285,13 +357,14 @@ class InAppWebViewController { error: error, message: message, serverCertificate: serverCertificate); - if (_webview != null && _webview.onReceivedServerTrustAuthRequest != null) + if (_webview != null && + _webview.onReceivedServerTrustAuthRequest != null) return (await _webview.onReceivedServerTrustAuthRequest( - this, challenge)) + this, challenge)) ?.toMap(); else if (_inAppBrowser != null) return (await _inAppBrowser - .onReceivedServerTrustAuthRequest(challenge)) + .onReceivedServerTrustAuthRequest(challenge)) ?.toMap(); break; case "onReceivedClientCertRequest": @@ -325,11 +398,11 @@ class InAppWebViewController { List resources = call.arguments["resources"].cast(); if (_webview != null && _webview.androidOnPermissionRequest != null) return (await _webview.androidOnPermissionRequest( - this, origin, resources)) + this, origin, resources)) ?.toMap(); else if (_inAppBrowser != null) return (await _inAppBrowser.androidOnPermissionRequest( - origin, resources)) + origin, resources)) ?.toMap(); break; case "onUpdateVisitedHistory": @@ -347,10 +420,11 @@ class InAppWebViewController { else if (_inAppBrowser != null) _inAppBrowser.iosOnWebContentProcessDidTerminate(); break; - case "onDidCommit": - if (_webview != null && _webview.iosOnDidCommit != null) - _webview.iosOnDidCommit(this); - else if (_inAppBrowser != null) _inAppBrowser.iosOnDidCommit(); + case "onPageCommitVisible": + String url = call.arguments["url"]; + if (_webview != null && _webview.onPageCommitVisible != null) + _webview.onPageCommitVisible(this, url); + else if (_inAppBrowser != null) _inAppBrowser.onPageCommitVisible(url); break; case "onDidReceiveServerRedirectForProvisionalNavigation": if (_webview != null && @@ -362,11 +436,13 @@ class InAppWebViewController { break; case "onLongPressHitTestResult": Map hitTestResultMap = - call.arguments["hitTestResult"]; - InAppWebViewHitTestResultType type = InAppWebViewHitTestResultType.fromValue( - hitTestResultMap["type"].toInt()); + call.arguments["hitTestResult"]; + InAppWebViewHitTestResultType type = + InAppWebViewHitTestResultType.fromValue( + hitTestResultMap["type"].toInt()); String extra = hitTestResultMap["extra"]; - InAppWebViewHitTestResult hitTestResult = InAppWebViewHitTestResult(type: type, extra: extra); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult(type: type, extra: extra); if (_webview != null && _webview.onLongPressHitTestResult != null) _webview.onLongPressHitTestResult(this, hitTestResult); @@ -383,11 +459,13 @@ class InAppWebViewController { if (contextMenu != null && contextMenu.onCreateContextMenu != null) { Map hitTestResultMap = - call.arguments["hitTestResult"]; - InAppWebViewHitTestResultType type = InAppWebViewHitTestResultType.fromValue( - hitTestResultMap["type"].toInt()); + call.arguments["hitTestResult"]; + InAppWebViewHitTestResultType type = + InAppWebViewHitTestResultType.fromValue( + hitTestResultMap["type"].toInt()); String extra = hitTestResultMap["extra"]; - InAppWebViewHitTestResult hitTestResult = InAppWebViewHitTestResult(type: type, extra: extra); + InAppWebViewHitTestResult hitTestResult = + InAppWebViewHitTestResult(type: type, extra: extra); contextMenu.onCreateContextMenu(hitTestResult); } @@ -417,7 +495,8 @@ class InAppWebViewController { String iosId = call.arguments["iosId"]; String title = call.arguments["title"]; - ContextMenuItem menuItemClicked = ContextMenuItem(androidId: androidId, iosId: iosId, title: title, action: null); + ContextMenuItem menuItemClicked = ContextMenuItem( + androidId: androidId, iosId: iosId, title: title, action: null); for (var menuItem in contextMenu.menuItems) { if ((Platform.isAndroid && menuItem.androidId == androidId) || @@ -434,18 +513,14 @@ class InAppWebViewController { } break; case "onEnterFullscreen": - if (_webview != null && - _webview.onEnterFullscreen != null) + if (_webview != null && _webview.onEnterFullscreen != null) _webview.onEnterFullscreen(this); - else if (_inAppBrowser != null) - _inAppBrowser.onEnterFullscreen(); + else if (_inAppBrowser != null) _inAppBrowser.onEnterFullscreen(); break; case "onExitFullscreen": - if (_webview != null && - _webview.onExitFullscreen != null) + if (_webview != null && _webview.onExitFullscreen != null) _webview.onExitFullscreen(this); - else if (_inAppBrowser != null) - _inAppBrowser.onExitFullscreen(); + else if (_inAppBrowser != null) _inAppBrowser.onExitFullscreen(); break; case "onCallJsHandler": String handlerName = call.arguments["handlerName"]; @@ -616,8 +691,8 @@ class InAppWebViewController { Uint8List body = Uint8List.fromList(argMap["body"].cast()); String mode = argMap["mode"]; FetchRequestCredential credentials = - FetchRequest.createFetchRequestCredentialFromMap( - argMap["credentials"]); + FetchRequest.createFetchRequestCredentialFromMap( + argMap["credentials"]); String cache = argMap["cache"]; String redirect = argMap["redirect"]; String referrer = argMap["referrer"]; @@ -639,7 +714,8 @@ class InAppWebViewController { integrity: integrity, keepalive: keepalive); - if (_webview != null && _webview.shouldInterceptFetchRequest != null) + if (_webview != null && + _webview.shouldInterceptFetchRequest != null) return jsonEncode( await _webview.shouldInterceptFetchRequest(this, request)); else if (_inAppBrowser != null) @@ -713,7 +789,7 @@ class InAppWebViewController { try { var htmlRequest = await client.getUrl(url); html = - await (await htmlRequest.close()).transform(Utf8Decoder()).join(); + await (await htmlRequest.close()).transform(Utf8Decoder()).join(); } catch (e) { print(e); } @@ -756,8 +832,8 @@ class InAppWebViewController { manifestUrl = manifestUrl.substring(1); } manifestUrl = ((assetPathBase == null) - ? url.scheme + "://" + url.host + "/" - : assetPathBase) + + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + manifestUrl; } continue; @@ -796,7 +872,7 @@ class InAppWebViewController { if (manifestFound) { Map manifest = - json.decode(await manifestResponse.transform(Utf8Decoder()).join()); + json.decode(await manifestResponse.transform(Utf8Decoder()).join()); if (manifest.containsKey("icons")) { for (Map icon in manifest["icons"]) { favicons.addAll(_createFavicons(url, assetPathBase, icon["src"], @@ -822,16 +898,16 @@ class InAppWebViewController { urlIcon = urlIcon.substring(1); } urlIcon = ((assetPathBase == null) - ? url.scheme + "://" + url.host + "/" - : assetPathBase) + + ? url.scheme + "://" + url.host + "/" + : assetPathBase) + urlIcon; } if (isManifest) { rel = (sizes != null) ? urlSplitted[urlSplitted.length - 1] - .replaceFirst("-" + sizes, "") - .split(" ")[0] - .split(".")[0] + .replaceFirst("-" + sizes, "") + .split(" ")[0] + .split(".")[0] : null; } if (sizes != null && sizes.isNotEmpty && sizes != "any") { @@ -879,10 +955,10 @@ class InAppWebViewController { ///The [androidHistoryUrl] parameter is the URL to use as the history entry. The default value is `about:blank`. If non-null, this must be a valid URL. This parameter is used only on Android. Future loadData( {@required String data, - String mimeType = "text/html", - String encoding = "utf8", - String baseUrl = "about:blank", - String androidHistoryUrl = "about:blank"}) async { + String mimeType = "text/html", + String encoding = "utf8", + String baseUrl = "about:blank", + String androidHistoryUrl = "about:blank"}) async { assert(data != null); Map args = {}; args.putIfAbsent('data', () => data); @@ -924,7 +1000,7 @@ class InAppWebViewController { ///``` Future loadFile( {@required String assetFilePath, - Map headers = const {}}) async { + Map headers = const {}}) async { assert(assetFilePath != null && assetFilePath.isNotEmpty); Map args = {}; args.putIfAbsent('url', () => assetFilePath); @@ -1088,7 +1164,7 @@ class InAppWebViewController { ///Forbidden names for JavaScript handlers are defined in [javaScriptHandlerForbiddenNames]. void addJavaScriptHandler( {@required String handlerName, - @required JavaScriptHandlerCallback callback}) { + @required JavaScriptHandlerCallback callback}) { assert(!javaScriptHandlerForbiddenNames.contains(handlerName)); this.javaScriptHandlersMap[handlerName] = (callback); } @@ -1122,9 +1198,9 @@ class InAppWebViewController { Map args = {}; InAppWebViewGroupOptions inAppWebViewGroupOptions = - InAppWebViewGroupOptions(); + InAppWebViewGroupOptions(); Map options = - await _channel.invokeMethod('getOptions', args); + await _channel.invokeMethod('getOptions', args); if (options != null) { options = options.cast(); inAppWebViewGroupOptions.crossPlatform = @@ -1146,7 +1222,7 @@ class InAppWebViewController { Future getCopyBackForwardList() async { Map args = {}; Map result = - await _channel.invokeMethod('getCopyBackForwardList', args); + await _channel.invokeMethod('getCopyBackForwardList', args); result = result.cast(); List historyListMap = result["history"]; @@ -1307,9 +1383,11 @@ class InAppWebViewController { ///**NOTE**: On iOS it is implemented using JavaScript. Future getHitTestResult() async { Map args = {}; - var hitTestResultMap = await _channel.invokeMethod('getHitTestResult', args); - InAppWebViewHitTestResultType type = InAppWebViewHitTestResultType.fromValue( - hitTestResultMap["type"].toInt()); + var hitTestResultMap = + await _channel.invokeMethod('getHitTestResult', args); + InAppWebViewHitTestResultType type = + InAppWebViewHitTestResultType.fromValue( + hitTestResultMap["type"].toInt()); String extra = hitTestResultMap["extra"]; return InAppWebViewHitTestResult(type: type, extra: extra); } @@ -1370,6 +1448,57 @@ class AndroidInAppWebViewController { return await _controller._channel.invokeMethod('getOriginalUrl', args); } + ///Scrolls the contents of this WebView down by half the page size. + ///Returns `true` if the page was scrolled. + /// + ///[bottom] `true` to jump to bottom of page + Future pageDown({@required bool bottom}) async { + assert(bottom != null); + Map args = {}; + args.putIfAbsent("bottom", () => bottom); + return await _controller._channel.invokeMethod('pageDown', args); + } + + ///Scrolls the contents of this WebView up by half the view size. + ///Returns `true` if the page was scrolled. + /// + ///[bottom] `true` to jump to the top of the page + Future pageUp({@required bool top}) async { + assert(top != null); + Map args = {}; + args.putIfAbsent("top", () => top); + return await _controller._channel.invokeMethod('pageUp', args); + } + + ///Saves the current view as a web archive. + ///Returns the filename under which the file was saved, or `null` if saving the file failed. + /// + ///[basename] the filename where the archive should be placed. This value cannot be `null`. + /// + ///[autoname] if `false`, takes basename to be a file. + ///If `true`, [basename] is assumed to be a directory in which a filename will be chosen according to the URL of the current page. + Future saveWebArchive({@required String basename, @required bool autoname}) async { + assert(basename != null && autoname != null); + Map args = {}; + args.putIfAbsent("basename", () => basename); + args.putIfAbsent("autoname", () => autoname); + return await _controller._channel.invokeMethod('saveWebArchive', args); + } + + ///Performs zoom in in this WebView. + ///Returns `true` if zoom in succeeds, `false` if no zoom changes. + Future zoomIn() async { + Map args = {}; + return await _controller._channel.invokeMethod('zoomIn', args); + } + + ///Performs zoom out in this WebView. + ///Returns `true` if zoom out succeeds, `false` if no zoom changes. + Future zoomOut() async { + Map args = {}; + return await _controller._channel.invokeMethod('zoomOut', args); + } + ///Clears the client certificate preferences stored in response to proceeding/cancelling client cert requests. ///Note that WebView automatically clears these preferences when the system keychain is updated. ///The preferences are shared by all the WebViews that are created by the embedder application. @@ -1379,7 +1508,8 @@ class AndroidInAppWebViewController { ///**NOTE**: available on Android 21+. static Future clearClientCertPreferences() async { Map args = {}; - await InAppWebViewController._staticChannel.invokeMethod('clearClientCertPreferences', args); + await InAppWebViewController._staticChannel + .invokeMethod('clearClientCertPreferences', args); } ///Returns a URL pointing to the privacy policy for Safe Browsing reporting. This value will never be `null`. @@ -1406,13 +1536,29 @@ class AndroidInAppWebViewController { ///[hosts] represents the list of hosts. This value must never be null. /// ///**NOTE**: available only on Android 27+. - static Future setSafeBrowsingWhitelist({@required List hosts}) async { + static Future setSafeBrowsingWhitelist( + {@required List hosts}) async { assert(hosts != null); Map args = {}; args.putIfAbsent('hosts', () => hosts); return await InAppWebViewController._staticChannel .invokeMethod('setSafeBrowsingWhitelist', args); } + + ///If WebView has already been loaded into the current process this method will return the package that was used to load it. + ///Otherwise, the package that would be used if the WebView was loaded right now will be returned; + ///this does not cause WebView to be loaded, so this information may become outdated at any time. + ///The WebView package changes either when the current WebView package is updated, disabled, or uninstalled. + ///It can also be changed through a Developer Setting. If the WebView package changes, any app process that + ///has loaded WebView will be killed. + ///The next time the app starts and loads WebView it will use the new WebView package instead. + /// + ///**NOTE**: available only on Android 26+. + static Future getCurrentWebViewPackage() async { + Map args = {}; + Map packageInfo = (await InAppWebViewController._staticChannel.invokeMethod('getCurrentWebViewPackage', args))?.cast(); + return AndroidWebViewPackageInfo.fromMap(packageInfo); + } } ///InAppWebViewControllerIOS class represents the iOS controller that contains only ios-specific methods for the WebView. diff --git a/lib/src/types.dart b/lib/src/types.dart index fa15b7dc2..63a4a7cc0 100755 --- a/lib/src/types.dart +++ b/lib/src/types.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:uuid/uuid.dart'; import 'package:flutter/foundation.dart'; +import 'webview.dart'; import 'webview_options.dart'; var uuidGenerator = new Uuid(); @@ -23,7 +24,9 @@ typedef dynamic JavaScriptHandlerCallback(List arguments); ///Class representing the level of a console message. class ConsoleMessageLevel { final int _value; + const ConsoleMessageLevel._internal(this._value); + static ConsoleMessageLevel fromValue(int value) { if (value != null && value >= 0 && value <= 4) return ConsoleMessageLevel._internal(value); @@ -31,6 +34,7 @@ class ConsoleMessageLevel { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -60,7 +64,7 @@ class ConsoleMessageLevel { int get hashCode => _value.hashCode; } -///Public class representing a resource response of the [InAppBrowser] WebView. +///Class representing a resource response of the [InAppBrowser] WebView. ///It is used by the method [InAppBrowser.onLoadResource()]. class LoadedResource { ///A string representing the type of resource. @@ -113,38 +117,94 @@ class InAppWebViewInitialData { } } -/* -///Public class representing a resource request of the WebView. -///It is used by the event [shouldInterceptRequest()]. +///Class representing a resource request of the WebView used by the event [WebView.androidShouldInterceptRequest]. +///Available only on Android. +/// +///**Official Android API**: https://developer.android.com/reference/android/webkit/WebResourceRequest class WebResourceRequest { - + ///The URL for which the resource request was made. String url; + ///The headers associated with the request. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it will be always `null`. Map headers; + ///The method associated with the request, for example `GET`. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it will be always "GET". String method; + ///Gets whether a gesture (such as a click) was associated with the request. + ///For security reasons in certain situations this method may return `false` even though + ///the sequence of events which caused the request to be created was initiated by a user + ///gesture. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it will be always `false`. + bool hasGesture; + ///Whether the request was made in order to fetch the main frame's document. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it will be always `true`. + bool isForMainFrame; + ///Whether the request was a result of a server-side redirect. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it will be always `false`. + bool isRedirect; - WebResourceRequest({@required this.url, @required this.headers, @required this.method}); - + WebResourceRequest({ + @required this.url, + this.headers, + this.method, + this.hasGesture, + this.isForMainFrame, + this.isRedirect + }); } -///Public class representing a resource response of the WebView. -///It is used by the event [shouldInterceptRequest()]. +///Class representing a resource response of the WebView used by the event [WebView.androidShouldInterceptRequest]. +///Available only on Android. +/// +///**Official Android API**: https://developer.android.com/reference/android/webkit/WebResourceResponse class WebResourceResponse { + ///The resource response's MIME type, for example `text/html`. String contentType; + ///The resource response's encoding. The default value is `utf-8`. String contentEncoding; + ///The data provided by the resource response. Uint8List data; - - WebResourceResponse({@required this.contentType, this.contentEncoding = "utf-8", @required this.data}): assert(contentType != null && contentEncoding != null && data != null); + ///The headers for the resource response. If [headers] isn't `null`, then you need to set also [statusCode] and [reasonPhrase]. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it won't be used. + Map headers; + ///The status code needs to be in the ranges [100, 299], [400, 599]. Causing a redirect by specifying a 3xx code is not supported. + ///If statusCode is set, then you need to set also [headers] and [reasonPhrase]. This value cannot be `null`. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it won't be used. + int statusCode; + ///The phrase describing the status code, for example `"OK"`. Must be non-empty. + ///If reasonPhrase is set, then you need to set also [headers] and [reasonPhrase]. This value cannot be `null`. + /// + ///**NOTE**: Available on Android 21+. For Android < 21 it won't be used. + String reasonPhrase; + + WebResourceResponse({ + this.contentType = "", + this.contentEncoding = "utf-8", + this.data = null, + this.headers, + this.statusCode, + this.reasonPhrase + }); Map toMap() { return { "contentType": contentType, "contentEncoding": contentEncoding, - "data": data + "data": data, + "statusCode": statusCode, + "reasonPhrase": reasonPhrase }; } -}*/ +} -///Public class representing the response returned by the [onLoadResourceCustomScheme()] event. +///Class representing the response returned by the [WebView.onLoadResourceCustomScheme] event. ///It allows to load a specific resource. The resource data must be encoded to `base64`. class CustomSchemeResponse { ///Data enconded to 'base64'. @@ -170,7 +230,7 @@ class CustomSchemeResponse { } } -///Public class representing a JavaScript console message from WebCore. +///Class representing a JavaScript console message from WebCore. ///This could be a issued by a call to one of the console logging functions (e.g. console.log('...')) or a JavaScript error on the page. /// ///To receive notifications of these messages, use the [onConsoleMessage] event. @@ -236,7 +296,9 @@ class GeolocationPermissionShowPromptResponse { ///Class used by [JsAlertResponse] class. class JsAlertResponseAction { final int _value; + const JsAlertResponseAction._internal(this._value); + int toValue() => _value; static const CONFIRM = const JsAlertResponseAction._internal(0); @@ -247,7 +309,7 @@ class JsAlertResponseAction { int get hashCode => _value.hashCode; } -///Class represents the response used by the [onJsAlert] event to control a JavaScript alert dialog. +///Class that represents the response used by the [onJsAlert] event to control a JavaScript alert dialog. class JsAlertResponse { ///Message to be displayed in the window. String message; @@ -280,7 +342,9 @@ class JsAlertResponse { ///Class used by [JsConfirmResponse] class. class JsConfirmResponseAction { final int _value; + const JsConfirmResponseAction._internal(this._value); + int toValue() => _value; static const CONFIRM = const JsConfirmResponseAction._internal(0); @@ -330,7 +394,9 @@ class JsConfirmResponse { ///Class used by [JsPromptResponse] class. class JsPromptResponseAction { final int _value; + const JsPromptResponseAction._internal(this._value); + int toValue() => _value; static const CONFIRM = const JsPromptResponseAction._internal(0); @@ -390,7 +456,9 @@ class JsPromptResponse { ///Class that represents the reason the resource was caught by Safe Browsing. class SafeBrowsingThreat { final int _value; + const SafeBrowsingThreat._internal(this._value); + static SafeBrowsingThreat fromValue(int value) { if (value != null && value >= 0 && value <= 4) return SafeBrowsingThreat._internal(value); @@ -398,6 +466,7 @@ class SafeBrowsingThreat { } int toValue() => _value; + String toString() { switch (_value) { case 1: @@ -434,7 +503,9 @@ class SafeBrowsingThreat { ///Class used by [SafeBrowsingResponse] class. class SafeBrowsingResponseAction { final int _value; + const SafeBrowsingResponseAction._internal(this._value); + int toValue() => _value; ///Act as if the user clicked the "back to safety" button. @@ -474,7 +545,9 @@ class SafeBrowsingResponse { ///Class used by [HttpAuthResponse] class. class HttpAuthResponseAction { final int _value; + const HttpAuthResponseAction._internal(this._value); + int toValue() => _value; ///Instructs the WebView to cancel the authentication request. @@ -574,7 +647,9 @@ class HttpAuthCredential { ///Class used by [ServerTrustAuthResponse] class. class ServerTrustAuthResponseAction { final int _value; + const ServerTrustAuthResponseAction._internal(this._value); + int toValue() => _value; ///Instructs the WebView to cancel the authentication challenge. @@ -631,7 +706,9 @@ class ServerTrustChallenge { ///Class used by [ClientCertResponse] class. class ClientCertResponseAction { final int _value; + const ClientCertResponseAction._internal(this._value); + int toValue() => _value; ///Cancel this request. @@ -717,7 +794,9 @@ class Favicon { ///Class that represents an Android-specific class used to override the way the cache is used. class AndroidCacheMode { final int _value; + const AndroidCacheMode._internal(this._value); + static AndroidCacheMode fromValue(int value) { if (value != null && value >= 0 && value <= 3) return AndroidCacheMode._internal(value); @@ -725,6 +804,7 @@ class AndroidCacheMode { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -745,15 +825,13 @@ class AndroidCacheMode { static const LOAD_DEFAULT = const AndroidCacheMode._internal(-1); ///Use cached resources when they are available, even if they have expired. Otherwise load resources from the network. - static const LOAD_CACHE_ELSE_NETWORK = - const AndroidCacheMode._internal(1); + static const LOAD_CACHE_ELSE_NETWORK = const AndroidCacheMode._internal(1); ///Don't use the cache, load from the network. static const LOAD_NO_CACHE = const AndroidCacheMode._internal(2); ///Don't use the network, load from the cache. - static const LOAD_CACHE_ONLY = - const AndroidCacheMode._internal(3); + static const LOAD_CACHE_ONLY = const AndroidCacheMode._internal(3); bool operator ==(value) => value == _value; @@ -766,7 +844,9 @@ class AndroidCacheMode { ///**NOTE**: available on Android 24+. class AndroidActionModeMenuItem { final int _value; + const AndroidActionModeMenuItem._internal(this._value); + static AndroidActionModeMenuItem fromValue(int value) { if (value != null && value != 3 && value >= 0 && value <= 4) return AndroidActionModeMenuItem._internal(value); @@ -774,6 +854,7 @@ class AndroidActionModeMenuItem { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -790,12 +871,10 @@ class AndroidActionModeMenuItem { } ///No menu items should be disabled. - static const MENU_ITEM_NONE = - const AndroidActionModeMenuItem._internal(0); + static const MENU_ITEM_NONE = const AndroidActionModeMenuItem._internal(0); ///Disable menu item "Share". - static const MENU_ITEM_SHARE = - const AndroidActionModeMenuItem._internal(1); + static const MENU_ITEM_SHARE = const AndroidActionModeMenuItem._internal(1); ///Disable menu item "Web Search". static const MENU_ITEM_WEB_SEARCH = @@ -816,7 +895,9 @@ class AndroidActionModeMenuItem { ///**NOTE**: available on Android 29+. class AndroidForceDark { final int _value; + const AndroidForceDark._internal(this._value); + static AndroidForceDark fromValue(int value) { if (value != null && value >= 0 && value <= 2) return AndroidForceDark._internal(value); @@ -824,6 +905,7 @@ class AndroidForceDark { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -842,8 +924,7 @@ class AndroidForceDark { static const FORCE_DARK_OFF = const AndroidForceDark._internal(0); ///Enable force dark dependent on the state of the WebView parent view. - static const FORCE_DARK_AUTO = - const AndroidForceDark._internal(1); + static const FORCE_DARK_AUTO = const AndroidForceDark._internal(1); ///Unconditionally enable force dark. In this mode WebView content will always be rendered so as to emulate a dark theme. static const FORCE_DARK_ON = const AndroidForceDark._internal(2); @@ -857,7 +938,9 @@ class AndroidForceDark { ///Class that represents an Android-specific class used to set the underlying layout algorithm. class AndroidLayoutAlgorithm { final String _value; + const AndroidLayoutAlgorithm._internal(this._value); + static AndroidLayoutAlgorithm fromValue(String value) { return (["NORMAL", "TEXT_AUTOSIZING"].contains(value)) ? AndroidLayoutAlgorithm._internal(value) @@ -865,12 +948,12 @@ class AndroidLayoutAlgorithm { } String toValue() => _value; + @override String toString() => _value; ///NORMAL means no rendering changes. This is the recommended choice for maximum compatibility across different platforms and Android versions. - static const NORMAL = - const AndroidLayoutAlgorithm._internal("NORMAL"); + static const NORMAL = const AndroidLayoutAlgorithm._internal("NORMAL"); ///TEXT_AUTOSIZING boosts font size of paragraphs based on heuristics to make the text readable when viewing a wide-viewport layout in the overview mode. ///It is recommended to enable zoom support [AndroidInAppWebViewOptions.supportZoom] when using this mode. @@ -890,7 +973,9 @@ class AndroidLayoutAlgorithm { ///**NOTE**: available on Android 21+. class AndroidMixedContentMode { final int _value; + const AndroidMixedContentMode._internal(this._value); + static AndroidMixedContentMode fromValue(int value) { if (value != null && value >= 0 && value <= 2) return AndroidMixedContentMode._internal(value); @@ -898,6 +983,7 @@ class AndroidMixedContentMode { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -938,7 +1024,9 @@ class AndroidMixedContentMode { ///Class that represents an iOS-specific class used to set the level of granularity with which the user can interactively select content in the web view. class IOSWKSelectionGranularity { final int _value; + const IOSWKSelectionGranularity._internal(this._value); + static IOSWKSelectionGranularity fromValue(int value) { if (value != null && value >= 0 && value <= 1) return IOSWKSelectionGranularity._internal(value); @@ -946,6 +1034,7 @@ class IOSWKSelectionGranularity { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -961,8 +1050,7 @@ class IOSWKSelectionGranularity { static const DYNAMIC = const IOSWKSelectionGranularity._internal(0); ///Selection endpoints can be placed at any character boundary. - static const CHARACTER = - const IOSWKSelectionGranularity._internal(1); + static const CHARACTER = const IOSWKSelectionGranularity._internal(1); bool operator ==(value) => value == _value; @@ -975,7 +1063,9 @@ class IOSWKSelectionGranularity { ///**NOTE**: available on iOS 10.0+. class IOSWKDataDetectorTypes { final String _value; + const IOSWKDataDetectorTypes._internal(this._value); + static IOSWKDataDetectorTypes fromValue(String value) { return ([ "NONE", @@ -995,6 +1085,7 @@ class IOSWKDataDetectorTypes { } String toValue() => _value; + @override String toString() => _value; @@ -1009,8 +1100,7 @@ class IOSWKDataDetectorTypes { static const LINK = const IOSWKDataDetectorTypes._internal("LINK"); ///Addresses are detected and turned into links. - static const ADDRESS = - const IOSWKDataDetectorTypes._internal("ADDRESS"); + static const ADDRESS = const IOSWKDataDetectorTypes._internal("ADDRESS"); ///Dates and times that are in the future are detected and turned into links. static const CALENDAR_EVENT = @@ -1044,22 +1134,23 @@ class IOSWKDataDetectorTypes { ///Class that represents a floating-point value that determines the rate of deceleration after the user lifts their finger. class IOSUIScrollViewDecelerationRate { final String _value; + const IOSUIScrollViewDecelerationRate._internal(this._value); + static IOSUIScrollViewDecelerationRate fromValue(String value) { - return ([ - "NORMAL", - "FAST" - ].contains(value)) + return (["NORMAL", "FAST"].contains(value)) ? IOSUIScrollViewDecelerationRate._internal(value) : null; } String toValue() => _value; + @override String toString() => _value; ///The default deceleration rate for a scroll view: `0.998`. - static const NORMAL = const IOSUIScrollViewDecelerationRate._internal("NORMAL"); + static const NORMAL = + const IOSUIScrollViewDecelerationRate._internal("NORMAL"); ///A fast deceleration rate for a scroll view: `0.99`. static const FAST = const IOSUIScrollViewDecelerationRate._internal("FAST"); @@ -1073,7 +1164,9 @@ class IOSUIScrollViewDecelerationRate { ///Class that represents the content mode to prefer when loading and rendering a webpage. class UserPreferredContentMode { final int _value; + const UserPreferredContentMode._internal(this._value); + static UserPreferredContentMode fromValue(int value) { if (value != null && value >= 0 && value <= 2) return UserPreferredContentMode._internal(value); @@ -1081,6 +1174,7 @@ class UserPreferredContentMode { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -1095,15 +1189,13 @@ class UserPreferredContentMode { } ///The recommended content mode for the current platform. - static const RECOMMENDED = - const UserPreferredContentMode._internal(0); + static const RECOMMENDED = const UserPreferredContentMode._internal(0); ///Represents content targeting mobile browsers. static const MOBILE = const UserPreferredContentMode._internal(1); ///Represents content targeting desktop browsers. - static const DESKTOP = - const UserPreferredContentMode._internal(2); + static const DESKTOP = const UserPreferredContentMode._internal(2); bool operator ==(value) => value == _value; @@ -1114,7 +1206,9 @@ class UserPreferredContentMode { ///Class that represents an iOS-specific class used to specify the modal presentation style when presenting a view controller. class IOSUIModalPresentationStyle { final int _value; + const IOSUIModalPresentationStyle._internal(this._value); + static IOSUIModalPresentationStyle fromValue(int value) { if (value != null && value >= 0 && value <= 9) return IOSUIModalPresentationStyle._internal(value); @@ -1122,6 +1216,7 @@ class IOSUIModalPresentationStyle { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -1150,20 +1245,16 @@ class IOSUIModalPresentationStyle { } ///A presentation style in which the presented view covers the screen. - static const FULL_SCREEN = - const IOSUIModalPresentationStyle._internal(0); + static const FULL_SCREEN = const IOSUIModalPresentationStyle._internal(0); ///A presentation style that partially covers the underlying content. - static const PAGE_SHEET = - const IOSUIModalPresentationStyle._internal(1); + static const PAGE_SHEET = const IOSUIModalPresentationStyle._internal(1); ///A presentation style that displays the content centered in the screen. - static const FORM_SHEET = - const IOSUIModalPresentationStyle._internal(2); + static const FORM_SHEET = const IOSUIModalPresentationStyle._internal(2); ///A presentation style where the content is displayed over another view controller’s content. - static const CURRENT_CONTEXT = - const IOSUIModalPresentationStyle._internal(3); + static const CURRENT_CONTEXT = const IOSUIModalPresentationStyle._internal(3); ///A custom view presentation style that is managed by a custom presentation controller and one or more custom animator objects. static const CUSTOM = const IOSUIModalPresentationStyle._internal(4); @@ -1185,8 +1276,7 @@ class IOSUIModalPresentationStyle { ///The default presentation style chosen by the system. /// ///**NOTE**: available on iOS 13.0+. - static const AUTOMATIC = - const IOSUIModalPresentationStyle._internal(9); + static const AUTOMATIC = const IOSUIModalPresentationStyle._internal(9); bool operator ==(value) => value == _value; @@ -1197,7 +1287,9 @@ class IOSUIModalPresentationStyle { ///Class that represents an iOS-specific class used to specify the transition style when presenting a view controller. class IOSUIModalTransitionStyle { final int _value; + const IOSUIModalTransitionStyle._internal(this._value); + static IOSUIModalTransitionStyle fromValue(int value) { if (value != null && value >= 0 && value <= 3) return IOSUIModalTransitionStyle._internal(value); @@ -1205,6 +1297,7 @@ class IOSUIModalTransitionStyle { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -1222,25 +1315,21 @@ class IOSUIModalTransitionStyle { ///When the view controller is presented, its view slides up from the bottom of the screen. ///On dismissal, the view slides back down. This is the default transition style. - static const COVER_VERTICAL = - const IOSUIModalTransitionStyle._internal(0); + static const COVER_VERTICAL = const IOSUIModalTransitionStyle._internal(0); ///When the view controller is presented, the current view initiates a horizontal 3D flip from right-to-left, ///resulting in the revealing of the new view as if it were on the back of the previous view. ///On dismissal, the flip occurs from left-to-right, returning to the original view. - static const FLIP_HORIZONTAL = - const IOSUIModalTransitionStyle._internal(1); + static const FLIP_HORIZONTAL = const IOSUIModalTransitionStyle._internal(1); ///When the view controller is presented, the current view fades out while the new view fades in at the same time. ///On dismissal, a similar type of cross-fade is used to return to the original view. - static const CROSS_DISSOLVE = - const IOSUIModalTransitionStyle._internal(2); + static const CROSS_DISSOLVE = const IOSUIModalTransitionStyle._internal(2); ///When the view controller is presented, one corner of the current view curls up to reveal the presented view underneath. ///On dismissal, the curled up page unfurls itself back on top of the presented view. ///A view controller presented using this transition is itself prevented from presenting any additional view controllers. - static const PARTIAL_CURL = - const IOSUIModalTransitionStyle._internal(3); + static const PARTIAL_CURL = const IOSUIModalTransitionStyle._internal(3); bool operator ==(value) => value == _value; @@ -1253,7 +1342,9 @@ class IOSUIModalTransitionStyle { ///**NOTE**: available on iOS 11.0+. class IOSSafariDismissButtonStyle { final int _value; + const IOSSafariDismissButtonStyle._internal(this._value); + static IOSSafariDismissButtonStyle fromValue(int value) { if (value != null && value >= 0 && value <= 2) return IOSSafariDismissButtonStyle._internal(value); @@ -1261,6 +1352,7 @@ class IOSSafariDismissButtonStyle { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -1300,18 +1392,14 @@ class InAppWebViewGroupOptions { ///iOS-specific options. IOSInAppWebViewOptions ios; - InAppWebViewGroupOptions( - {this.crossPlatform, - this.android, - this.ios}); + InAppWebViewGroupOptions({this.crossPlatform, this.android, this.ios}); Map toMap() { Map options = {}; options.addAll(this.crossPlatform?.toMap() ?? {}); if (Platform.isAndroid) options.addAll(this.android?.toMap() ?? {}); - else if (Platform.isIOS) - options.addAll(this.ios?.toMap() ?? {}); + else if (Platform.isIOS) options.addAll(this.ios?.toMap() ?? {}); return options; } @@ -1345,19 +1433,13 @@ class InAppBrowserClassOptions { Map options = {}; options.addAll(this.crossPlatform?.toMap() ?? {}); - options.addAll( - this.inAppWebViewGroupOptions?.crossPlatform?.toMap() ?? {}); + options.addAll(this.inAppWebViewGroupOptions?.crossPlatform?.toMap() ?? {}); if (Platform.isAndroid) { options.addAll(this.android?.toMap() ?? {}); - options.addAll(this - .inAppWebViewGroupOptions?.android - ?.toMap() ?? - {}); + options.addAll(this.inAppWebViewGroupOptions?.android?.toMap() ?? {}); } else if (Platform.isIOS) { options.addAll(this.ios?.toMap() ?? {}); - options.addAll( - this.inAppWebViewGroupOptions?.ios?.toMap() ?? - {}); + options.addAll(this.inAppWebViewGroupOptions?.ios?.toMap() ?? {}); } return options; @@ -1376,15 +1458,13 @@ class ChromeSafariBrowserClassOptions { ///iOS-specific options. IOSSafariOptions ios; - ChromeSafariBrowserClassOptions( - {this.android, this.ios}); + ChromeSafariBrowserClassOptions({this.android, this.ios}); Map toMap() { Map options = {}; if (Platform.isAndroid) options.addAll(this.android?.toMap() ?? {}); - else if (Platform.isIOS) - options.addAll(this.ios?.toMap() ?? {}); + else if (Platform.isIOS) options.addAll(this.ios?.toMap() ?? {}); return options; } @@ -1397,7 +1477,9 @@ class ChromeSafariBrowserClassOptions { ///Class used by [AjaxRequest] class. class AjaxRequestAction { final int _value; + const AjaxRequestAction._internal(this._value); + int toValue() => _value; ///Aborts the current [AjaxRequest]. @@ -1425,7 +1507,9 @@ class AjaxRequestAction { ///Class used by [AjaxRequestEvent] class. class AjaxRequestEventType { final String _value; + const AjaxRequestEventType._internal(this._value); + static AjaxRequestEventType fromValue(String value) { return (["loadstart", "load", "loadend", "progress", "error", "abort"] .contains(value)) @@ -1434,6 +1518,7 @@ class AjaxRequestEventType { } String toValue() => _value; + String toString() => _value; ///The LOADSTART event is fired when a request has started to load data. @@ -1488,7 +1573,9 @@ class AjaxRequestEvent { ///Class used by [AjaxRequest] class. It represents the state of an [AjaxRequest]. class AjaxRequestReadyState { final int _value; + const AjaxRequestReadyState._internal(this._value); + static AjaxRequestReadyState fromValue(int value) { if (value != null && value >= 0 && value <= 4) return AjaxRequestReadyState._internal(value); @@ -1496,6 +1583,7 @@ class AjaxRequestReadyState { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -1680,7 +1768,9 @@ class AjaxRequest { ///Class used by [FetchRequest] class. class FetchRequestAction { final int _value; + const FetchRequestAction._internal(this._value); + int toValue() => _value; ///Aborts the fetch request. @@ -1885,14 +1975,16 @@ class FetchRequest { iconURL: credentialsMap["iconURL"]); } } - return null; + return FetchRequestCredential(); } } ///Class that represents the possible resource type defined for a [ContentBlockerTrigger]. class ContentBlockerTriggerResourceType { final String _value; + const ContentBlockerTriggerResourceType._internal(this._value); + static ContentBlockerTriggerResourceType fromValue(String value) { return ([ "document", @@ -1909,6 +2001,7 @@ class ContentBlockerTriggerResourceType { } String toValue() => _value; + @override String toString() => _value; @@ -1938,7 +2031,9 @@ class ContentBlockerTriggerResourceType { ///Class that represents the possible load type for a [ContentBlockerTrigger]. class ContentBlockerTriggerLoadType { final String _value; + const ContentBlockerTriggerLoadType._internal(this._value); + static ContentBlockerTriggerLoadType fromValue(String value) { return (["first-party", "third-party"].contains(value)) ? ContentBlockerTriggerLoadType._internal(value) @@ -1946,6 +2041,7 @@ class ContentBlockerTriggerLoadType { } String toValue() => _value; + @override String toString() => _value; @@ -1966,7 +2062,9 @@ class ContentBlockerTriggerLoadType { ///Class that represents the kind of action that can be used with a [ContentBlockerTrigger]. class ContentBlockerActionType { final String _value; + const ContentBlockerActionType._internal(this._value); + static ContentBlockerActionType fromValue(String value) { return (["block", "css-display-none", "make-https"].contains(value)) ? ContentBlockerActionType._internal(value) @@ -1974,6 +2072,7 @@ class ContentBlockerActionType { } String toValue() => _value; + @override String toString() => _value; @@ -2010,7 +2109,9 @@ class Cookie { ///Class used by [PermissionRequestResponse] class. class PermissionRequestResponseAction { final int _value; + const PermissionRequestResponseAction._internal(this._value); + int toValue() => _value; ///Denies the request. @@ -2046,7 +2147,9 @@ class PermissionRequestResponse { ///It represents the policy to pass back to the decision handler. class ShouldOverrideUrlLoadingAction { final int _value; + const ShouldOverrideUrlLoadingAction._internal(this._value); + int toValue() => _value; ///Cancel the navigation. @@ -2070,8 +2173,11 @@ class ShouldOverrideUrlLoadingAction { ///Class that represents the type of action triggering a navigation on iOS for the [shouldOverrideUrlLoading] event. class IOSWKNavigationType { final int _value; + const IOSWKNavigationType._internal(this._value); + int toValue() => _value; + static IOSWKNavigationType fromValue(int value) { if (value != null && ((value >= 0 && value <= 4) || value == -1)) return IOSWKNavigationType._internal(value); @@ -2116,21 +2222,28 @@ class ShouldOverrideUrlLoadingRequest { ///Indicates whether the request was made for the main frame. On Android < 21, this is always `true`. bool isForMainFrame; - ///Gets whether the request was a result of a server-side redirect. Available only on Android. On Android < 21, this is always `false`. - bool androidHasGesture; - ///Gets whether a gesture (such as a click) was associated with the request. ///For security reasons in certain situations this method may return `false` even though ///the sequence of events which caused the request to be created was initiated by a user ///gesture. /// ///Available only on Android. On Android < 24, this is always `false`. + bool androidHasGesture; + + ///Gets whether the request was a result of a server-side redirect. Available only on Android. On Android < 21, this is always `false`. bool androidIsRedirect; ///The type of action triggering the navigation. Available only on iOS. IOSWKNavigationType iosWKNavigationType; - ShouldOverrideUrlLoadingRequest({this.url, this.method, this.headers, this.isForMainFrame, this.androidHasGesture, this.androidIsRedirect, this.iosWKNavigationType}); + ShouldOverrideUrlLoadingRequest( + {this.url, + this.method, + this.headers, + this.isForMainFrame, + this.androidHasGesture, + this.androidIsRedirect, + this.iosWKNavigationType}); } ///Class that represents the navigation request used by the [shouldOverrideUrlLoading] event. @@ -2147,7 +2260,11 @@ class OnCreateWindowRequest { ///The type of action triggering the navigation. Available only on iOS. IOSWKNavigationType iosWKNavigationType; - OnCreateWindowRequest({this.url, this.androidIsDialog, this.androidIsUserGesture, this.iosWKNavigationType}); + OnCreateWindowRequest( + {this.url, + this.androidIsDialog, + this.androidIsUserGesture, + this.iosWKNavigationType}); } ///Class that encapsulates information about the amount of storage currently used by an origin for the JavaScript storage APIs. @@ -2165,11 +2282,7 @@ class AndroidWebStorageOrigin { AndroidWebStorageOrigin({this.origin, this.quota, this.usage}); Map toMap() { - return { - "origin": origin, - "quota": quota, - "usage": usage - }; + return {"origin": origin, "quota": quota, "usage": usage}; } String toString() { @@ -2182,7 +2295,9 @@ class AndroidWebStorageOrigin { ///**NOTE**: available on iOS 9.0+. class IOSWKWebsiteDataType { final String _value; + const IOSWKWebsiteDataType._internal(this._value); + static IOSWKWebsiteDataType fromValue(String value) { return ([ "WKWebsiteDataTypeFetchCache", @@ -2195,12 +2310,13 @@ class IOSWKWebsiteDataType { "WKWebsiteDataTypeWebSQLDatabases", "WKWebsiteDataTypeIndexedDBDatabases", "WKWebsiteDataTypeServiceWorkerRegistrations" - ].contains(value)) + ].contains(value)) ? IOSWKWebsiteDataType._internal(value) : null; } String toValue() => _value; + @override String toString() => _value; @@ -2208,45 +2324,48 @@ class IOSWKWebsiteDataType { /// ///**NOTE**: available on iOS 11.3+. static const WKWebsiteDataTypeFetchCache = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeFetchCache"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeFetchCache"); ///On-disk caches. static const WKWebsiteDataTypeDiskCache = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeDiskCache"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeDiskCache"); ///In-memory caches. static const WKWebsiteDataTypeMemoryCache = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeMemoryCache"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeMemoryCache"); ///HTML offline web application caches. static const WKWebsiteDataTypeOfflineWebApplicationCache = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeOfflineWebApplicationCache"); + const IOSWKWebsiteDataType._internal( + "WKWebsiteDataTypeOfflineWebApplicationCache"); ///Cookies. static const WKWebsiteDataTypeCookies = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeCookies"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeCookies"); ///HTML session storage. static const WKWebsiteDataTypeSessionStorage = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeSessionStorage"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeSessionStorage"); ///HTML local storage. static const WKWebsiteDataTypeLocalStorage = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeLocalStorage"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeLocalStorage"); ///WebSQL databases. static const WKWebsiteDataTypeWebSQLDatabases = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeWebSQLDatabases"); + const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeWebSQLDatabases"); ///IndexedDB databases. static const WKWebsiteDataTypeIndexedDBDatabases = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeIndexedDBDatabases"); + const IOSWKWebsiteDataType._internal( + "WKWebsiteDataTypeIndexedDBDatabases"); ///Service worker registrations. /// ///**NOTE**: available on iOS 11.3+. static const WKWebsiteDataTypeServiceWorkerRegistrations = - const IOSWKWebsiteDataType._internal("WKWebsiteDataTypeServiceWorkerRegistrations"); + const IOSWKWebsiteDataType._internal( + "WKWebsiteDataTypeServiceWorkerRegistrations"); ///Returns a set of all available website data types. static final Set ALL = [ @@ -2286,10 +2405,7 @@ class IOSWKWebsiteDataRecord { dataTypesString.add(dataType.toValue()); } - return { - "displayName": displayName, - "dataTypes": dataTypesString - }; + return {"displayName": displayName, "dataTypes": dataTypesString}; } String toString() { @@ -2300,7 +2416,9 @@ class IOSWKWebsiteDataRecord { ///Class representing the [InAppWebViewHitTestResult] type. class InAppWebViewHitTestResultType { final int _value; + const InAppWebViewHitTestResultType._internal(this._value); + static InAppWebViewHitTestResultType fromValue(int value) { if (value != null && [0, 2, 3, 4, 5, 7, 8, 9].contains(value)) return InAppWebViewHitTestResultType._internal(value); @@ -2308,6 +2426,7 @@ class InAppWebViewHitTestResultType { } int toValue() => _value; + @override String toString() { switch (_value) { @@ -2333,20 +2452,30 @@ class InAppWebViewHitTestResultType { ///Default [InAppWebViewHitTestResult], where the target is unknown. static const UNKNOWN_TYPE = const InAppWebViewHitTestResultType._internal(0); + ///[InAppWebViewHitTestResult] for hitting a phone number. static const PHONE_TYPE = const InAppWebViewHitTestResultType._internal(2); + ///[InAppWebViewHitTestResult] for hitting a map address. static const GEO_TYPE = const InAppWebViewHitTestResultType._internal(3); + ///[InAppWebViewHitTestResult] for hitting an email address. static const EMAIL_TYPE = const InAppWebViewHitTestResultType._internal(4); + ///[InAppWebViewHitTestResult] for hitting an HTML::img tag. static const IMAGE_TYPE = const InAppWebViewHitTestResultType._internal(5); + ///[InAppWebViewHitTestResult] for hitting a HTML::a tag with src=http. - static const SRC_ANCHOR_TYPE = const InAppWebViewHitTestResultType._internal(7); + static const SRC_ANCHOR_TYPE = + const InAppWebViewHitTestResultType._internal(7); + ///[InAppWebViewHitTestResult] for hitting a HTML::a tag with src=http + HTML::img. - static const SRC_IMAGE_ANCHOR_TYPE = const InAppWebViewHitTestResultType._internal(8); + static const SRC_IMAGE_ANCHOR_TYPE = + const InAppWebViewHitTestResultType._internal(8); + ///[InAppWebViewHitTestResult] for hitting an edit text area. - static const EDIT_TEXT_TYPE = const InAppWebViewHitTestResultType._internal(9); + static const EDIT_TEXT_TYPE = + const InAppWebViewHitTestResultType._internal(9); bool operator ==(value) => value == _value; @@ -2358,8 +2487,313 @@ class InAppWebViewHitTestResultType { class InAppWebViewHitTestResult { ///The type of the hit test result. InAppWebViewHitTestResultType type; + ///Additional type-dependant information about the result. String extra; InAppWebViewHitTestResult({this.type, this.extra}); } + +///Class that represents the action to take used by the [WebView.androidOnRenderProcessUnresponsive] and [WebView.androidOnRenderProcessResponsive] event +///to terminate the Android [WebViewRenderProcess](https://developer.android.com/reference/android/webkit/WebViewRenderProcess). +class WebViewRenderProcessAction { + final int _value; + + const WebViewRenderProcessAction._internal(this._value); + + int toValue() => _value; + + ///Cause this renderer to terminate. + static const TERMINATE = const WebViewRenderProcessAction._internal(0); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; + + Map toMap() { + return { + "action": _value + }; + } +} + +///Class that provides more specific information about why the render process exited. +///It is used by the [WebView.androidOnRenderProcessGone] event. +class RenderProcessGoneDetail { + ///Indicates whether the render process was observed to crash, or whether it was killed by the system. + /// + ///If the render process was killed, this is most likely caused by the system being low on memory. + bool didCrash; + /// Returns the renderer priority that was set at the time that the renderer exited. This may be greater than the priority that + /// any individual [WebView] requested using []. + RendererPriority rendererPriorityAtExit; + + RenderProcessGoneDetail({this.didCrash, this.rendererPriorityAtExit}); +} + +///Class used by [RendererPriorityPolicy] class. +class RendererPriority { + final int _value; + + const RendererPriority._internal(this._value); + + static RendererPriority fromValue(int value) { + if (value != null && value >= 0 && value <= 2) + return RendererPriority._internal(value); + return null; + } + + int toValue() => _value; + + @override + String toString() { + switch (_value) { + case 0: + return "RENDERER_PRIORITY_WAIVED"; + case 1: + return "RENDERER_PRIORITY_BOUND"; + case 2: + default: + return "RENDERER_PRIORITY_IMPORTANT"; + } + } + + ///The renderer associated with this WebView is bound with Android `Context#BIND_WAIVE_PRIORITY`. + ///At this priority level WebView renderers will be strong targets for out of memory killing. + static const RENDERER_PRIORITY_WAIVED = const RendererPriority._internal(0); + + ///The renderer associated with this WebView is bound with the default priority for services. + static const RENDERER_PRIORITY_BOUND = const RendererPriority._internal(1); + + ///The renderer associated with this WebView is bound with Android `Context#BIND_IMPORTANT`. + static const RENDERER_PRIORITY_IMPORTANT = const RendererPriority._internal(2); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} + +///Class that represents the priority policy will be used to determine whether an out of process renderer should be considered to be a target for OOM killing. +///When a WebView is destroyed it will cease to be considerered when calculating the renderer priority. +///Once no WebViews remain associated with the renderer, the priority of the renderer will be reduced to [RendererPriority.RENDERER_PRIORITY_WAIVED]. +///The default policy is to set the priority to [RendererPriority.RENDERER_PRIORITY_IMPORTANT] regardless of visibility, +///and this should not be changed unless the caller also handles renderer crashes with [WebView.androidOnRenderProcessGone]. +///Any other setting will result in WebView renderers being killed by the system more aggressively than the application. +class RendererPriorityPolicy { + ///The minimum priority at which this WebView desires the renderer process to be bound. + RendererPriority rendererRequestedPriority; + ///If true, this flag specifies that when this WebView is not visible, it will be treated as if it had requested a priority of [RendererPriority.RENDERER_PRIORITY_WAIVED]. + bool waivedWhenNotVisible; + + RendererPriorityPolicy({@required this.rendererRequestedPriority, @required this.waivedWhenNotVisible}); + + Map toMap() { + return { + "rendererRequestedPriority": rendererRequestedPriority?.toValue(), + "waivedWhenNotVisible": waivedWhenNotVisible + }; + } + + static RendererPriorityPolicy fromMap(Map map) { + return map != null ? RendererPriorityPolicy( + rendererRequestedPriority: RendererPriority.fromValue(map["rendererRequestedPriority"]), + waivedWhenNotVisible: map["waivedWhenNotVisible"] + ) : RendererPriorityPolicy(); + } +} + +///Class that represents the action to take used by the [WebView.androidOnFormResubmission] event. +class FormResubmissionAction { + final int _value; + + const FormResubmissionAction._internal(this._value); + + int toValue() => _value; + + ///Resend data + static const RESEND = const FormResubmissionAction._internal(0); + + ///Don't resend data + static const DONT_RESEND = const FormResubmissionAction._internal(1); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; + + Map toMap() { + return { + "action": _value + }; + } +} + +///Class that represents an Android-specific class used to configure the WebView's over-scroll mode. +///Setting the over-scroll mode of a WebView will have an effect only if the WebView is capable of scrolling. +class AndroidOverScrollMode { + final int _value; + + const AndroidOverScrollMode._internal(this._value); + + static AndroidOverScrollMode fromValue(int value) { + if (value != null && value >= 0 && value <= 2) + return AndroidOverScrollMode._internal(value); + return null; + } + + int toValue() => _value; + + @override + String toString() { + switch (_value) { + case 1: + return "OVER_SCROLL_IF_CONTENT_SCROLLS"; + case 2: + return "OVER_SCROLL_NEVER"; + case 0: + default: + return "OVER_SCROLL_ALWAYS"; + } + } + + ///Always allow a user to over-scroll this view, provided it is a view that can scroll. + static const OVER_SCROLL_ALWAYS = + const AndroidOverScrollMode._internal(0); + + ///Allow a user to over-scroll this view only if the content is large enough to meaningfully scroll, provided it is a view that can scroll. + static const OVER_SCROLL_IF_CONTENT_SCROLLS = + const AndroidOverScrollMode._internal(1); + + ///Never allow a user to over-scroll this view. + static const OVER_SCROLL_NEVER = + const AndroidOverScrollMode._internal(2); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} + +///Class that represents an Android-specific class used to configure the style of the scrollbars. +///The scrollbars can be overlaid or inset. +///When inset, they add to the padding of the view. And the scrollbars can be drawn inside the padding area or on the edge of the view. +///For example, if a view has a background drawable and you want to draw the scrollbars inside the padding specified by the drawable, +///you can use SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to appear at the edge of the view, ignoring the padding, +///then you can use SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET. +class AndroidScrollBarStyle { + final int _value; + + const AndroidScrollBarStyle._internal(this._value); + + static AndroidScrollBarStyle fromValue(int value) { + if (value != null && [0, 16777216, 33554432, 50331648].contains(value)) + return AndroidScrollBarStyle._internal(value); + return null; + } + + int toValue() => _value; + + @override + String toString() { + switch (_value) { + case 16777216: + return "SCROLLBARS_INSIDE_INSET"; + case 33554432: + return "SCROLLBARS_OUTSIDE_OVERLAY"; + case 50331648: + return "SCROLLBARS_OUTSIDE_INSET"; + case 0: + default: + return "SCROLLBARS_INSIDE_OVERLAY"; + } + } + + ///The scrollbar style to display the scrollbars inside the content area, without increasing the padding. + ///The scrollbars will be overlaid with translucency on the view's content. + static const SCROLLBARS_INSIDE_OVERLAY = + const AndroidScrollBarStyle._internal(0); + + ///The scrollbar style to display the scrollbars inside the padded area, increasing the padding of the view. + ///The scrollbars will not overlap the content area of the view. + static const SCROLLBARS_INSIDE_INSET = + const AndroidScrollBarStyle._internal(16777216); + + ///The scrollbar style to display the scrollbars at the edge of the view, without increasing the padding. + ///The scrollbars will be overlaid with translucency. + static const SCROLLBARS_OUTSIDE_OVERLAY = + const AndroidScrollBarStyle._internal(33554432); + + ///The scrollbar style to display the scrollbars at the edge of the view, increasing the padding of the view. + ///The scrollbars will only overlap the background, if any. + static const SCROLLBARS_OUTSIDE_INSET = + const AndroidScrollBarStyle._internal(50331648); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} + +///Class that represents an Android-specific class used to configure the position of the vertical scroll bar. +class AndroidVerticalScrollbarPosition { + final int _value; + + const AndroidVerticalScrollbarPosition._internal(this._value); + + static AndroidVerticalScrollbarPosition fromValue(int value) { + if (value != null && value >= 0 && value <= 2) + return AndroidVerticalScrollbarPosition._internal(value); + return null; + } + + int toValue() => _value; + + @override + String toString() { + switch (_value) { + case 1: + return "SCROLLBAR_POSITION_LEFT"; + case 2: + return "SCROLLBAR_POSITION_RIGHT"; + case 0: + default: + return "SCROLLBAR_POSITION_DEFAULT"; + } + } + + ///Position the scroll bar at the default position as determined by the system. + static const SCROLLBAR_POSITION_DEFAULT = + const AndroidVerticalScrollbarPosition._internal(0); + + ///Position the scroll bar along the left edge. + static const SCROLLBAR_POSITION_LEFT = + const AndroidVerticalScrollbarPosition._internal(1); + + ///Position the scroll bar along the right edge. + static const SCROLLBAR_POSITION_RIGHT = + const AndroidVerticalScrollbarPosition._internal(2); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} + +///Class that represents an Android WebView package info. +class AndroidWebViewPackageInfo { + ///The version name of this package. + String versionName; + ///The name of this package. + String packageName; + + AndroidWebViewPackageInfo({this.versionName, this.packageName}); + + static AndroidWebViewPackageInfo fromMap(Map map) { + return map != null ? AndroidWebViewPackageInfo( + versionName: map["versionName"], + packageName: map["packageName"] + ) : AndroidWebViewPackageInfo(); + } +} diff --git a/lib/src/webview.dart b/lib/src/webview.dart index 09a82ccbc..6f52f5a28 100644 --- a/lib/src/webview.dart +++ b/lib/src/webview.dart @@ -9,13 +9,22 @@ abstract class WebView { final void Function(InAppWebViewController controller) onWebViewCreated; ///Event fired when the [WebView] starts to load an [url]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455621-webview final void Function(InAppWebViewController controller, String url) onLoadStart; ///Event fired when the [WebView] finishes loading an [url]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageFinished(android.webkit.WebView,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview final void Function(InAppWebViewController controller, String url) onLoadStop; ///Event fired when the [WebView] encounters an error loading an [url]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview final void Function(InAppWebViewController controller, String url, int code, String message) onLoadError; @@ -28,14 +37,21 @@ abstract class WebView { ///[description] represents the description of the HTTP error. On iOS, it is always an empty string. /// ///**NOTE**: available on Android 23+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview final void Function(InAppWebViewController controller, String url, int statusCode, String description) onLoadHttpError; ///Event fired when the current [progress] of loading a page is changed. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onProgressChanged(android.webkit.WebView,%20int) final void Function(InAppWebViewController controller, int progress) onProgressChanged; ///Event fired when the [WebView] receives a [ConsoleMessage]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onConsoleMessage(android.webkit.ConsoleMessage) final void Function( InAppWebViewController controller, ConsoleMessage consoleMessage) onConsoleMessage; @@ -51,6 +67,9 @@ abstract class WebView { ///[shouldOverrideUrlLoadingRequest] represents the navigation request. /// ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useShouldOverrideUrlLoading] option to `true`. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#shouldOverrideUrlLoading(android.webkit.WebView,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455641-webview final Future Function( InAppWebViewController controller, ShouldOverrideUrlLoadingRequest shouldOverrideUrlLoadingRequest) @@ -68,6 +87,9 @@ abstract class WebView { ///[x] represents the current horizontal scroll origin in pixels. /// ///[y] represents the current vertical scroll origin in pixels. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#onScrollChanged(int,%20int,%20int,%20int) + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll final void Function(InAppWebViewController controller, int x, int y) onScrollChanged; @@ -76,6 +98,9 @@ abstract class WebView { ///[url] represents the url of the file. /// ///**NOTE**: In order to be able to listen this event, you need to set [InAppWebViewOptions.useOnDownloadStart] option to `true`. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setDownloadListener(android.webkit.DownloadListener) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview final void Function(InAppWebViewController controller, String url) onDownloadStart; @@ -84,6 +109,8 @@ abstract class WebView { ///[scheme] represents the scheme of the url. /// ///[url] represents the url of the request. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkurlschemehandler final Future Function( InAppWebViewController controller, String scheme, String url) onLoadResourceCustomScheme; @@ -94,6 +121,9 @@ abstract class WebView { ///[onCreateWindowRequest] represents the request. /// ///**NOTE**: on Android you need to set [AndroidInAppWebViewOptions.supportMultipleWindows] option to `true`. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onCreateWindow(android.webkit.WebView,%20boolean,%20boolean,%20android.os.Message) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536907-webview final void Function(InAppWebViewController controller, OnCreateWindowRequest onCreateWindowRequest) onCreateWindow; @@ -101,6 +131,9 @@ abstract class WebView { ///If [JsAlertResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. /// ///[message] represents the message to be displayed in the alert dialog. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsAlert(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1537406-webview final Future Function( InAppWebViewController controller, String message) onJsAlert; @@ -108,6 +141,9 @@ abstract class WebView { ///If [JsConfirmResponse.handledByClient] is `true`, the webview will assume that the client will handle the dialog. /// ///[message] represents the message to be displayed in the alert dialog. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsConfirm(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20android.webkit.JsResult) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1536489-webview final Future Function( InAppWebViewController controller, String message) onJsConfirm; @@ -117,12 +153,18 @@ abstract class WebView { ///[message] represents the message to be displayed in the alert dialog. /// ///[defaultValue] represents the default value displayed in the prompt dialog. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onJsPrompt(android.webkit.WebView,%20java.lang.String,%20java.lang.String,%20java.lang.String,%20android.webkit.JsPromptResult) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wkuidelegate/1538086-webview final Future Function(InAppWebViewController controller, String message, String defaultValue) onJsPrompt; ///Event fired when the WebView received an HTTP authentication request. The default behavior is to cancel the request. /// ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [HttpAuthChallenge]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpAuthRequest(android.webkit.WebView,%20android.webkit.HttpAuthHandler,%20java.lang.String,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview final Future Function( InAppWebViewController controller, HttpAuthChallenge challenge) onReceivedHttpAuthRequest; @@ -131,6 +173,9 @@ abstract class WebView { ///The host application must return either [ServerTrustAuthResponse] instance with [ServerTrustAuthResponseAction.CANCEL] or [ServerTrustAuthResponseAction.PROCEED]. /// ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ServerTrustChallenge]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedSslError(android.webkit.WebView,%20android.webkit.SslErrorHandler,%20android.net.http.SslError) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview final Future Function( InAppWebViewController controller, ServerTrustChallenge challenge) onReceivedServerTrustAuthRequest; @@ -141,6 +186,9 @@ abstract class WebView { ///Note that, multiple layers in chromium network stack might be caching the responses. /// ///[challenge] contains data about host, port, protocol, realm, etc. as specified in the [ClientCertChallenge]. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedClientCertRequest(android.webkit.WebView,%20android.webkit.ClientCertRequest) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455638-webview final Future Function( InAppWebViewController controller, ClientCertChallenge challenge) onReceivedClientCertRequest; @@ -153,6 +201,8 @@ abstract class WebView { ///[numberOfMatches] represents how many matches have been found. /// ///[isDoneCounting] whether the find operation has actually completed. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebView#setFindListener(android.webkit.WebView.FindListener) final void Function(InAppWebViewController controller, int activeMatchOrdinal, int numberOfMatches, bool isDoneCounting) onFindResultReceived; @@ -220,6 +270,8 @@ abstract class WebView { ///[url] represents the url being visited. /// ///[androidIsReload] indicates if this url is being reloaded. Available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#doUpdateVisitedHistory(android.webkit.WebView,%20java.lang.String,%20boolean) final void Function( InAppWebViewController controller, String url, bool androidIsReload) onUpdateVisitedHistory; @@ -234,15 +286,35 @@ abstract class WebView { ///Event fired when an HTML element of the webview has been clicked and held. /// ///[hitTestResult] represents the hit result for hitting an HTML elements. + /// + ///**Official Android API**: https://developer.android.com/reference/android/view/View#setOnLongClickListener(android.view.View.OnLongClickListener) + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uilongpressgesturerecognizer final void Function(InAppWebViewController controller, InAppWebViewHitTestResult hitTestResult) onLongPressHitTestResult; ///Event fired when the current page has entered full screen mode. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onShowCustomView(android.view.View,%20android.webkit.WebChromeClient.CustomViewCallback) + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621621-didbecomevisiblenotification final void Function(InAppWebViewController controller) onEnterFullscreen; ///Event fired when the current page has exited full screen mode. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onHideCustomView() + ///**Official iOS API**: https://developer.apple.com/documentation/uikit/uiwindow/1621617-didbecomehiddennotification final void Function(InAppWebViewController controller) onExitFullscreen; + ///Called when the web view begins to receive web content. + /// + ///This event occurs early in the document loading process, and as such + ///you should expect that linked resources (for example, CSS and images) may not be available. + /// + ///[url] represents the URL corresponding to the page navigation that triggered this callback. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onPageCommitVisible(android.webkit.WebView,%20java.lang.String) + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455635-webview + final Future Function(InAppWebViewController controller, String url) onPageCommitVisible; + ///Event fired when the webview notifies that a loading URL has been flagged by Safe Browsing. ///The default behavior is to show an interstitial to the user, with the reporting checkbox visible. /// @@ -251,6 +323,8 @@ abstract class WebView { ///[threatType] represents the reason the resource was caught by Safe Browsing, corresponding to a [SafeBrowsingThreat]. /// ///**NOTE**: available only on Android 27+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onSafeBrowsingHit(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20int,%20android.webkit.SafeBrowsingResponse) final Future Function(InAppWebViewController controller, String url, SafeBrowsingThreat threatType) androidOnSafeBrowsingHit; @@ -261,6 +335,8 @@ abstract class WebView { ///[resources] represents the array of resources the web content wants to access. /// ///**NOTE**: available only on Android 23+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onPermissionRequest(android.webkit.PermissionRequest) final Future Function( InAppWebViewController controller, String origin, @@ -273,6 +349,8 @@ abstract class WebView { ///[origin] represents the origin of the web content attempting to use the Geolocation API. /// ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsShowPrompt(java.lang.String,%20android.webkit.GeolocationPermissions.Callback) final Future Function( InAppWebViewController controller, String origin) androidOnGeolocationPermissionsShowPrompt; @@ -281,23 +359,111 @@ abstract class WebView { ///Any related UI should therefore be hidden. /// ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebChromeClient#onGeolocationPermissionsHidePrompt() final Future Function(InAppWebViewController controller) androidOnGeolocationPermissionsHidePrompt; + ///Notify the host application of a resource request and allow the application to return the data. + ///If the return value is `null`, the WebView will continue to load the resource as usual. + ///Otherwise, the return response and data will be used. + /// + ///This callback is invoked for a variety of URL schemes (e.g., `http(s):`, `data:`, `file:`, etc.), + ///not only those schemes which send requests over the network. + ///This is not called for `javascript:` URLs, `blob:` URLs, or for assets accessed via `file:///android_asset/` or `file:///android_res/` URLs. + /// + ///In the case of redirects, this is only called for the initial resource URL, not any subsequent redirect URLs. + /// + ///[request] Object containing the details of the request. + /// + ///**NOTE**: available only on Android. + /// + ///**Official Android API**: + ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20android.webkit.WebResourceRequest) + ///- https://developer.android.com/reference/android/webkit/WebViewClient#shouldInterceptRequest(android.webkit.WebView,%20java.lang.String) + final Future Function(InAppWebViewController controller, WebResourceRequest request) + androidShouldInterceptRequest; + + ///Event called when the renderer currently associated with the WebView becomes unresponsive as a result of a long running blocking task such as the execution of JavaScript. + /// + ///If a WebView fails to process an input event, or successfully navigate to a new URL within a reasonable time frame, the renderer is considered to be unresponsive, and this callback will be called. + /// + ///This callback will continue to be called at regular intervals as long as the renderer remains unresponsive. + ///If the renderer becomes responsive again, [androidOnRenderProcessResponsive] will be called once, + ///and this method will not subsequently be called unless another period of unresponsiveness is detected. + /// + ///The minimum interval between successive calls to `androidOnRenderProcessUnresponsive` is 5 seconds. + /// + ///No action is taken by WebView as a result of this method call. + ///Applications may choose to terminate the associated renderer via the object that is passed to this callback, + ///if in multiprocess mode, however this must be accompanied by correctly handling [androidOnRenderProcessGone] for this WebView, + ///and all other WebViews associated with the same renderer. Failure to do so will result in application termination. + /// + ///**NOTE**: available only on Android 29+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessUnresponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) + final Future Function(InAppWebViewController controller, String url) + androidOnRenderProcessUnresponsive; + + ///Event called once when an unresponsive renderer currently associated with the WebView becomes responsive. + /// + ///After a WebView renderer becomes unresponsive, which is notified to the application by [androidOnRenderProcessUnresponsive], + ///it is possible for the blocking renderer task to complete, returning the renderer to a responsive state. + ///In that case, this method is called once to indicate responsiveness. + /// + ///No action is taken by WebView as a result of this method call. + /// + ///**NOTE**: available only on Android 29+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewRenderProcessClient#onRenderProcessResponsive(android.webkit.WebView,%20android.webkit.WebViewRenderProcess) + final Future Function(InAppWebViewController controller, String url) + androidOnRenderProcessResponsive; + + ///Event fired when the given WebView's render process has exited. + ///The application's implementation of this callback should only attempt to clean up the WebView. + ///The WebView should be removed from the view hierarchy, all references to it should be cleaned up. + /// + ///[detail] the reason why it exited. + /// + ///**NOTE**: available only on Android 26+. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onRenderProcessGone(android.webkit.WebView,%20android.webkit.RenderProcessGoneDetail) + final Future Function(InAppWebViewController controller, RenderProcessGoneDetail detail) + androidOnRenderProcessGone; + + ///As the host application if the browser should resend data as the requested page was a result of a POST. The default is to not resend the data. + /// + ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onFormResubmission(android.webkit.WebView,%20android.os.Message,%20android.os.Message) + final Future Function(InAppWebViewController controller, String url) + androidOnFormResubmission; + + ///Event fired when the scale applied to the WebView has changed. + /// + ///[oldScale] The old scale factor. + /// + ///[newScale] The new scale factor. + /// + ///**NOTE**: available only on Android. + /// + ///**Official Android API**: https://developer.android.com/reference/android/webkit/WebViewClient#onScaleChanged(android.webkit.WebView,%20float,%20float) + final Future Function(InAppWebViewController controller, double oldScale, double newScale) + androidOnScaleChanged; + ///Invoked when the web view's web content process is terminated. /// ///**NOTE**: available only on iOS. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455639-webviewwebcontentprocessdidtermi final Future Function(InAppWebViewController controller) iosOnWebContentProcessDidTerminate; - ///Called when the web view begins to receive web content. - /// - ///**NOTE**: available only on iOS. - final Future Function(InAppWebViewController controller) iosOnDidCommit; - ///Called when a web view receives a server redirect. /// ///**NOTE**: available only on iOS. + /// + ///**Official iOS API**: https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455627-webview final Future Function(InAppWebViewController controller) iosOnDidReceiveServerRedirectForProvisionalNavigation; @@ -349,12 +515,18 @@ abstract class WebView { this.onLongPressHitTestResult, this.onEnterFullscreen, this.onExitFullscreen, + this.onPageCommitVisible, this.androidOnSafeBrowsingHit, this.androidOnPermissionRequest, this.androidOnGeolocationPermissionsShowPrompt, this.androidOnGeolocationPermissionsHidePrompt, + this.androidShouldInterceptRequest, + this.androidOnRenderProcessGone, + this.androidOnRenderProcessResponsive, + this.androidOnRenderProcessUnresponsive, + this.androidOnFormResubmission, + this.androidOnScaleChanged, this.iosOnWebContentProcessDidTerminate, - this.iosOnDidCommit, this.iosOnDidReceiveServerRedirectForProvisionalNavigation, this.initialUrl, this.initialFile, diff --git a/lib/src/webview_options.dart b/lib/src/webview_options.dart index 3a7b004ad..800851b37 100755 --- a/lib/src/webview_options.dart +++ b/lib/src/webview_options.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'content_blocker.dart'; import 'types.dart'; +import 'webview.dart'; class AndroidOptions {} @@ -209,7 +210,7 @@ class InAppWebViewOptions }); } - InAppWebViewOptions options = new InAppWebViewOptions(); + InAppWebViewOptions options = InAppWebViewOptions(); options.useShouldOverrideUrlLoading = map["useShouldOverrideUrlLoading"]; options.useOnLoadResource = map["useOnLoadResource"]; options.useOnDownloadStart = map["useOnDownloadStart"]; @@ -414,49 +415,100 @@ class AndroidInAppWebViewOptions ///If the url request of a subframe matches the regular expression, then the request of that subframe is canceled. String regexToCancelSubFramesLoading; - AndroidInAppWebViewOptions( - {this.textZoom = 100, - this.clearSessionCache = false, - this.builtInZoomControls = false, - this.displayZoomControls = false, - this.supportZoom = true, - this.databaseEnabled = false, - this.domStorageEnabled = true, - this.useWideViewPort = true, - this.safeBrowsingEnabled = true, - this.mixedContentMode, - this.allowContentAccess = true, - this.allowFileAccess = true, - this.allowFileAccessFromFileURLs = false, - this.allowUniversalAccessFromFileURLs = false, - this.appCachePath, - this.blockNetworkImage = false, - this.blockNetworkLoads = false, - this.cacheMode = AndroidCacheMode.LOAD_DEFAULT, - this.cursiveFontFamily = "cursive", - this.defaultFixedFontSize = 16, - this.defaultFontSize = 16, - this.defaultTextEncodingName = "UTF-8", - this.disabledActionModeMenuItems, - this.fantasyFontFamily = "fantasy", - this.fixedFontFamily = "monospace", - this.forceDark = AndroidForceDark.FORCE_DARK_OFF, - this.geolocationEnabled = true, - this.layoutAlgorithm, - this.loadWithOverviewMode = true, - this.loadsImagesAutomatically = true, - this.minimumLogicalFontSize = 8, - this.needInitialFocus = true, - this.offscreenPreRaster = false, - this.sansSerifFontFamily = "sans-serif", - this.serifFontFamily = "sans-serif", - this.standardFontFamily = "sans-serif", - this.saveFormData = true, - this.thirdPartyCookiesEnabled = true, - this.hardwareAcceleration = true, - this.initialScale = 0, - this.supportMultipleWindows = false, - this.regexToCancelSubFramesLoading}); + ///Set to `true` to be able to listen at the [WebView.androidShouldInterceptRequest] event. The default value is `false`. + bool useShouldInterceptRequest; + + ///Set to `true` to be able to listen at the [WebView.androidOnRenderProcessGone] event. The default value is `false`. + bool useOnRenderProcessGone; + + ///Sets the WebView's over-scroll mode. + ///Setting the over-scroll mode of a WebView will have an effect only if the WebView is capable of scrolling. + ///The default value is [AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS]. + AndroidOverScrollMode overScrollMode; + + ///Informs WebView of the network state. + ///This is used to set the JavaScript property `window.navigator.isOnline` and generates the online/offline event as specified in HTML5, sec. 5.7.7. + bool networkAvailable; + + ///Specify the style of the scrollbars. The scrollbars can be overlaid or inset. + ///When inset, they add to the padding of the view. And the scrollbars can be drawn inside the padding area or on the edge of the view. + ///For example, if a view has a background drawable and you want to draw the scrollbars inside the padding specified by the drawable, + ///you can use SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to appear at the edge of the view, ignoring the padding, + ///then you can use SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET. + ///The default value is [AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY]. + AndroidScrollBarStyle scrollBarStyle; + + ///Set the position of the vertical scroll bar. + ///The default value is [AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT]. + AndroidVerticalScrollbarPosition verticalScrollbarPosition; + + ///Defines the delay in milliseconds that a scrollbar waits before fade out. + int scrollBarDefaultDelayBeforeFade; + + ///Define whether scrollbars will fade when the view is not scrolling. + ///The default value is `true`. + bool scrollbarFadingEnabled; + + ///Define the scrollbar fade duration in milliseconds. + int scrollBarFadeDuration; + + ///Set the renderer priority policy for this WebView. + RendererPriorityPolicy rendererPriorityPolicy; + + AndroidInAppWebViewOptions({ + this.textZoom = 100, + this.clearSessionCache = false, + this.builtInZoomControls = false, + this.displayZoomControls = false, + this.supportZoom = true, + this.databaseEnabled = false, + this.domStorageEnabled = true, + this.useWideViewPort = true, + this.safeBrowsingEnabled = true, + this.mixedContentMode, + this.allowContentAccess = true, + this.allowFileAccess = true, + this.allowFileAccessFromFileURLs = false, + this.allowUniversalAccessFromFileURLs = false, + this.appCachePath, + this.blockNetworkImage = false, + this.blockNetworkLoads = false, + this.cacheMode = AndroidCacheMode.LOAD_DEFAULT, + this.cursiveFontFamily = "cursive", + this.defaultFixedFontSize = 16, + this.defaultFontSize = 16, + this.defaultTextEncodingName = "UTF-8", + this.disabledActionModeMenuItems, + this.fantasyFontFamily = "fantasy", + this.fixedFontFamily = "monospace", + this.forceDark = AndroidForceDark.FORCE_DARK_OFF, + this.geolocationEnabled = true, + this.layoutAlgorithm, + this.loadWithOverviewMode = true, + this.loadsImagesAutomatically = true, + this.minimumLogicalFontSize = 8, + this.needInitialFocus = true, + this.offscreenPreRaster = false, + this.sansSerifFontFamily = "sans-serif", + this.serifFontFamily = "sans-serif", + this.standardFontFamily = "sans-serif", + this.saveFormData = true, + this.thirdPartyCookiesEnabled = true, + this.hardwareAcceleration = true, + this.initialScale = 0, + this.supportMultipleWindows = false, + this.regexToCancelSubFramesLoading, + this.useShouldInterceptRequest = false, + this.useOnRenderProcessGone = false, + this.overScrollMode = AndroidOverScrollMode.OVER_SCROLL_IF_CONTENT_SCROLLS, + this.networkAvailable, + this.scrollBarStyle = AndroidScrollBarStyle.SCROLLBARS_INSIDE_OVERLAY, + this.verticalScrollbarPosition = AndroidVerticalScrollbarPosition.SCROLLBAR_POSITION_DEFAULT, + this.scrollBarDefaultDelayBeforeFade, + this.scrollbarFadingEnabled = true, + this.scrollBarFadeDuration, + this.rendererPriorityPolicy, + }); @override Map toMap() { @@ -502,12 +554,22 @@ class AndroidInAppWebViewOptions "thirdPartyCookiesEnabled": thirdPartyCookiesEnabled, "hardwareAcceleration": hardwareAcceleration, "supportMultipleWindows": supportMultipleWindows, - "regexToCancelSubFramesLoading": regexToCancelSubFramesLoading + "regexToCancelSubFramesLoading": regexToCancelSubFramesLoading, + "useShouldInterceptRequest": useShouldInterceptRequest, + "useOnRenderProcessGone": useOnRenderProcessGone, + "overScrollMode": overScrollMode?.toValue(), + "networkAvailable": networkAvailable, + "scrollBarStyle": scrollBarStyle?.toValue(), + "verticalScrollbarPosition": verticalScrollbarPosition?.toValue(), + "scrollBarDefaultDelayBeforeFade": scrollBarDefaultDelayBeforeFade, + "scrollbarFadingEnabled": scrollbarFadingEnabled, + "scrollBarFadeDuration": scrollBarFadeDuration, + "rendererPriorityPolicy": rendererPriorityPolicy?.toMap() }; } static AndroidInAppWebViewOptions fromMap(Map map) { - AndroidInAppWebViewOptions options = new AndroidInAppWebViewOptions(); + AndroidInAppWebViewOptions options = AndroidInAppWebViewOptions(); options.textZoom = map["textZoom"]; options.clearSessionCache = map["clearSessionCache"]; options.builtInZoomControls = map["builtInZoomControls"]; @@ -557,6 +619,16 @@ class AndroidInAppWebViewOptions options.hardwareAcceleration = map["hardwareAcceleration"]; options.supportMultipleWindows = map["supportMultipleWindows"]; options.regexToCancelSubFramesLoading = map["regexToCancelSubFramesLoading"]; + options.useShouldInterceptRequest = map["useShouldInterceptRequest"]; + options.useOnRenderProcessGone = map["useOnRenderProcessGone"]; + options.overScrollMode = AndroidOverScrollMode.fromValue(map["overScrollMode"]); + options.networkAvailable = map["networkAvailable"]; + options.scrollBarStyle = AndroidScrollBarStyle.fromValue(map["scrollBarStyle"]); + options.verticalScrollbarPosition = AndroidVerticalScrollbarPosition.fromValue(map["verticalScrollbarPosition"]); + options.scrollBarDefaultDelayBeforeFade = map["scrollBarDefaultDelayBeforeFade"]; + options.scrollbarFadingEnabled = map["scrollbarFadingEnabled"]; + options.scrollBarFadeDuration = map["scrollBarFadeDuration"]; + options.rendererPriorityPolicy = RendererPriorityPolicy.fromMap(map["rendererPriorityPolicy"]?.cast()); return options; } } @@ -735,7 +807,7 @@ class IOSInAppWebViewOptions .add(IOSWKDataDetectorTypes.fromValue(dataDetectorType)); }); - IOSInAppWebViewOptions options = new IOSInAppWebViewOptions(); + IOSInAppWebViewOptions options = IOSInAppWebViewOptions(); options.disallowOverScroll = map["disallowOverScroll"]; options.enableViewportScale = map["enableViewportScale"]; options.suppressesIncrementalRendering = @@ -802,7 +874,7 @@ class InAppBrowserOptions } static InAppBrowserOptions fromMap(Map map) { - InAppBrowserOptions options = new InAppBrowserOptions(); + InAppBrowserOptions options = InAppBrowserOptions(); options.hidden = map["hidden"]; options.toolbarTop = map["toolbarTop"]; options.toolbarTopBackgroundColor = map["toolbarTopBackgroundColor"]; @@ -842,7 +914,7 @@ class AndroidInAppBrowserOptions implements BrowserOptions, AndroidOptions { } static AndroidInAppBrowserOptions fromMap(Map map) { - AndroidInAppBrowserOptions options = new AndroidInAppBrowserOptions(); + AndroidInAppBrowserOptions options = AndroidInAppBrowserOptions(); options.hideTitleBar = map["hideTitleBar"]; options.toolbarTopFixedTitle = map["toolbarTopFixedTitle"]; options.closeOnCannotGoBack = map["closeOnCannotGoBack"]; @@ -902,7 +974,7 @@ class IOSInAppBrowserOptions implements BrowserOptions, IosOptions { } static IOSInAppBrowserOptions fromMap(Map map) { - IOSInAppBrowserOptions options = new IOSInAppBrowserOptions(); + IOSInAppBrowserOptions options = IOSInAppBrowserOptions(); options.toolbarBottom = map["toolbarBottom"]; options.toolbarBottomBackgroundColor = map["toolbarBottomBackgroundColor"]; options.toolbarBottomTranslucent = map["toolbarBottomTranslucent"]; @@ -1033,7 +1105,7 @@ class IOSSafariOptions implements ChromeSafariBrowserOptions, IosOptions { } static IOSSafariOptions fromMap(Map map) { - IOSSafariOptions options = new IOSSafariOptions(); + IOSSafariOptions options = IOSSafariOptions(); options.entersReaderIfAvailable = map["entersReaderIfAvailable"]; options.barCollapsingEnabled = map["barCollapsingEnabled"]; options.dismissButtonStyle =