From b189066940ed39d49ea15008edeeef1418506b94 Mon Sep 17 00:00:00 2001 From: Lorenzo Pichilli Date: Sun, 1 May 2022 17:06:16 +0200 Subject: [PATCH] added onReceivedError and onReceivedHttpError events --- CHANGELOG.md | 2 + .../flutter_inappwebview/Util.java | 40 ++ .../InAppWebViewChromeClient.java | 5 +- .../in_app_webview/InAppWebViewClient.java | 81 ++- .../types/WebResourceErrorExt.java | 76 ++ .../types/WebResourceRequestExt.java | 139 ++++ .../types/WebResourceResponseExt.java | 158 ++++ .../integration_test/in_app_webview/main.dart | 8 +- ...load_error.dart => on_received_error.dart} | 56 +- ...error.dart => on_received_http_error.dart} | 10 +- example/lib/in_app_webiew_example.screen.dart | 4 +- ios/Classes/InAppWebView/InAppWebView.swift | 61 +- ios/Classes/Types/WebResourceError.swift | 25 + ios/Classes/Types/WebResourceRequest.swift | 39 + ios/Classes/Types/WebResourceResponse.swift | 38 + lib/src/in_app_browser/in_app_browser.dart | 27 +- .../headless_in_app_webview.dart | 21 +- lib/src/in_app_webview/in_app_webview.dart | 20 +- .../in_app_webview_controller.dart | 106 ++- lib/src/in_app_webview/webview.dart | 39 +- lib/src/types/main.dart | 2 + lib/src/types/web_resource_error.dart | 42 ++ lib/src/types/web_resource_error_type.dart | 672 ++++++++++++++++++ lib/src/types/web_resource_request.dart | 2 +- lib/src/types/web_resource_response.dart | 29 +- 25 files changed, 1545 insertions(+), 157 deletions(-) create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceErrorExt.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceRequestExt.java create mode 100644 android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java rename example/integration_test/in_app_webview/{on_load_error.dart => on_received_error.dart} (50%) rename example/integration_test/in_app_webview/{on_load_http_error.dart => on_received_http_error.dart} (77%) create mode 100644 ios/Classes/Types/WebResourceError.swift create mode 100644 ios/Classes/Types/WebResourceRequest.swift create mode 100644 ios/Classes/Types/WebResourceResponse.swift create mode 100644 lib/src/types/web_resource_error.dart create mode 100644 lib/src/types/web_resource_error_type.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b802923..595351c2 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Added `underPageBackgroundColor`, `isTextInteractionEnabled`, `isSiteSpecificQuirksModeEnabled`, `upgradeKnownHostsToHTTPS` WebView settings - Added support for `onPermissionRequest` event on iOS 15.0+ - Updated `getMetaThemeColor` on iOS 15.0+ +- Deprecated `onLoadError` for `onReceivedError`. `onReceivedError` will be called also for subframes. +- Deprecated `onLoadHttpError` for `onReceivedError`. `onReceivedHttpError` will be called also for subframes. ### BREAKING CHANGES diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java index fceaffbf..85a12331 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/Util.java @@ -17,6 +17,7 @@ import org.json.JSONArray; import org.json.JSONObject; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Inet6Address; @@ -321,4 +322,43 @@ public class Util { public static Object getOrDefault(Map map, String key, Object defaultValue) { return map.containsKey(key) ? map.get(key) : defaultValue; } + + @Nullable + public static byte[] readAllBytes(@Nullable InputStream inputStream) { + if (inputStream == null) { + return null; + } + + final int bufLen = 4 * 0x400; // 4KB + byte[] buf = new byte[bufLen]; + int readLen; + IOException exception = null; + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + byte[] data = null; + + try { + while ((readLen = inputStream.read(buf, 0, bufLen)) != -1) + outputStream.write(buf, 0, readLen); + + data = outputStream.toByteArray(); + } catch (IOException e) { + exception = e; + } finally { + try { + inputStream.close(); + } catch (IOException e) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && exception != null) { + exception.addSuppressed(e); + } + } + try { + outputStream.close(); + } catch (IOException e) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && exception != null) { + exception.addSuppressed(e); + } + } + } + return data; + } } diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java index 25ecfa32..80b03afd 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewChromeClient.java @@ -46,7 +46,6 @@ import com.pichillilorenzo.flutter_inappwebview.in_app_browser.ActivityResultLis import com.pichillilorenzo.flutter_inappwebview.in_app_browser.InAppBrowserDelegate; import com.pichillilorenzo.flutter_inappwebview.InAppWebViewFlutterPlugin; import com.pichillilorenzo.flutter_inappwebview.R; -import com.pichillilorenzo.flutter_inappwebview.types.InAppWebViewInterface; import com.pichillilorenzo.flutter_inappwebview.types.URLRequest; import java.io.ByteArrayOutputStream; @@ -104,10 +103,10 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR @Nullable public InAppWebViewFlutterPlugin plugin; @Nullable - public InAppWebViewInterface inAppWebView; + public InAppWebView inAppWebView; public InAppWebViewChromeClient(@NonNull final InAppWebViewFlutterPlugin plugin, MethodChannel channel, - @NonNull InAppWebViewInterface inAppWebView, InAppBrowserDelegate inAppBrowserDelegate) { + @NonNull InAppWebView inAppWebView, InAppBrowserDelegate inAppBrowserDelegate) { super(); this.plugin = plugin; this.channel = channel; diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java index 829817a1..13b4628e 100755 --- a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/in_app_webview/InAppWebViewClient.java @@ -2,6 +2,7 @@ package com.pichillilorenzo.flutter_inappwebview.in_app_webview; import android.annotation.TargetApi; import android.graphics.Bitmap; +import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.os.Message; @@ -37,6 +38,9 @@ import com.pichillilorenzo.flutter_inappwebview.types.ServerTrustChallenge; import com.pichillilorenzo.flutter_inappwebview.types.URLCredential; import com.pichillilorenzo.flutter_inappwebview.types.URLProtectionSpace; import com.pichillilorenzo.flutter_inappwebview.types.URLRequest; +import com.pichillilorenzo.flutter_inappwebview.types.WebResourceErrorExt; +import com.pichillilorenzo.flutter_inappwebview.types.WebResourceRequestExt; +import com.pichillilorenzo.flutter_inappwebview.types.WebResourceResponseExt; import java.io.ByteArrayInputStream; import java.net.URI; @@ -261,30 +265,27 @@ public class InAppWebViewClient extends WebViewClient { @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onReceivedError(WebView view, @NonNull WebResourceRequest request, @NonNull WebResourceError error) { -// final InAppWebView webView = (InAppWebView) view; -// -// if (request.isForMainFrame()) { -// if (webView.options.disableDefaultErrorPage) { -// webView.stopLoading(); -// webView.loadUrl("about:blank"); -// } -// -// webView.isLoading = false; -// previousAuthRequestFailureCount = 0; -// credentialsProposed = null; -// -// if (inAppBrowserDelegate != null) { -// inAppBrowserDelegate.didFailNavigation(request.getUrl().toString(), error.getErrorCode(), error.getDescription().toString()); -// } -// } -// -// Map obj = new HashMap<>(); -// obj.put("url", request.getUrl().toString()); -// obj.put("code", error.getErrorCode()); -// obj.put("message", error.getDescription()); -// channel.invokeMethod("onLoadError", obj); + final InAppWebView webView = (InAppWebView) view; - super.onReceivedError(view, request, error); + if (request.isForMainFrame()) { + if (webView.customSettings.disableDefaultErrorPage) { + webView.stopLoading(); + webView.loadUrl("about:blank"); + } + + webView.isLoading = false; + previousAuthRequestFailureCount = 0; + credentialsProposed = null; + + if (inAppBrowserDelegate != null) { + inAppBrowserDelegate.didFailNavigation(request.getUrl().toString(), error.getErrorCode(), error.getDescription().toString()); + } + } + + Map obj = new HashMap<>(); + obj.put("request", WebResourceRequestExt.fromWebResourceRequest(request).toMap()); + obj.put("error", WebResourceErrorExt.fromWebResourceError(error).toMap()); + channel.invokeMethod("onReceivedError", obj); } @Override @@ -304,26 +305,36 @@ public class InAppWebViewClient extends WebViewClient { inAppBrowserDelegate.didFailNavigation(failingUrl, errorCode, description); } + WebResourceRequestExt request = new WebResourceRequestExt( + Uri.parse(failingUrl), + null, + false, + false, + true, + "GET"); + + WebResourceErrorExt error = new WebResourceErrorExt( + errorCode, + description + ); + Map obj = new HashMap<>(); - obj.put("url", failingUrl); - obj.put("code", errorCode); - obj.put("message", description); - channel.invokeMethod("onLoadError", obj); + obj.put("request", request.toMap()); + obj.put("error",error.toMap()); + channel.invokeMethod("onReceivedError", obj); super.onReceivedError(view, errorCode, description, failingUrl); } @RequiresApi(api = Build.VERSION_CODES.M) @Override - public void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { + public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) { super.onReceivedHttpError(view, request, errorResponse); - if(request.isForMainFrame()) { - Map obj = new HashMap<>(); - obj.put("url", request.getUrl().toString()); - obj.put("statusCode", errorResponse.getStatusCode()); - obj.put("description", errorResponse.getReasonPhrase()); - channel.invokeMethod("onLoadHttpError", obj); - } + + Map obj = new HashMap<>(); + obj.put("request", WebResourceRequestExt.fromWebResourceRequest(request).toMap()); + obj.put("errorResponse", WebResourceResponseExt.fromWebResourceResponse(errorResponse).toMap()); + channel.invokeMethod("onReceivedHttpError", obj); } @Override diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceErrorExt.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceErrorExt.java new file mode 100644 index 00000000..b3986046 --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceErrorExt.java @@ -0,0 +1,76 @@ +package com.pichillilorenzo.flutter_inappwebview.types; + +import android.os.Build; +import android.webkit.WebResourceError; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import java.util.HashMap; +import java.util.Map; + +public class WebResourceErrorExt { + private int errorCode; + @NonNull + private String description; + + public WebResourceErrorExt(int errorCode, @NonNull String description) { + this.errorCode = errorCode; + this.description = description; + } + + @RequiresApi(Build.VERSION_CODES.M) + static public WebResourceErrorExt fromWebResourceError(@NonNull WebResourceError error) { + return new WebResourceErrorExt(error.getErrorCode(), error.getDescription().toString()); + } + + public Map toMap() { + Map webResourceErrorMap = new HashMap<>(); + webResourceErrorMap.put("errorCode", getErrorCode()); + webResourceErrorMap.put("description", getDescription()); + return webResourceErrorMap; + } + + public int getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + + @NonNull + public String getDescription() { + return description; + } + + public void setDescription(@NonNull String description) { + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + WebResourceErrorExt that = (WebResourceErrorExt) o; + + if (errorCode != that.errorCode) return false; + return description.equals(that.description); + } + + @Override + public int hashCode() { + int result = errorCode; + result = 31 * result + description.hashCode(); + return result; + } + + @Override + public String toString() { + return "WebResourceErrorExt{" + + "errorCode=" + errorCode + + ", description='" + description + '\'' + + '}'; + } +} diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceRequestExt.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceRequestExt.java new file mode 100644 index 00000000..720c7037 --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceRequestExt.java @@ -0,0 +1,139 @@ +package com.pichillilorenzo.flutter_inappwebview.types; + +import android.net.Uri; +import android.os.Build; +import android.webkit.WebResourceRequest; + +import androidx.annotation.NonNull; +import androidx.annotation.RequiresApi; + +import java.util.HashMap; +import java.util.Map; + +public class WebResourceRequestExt { + @NonNull + private Uri url; + private Map headers; + private boolean isRedirect; + private boolean hasGesture; + private boolean isForMainFrame; + private String method; + + public WebResourceRequestExt(@NonNull Uri url, Map headers, boolean isRedirect, boolean hasGesture, boolean isForMainFrame, String method) { + this.url = url; + this.headers = headers; + this.isRedirect = isRedirect; + this.hasGesture = hasGesture; + this.isForMainFrame = isForMainFrame; + this.method = method; + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + static public WebResourceRequestExt fromWebResourceRequest(@NonNull WebResourceRequest request) { + return new WebResourceRequestExt(request.getUrl(), + request.getRequestHeaders(), + Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && request.isRedirect(), + request.hasGesture(), + request.isForMainFrame(), + request.getMethod() + ); + } + + public Map toMap() { + Map webResourceRequestMap = new HashMap<>(); + webResourceRequestMap.put("url", url.toString()); + webResourceRequestMap.put("headers", headers); + webResourceRequestMap.put("isRedirect", isRedirect); + webResourceRequestMap.put("hasGesture", hasGesture); + webResourceRequestMap.put("isForMainFrame", isForMainFrame); + webResourceRequestMap.put("method", method); + return webResourceRequestMap; + } + + @NonNull + public Uri getUrl() { + return url; + } + + public void setUrl(@NonNull Uri url) { + this.url = url; + } + + public Map getHeaders() { + return headers; + } + + public void setHeaders(Map headers) { + this.headers = headers; + } + + public boolean isRedirect() { + return isRedirect; + } + + public void setRedirect(boolean redirect) { + isRedirect = redirect; + } + + public boolean isHasGesture() { + return hasGesture; + } + + public void setHasGesture(boolean hasGesture) { + this.hasGesture = hasGesture; + } + + public boolean isForMainFrame() { + return isForMainFrame; + } + + public void setForMainFrame(boolean forMainFrame) { + isForMainFrame = forMainFrame; + } + + public String getMethod() { + return method; + } + + public void setMethod(String method) { + this.method = method; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + WebResourceRequestExt that = (WebResourceRequestExt) o; + + if (isRedirect != that.isRedirect) return false; + if (hasGesture != that.hasGesture) return false; + if (isForMainFrame != that.isForMainFrame) return false; + if (!url.equals(that.url)) return false; + if (headers != null ? !headers.equals(that.headers) : that.headers != null) return false; + return method != null ? method.equals(that.method) : that.method == null; + } + + @Override + public int hashCode() { + int result = url.hashCode(); + result = 31 * result + (headers != null ? headers.hashCode() : 0); + result = 31 * result + (isRedirect ? 1 : 0); + result = 31 * result + (hasGesture ? 1 : 0); + result = 31 * result + (isForMainFrame ? 1 : 0); + result = 31 * result + (method != null ? method.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "WebResourceRequestExt{" + + "url=" + url + + ", headers=" + headers + + ", isRedirect=" + isRedirect + + ", hasGesture=" + hasGesture + + ", isForMainFrame=" + isForMainFrame + + ", method='" + method + '\'' + + '}'; + } +} diff --git a/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java new file mode 100644 index 00000000..17a99cba --- /dev/null +++ b/android/src/main/java/com/pichillilorenzo/flutter_inappwebview/types/WebResourceResponseExt.java @@ -0,0 +1,158 @@ +package com.pichillilorenzo.flutter_inappwebview.types; + +import android.os.Build; +import android.webkit.WebResourceResponse; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import com.pichillilorenzo.flutter_inappwebview.Util; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class WebResourceResponseExt { + private String contentType; + private String contentEncoding; + @Nullable + private Integer statusCode; + @Nullable + private String reasonPhrase; + @Nullable + private Map headers; + @Nullable + private byte[] data; + + public WebResourceResponseExt(String contentType, String contentEncoding, @Nullable Integer statusCode, @Nullable String reasonPhrase, @Nullable Map headers, @Nullable byte[] data) { + this.contentType = contentType; + this.contentEncoding = contentEncoding; + this.statusCode = statusCode; + this.reasonPhrase = reasonPhrase; + this.headers = headers; + this.data = data; + } + + static public WebResourceResponseExt fromWebResourceResponse(@NonNull WebResourceResponse response) { + Integer statusCode = null; + String reasonPhrase = null; + Map headers = null; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + statusCode = response.getStatusCode(); + reasonPhrase = response.getReasonPhrase(); + headers = response.getResponseHeaders(); + } + return new WebResourceResponseExt(response.getMimeType(), + response.getEncoding(), + statusCode, + reasonPhrase, + headers, + Util.readAllBytes(response.getData()) + ); + } + + public Map toMap() { + Map webResourceResponseMap = new HashMap<>(); + webResourceResponseMap.put("contentType", contentType); + webResourceResponseMap.put("contentEncoding", contentEncoding); + webResourceResponseMap.put("statusCode", statusCode); + webResourceResponseMap.put("reasonPhrase", reasonPhrase); + webResourceResponseMap.put("headers", headers); + webResourceResponseMap.put("data", data); + return webResourceResponseMap; + } + + public String getContentType() { + return contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getContentEncoding() { + return contentEncoding; + } + + public void setContentEncoding(String contentEncoding) { + this.contentEncoding = contentEncoding; + } + + @Nullable + public Integer getStatusCode() { + return statusCode; + } + + public void setStatusCode(@Nullable Integer statusCode) { + this.statusCode = statusCode; + } + + @Nullable + public String getReasonPhrase() { + return reasonPhrase; + } + + public void setReasonPhrase(@Nullable String reasonPhrase) { + this.reasonPhrase = reasonPhrase; + } + + @Nullable + public Map getHeaders() { + return headers; + } + + public void setHeaders(@Nullable Map headers) { + this.headers = headers; + } + + @Nullable + public byte[] getData() { + return data; + } + + public void setData(@Nullable byte[] data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + WebResourceResponseExt that = (WebResourceResponseExt) o; + + if (contentType != null ? !contentType.equals(that.contentType) : that.contentType != null) + return false; + if (contentEncoding != null ? !contentEncoding.equals(that.contentEncoding) : that.contentEncoding != null) + return false; + if (statusCode != null ? !statusCode.equals(that.statusCode) : that.statusCode != null) + return false; + if (reasonPhrase != null ? !reasonPhrase.equals(that.reasonPhrase) : that.reasonPhrase != null) + return false; + if (headers != null ? !headers.equals(that.headers) : that.headers != null) return false; + return Arrays.equals(data, that.data); + } + + @Override + public int hashCode() { + int result = contentType != null ? contentType.hashCode() : 0; + result = 31 * result + (contentEncoding != null ? contentEncoding.hashCode() : 0); + result = 31 * result + (statusCode != null ? statusCode.hashCode() : 0); + result = 31 * result + (reasonPhrase != null ? reasonPhrase.hashCode() : 0); + result = 31 * result + (headers != null ? headers.hashCode() : 0); + result = 31 * result + Arrays.hashCode(data); + return result; + } + + @Override + public String toString() { + return "WebResourceResponseExt{" + + "contentType='" + contentType + '\'' + + ", contentEncoding='" + contentEncoding + '\'' + + ", statusCode=" + statusCode + + ", reasonPhrase='" + reasonPhrase + '\'' + + ", headers=" + headers + + ", data=" + Arrays.toString(data) + + '}'; + } +} diff --git a/example/integration_test/in_app_webview/main.dart b/example/integration_test/in_app_webview/main.dart index 14b4735c..0ef7c7a6 100644 --- a/example/integration_test/in_app_webview/main.dart +++ b/example/integration_test/in_app_webview/main.dart @@ -39,8 +39,8 @@ import 'on_console_message.dart'; import 'on_download_start_request.dart'; import 'on_find_result_received.dart'; import 'on_js_before_unload.dart'; -import 'on_load_error.dart'; -import 'on_load_http_error.dart'; +import 'on_received_error.dart'; +import 'on_received_http_error.dart'; import 'on_load_resource.dart'; import 'on_load_resource_custom_scheme.dart'; import 'on_navigation_response.dart'; @@ -99,7 +99,7 @@ void main() { getTitle(); programmaticScroll(); shouldOverrideUrlLoading(); - onLoadError(); + onReceivedError(); webViewWindows(); interceptAjaxRequest(); interceptFetchRequest(); @@ -109,7 +109,7 @@ void main() { onFindResultReceived(); onDownloadStartRequest(); javascriptDialogs(); - onLoadHttpError(); + onReceivedHttpError(); onLoadResourceCustomScheme(); onLoadResource(); onUpdateVisitedHistory(); diff --git a/example/integration_test/in_app_webview/on_load_error.dart b/example/integration_test/in_app_webview/on_received_error.dart similarity index 50% rename from example/integration_test/in_app_webview/on_load_error.dart rename to example/integration_test/in_app_webview/on_received_error.dart index 0667adaf..3d405b07 100644 --- a/example/integration_test/in_app_webview/on_load_error.dart +++ b/example/integration_test/in_app_webview/on_received_error.dart @@ -7,7 +7,7 @@ import 'package:flutter_test/flutter_test.dart'; import '../constants.dart'; -void onLoadError() { +void onReceivedError() { final shouldSkip = kIsWeb ? true : ![ @@ -16,10 +16,10 @@ void onLoadError() { TargetPlatform.macOS, ].contains(defaultTargetPlatform); - group('onLoadError', () { + group('onReceivedError', () { testWidgets('invalid url', (WidgetTester tester) async { final Completer errorUrlCompleter = Completer(); - final Completer errorCodeCompleter = Completer(); + final Completer errorCodeCompleter = Completer(); await tester.pumpWidget( Directionality( @@ -27,31 +27,45 @@ void onLoadError() { child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: TEST_NOT_A_WEBSITE_URL), - onLoadError: (controller, url, code, message) { - errorUrlCompleter.complete(url.toString()); - errorCodeCompleter.complete(code); + onReceivedError: (controller, request, error) { + errorUrlCompleter.complete(request.url.toString()); + errorCodeCompleter.complete(error.type); }, ), ), ); final String url = await errorUrlCompleter.future; - final int code = await errorCodeCompleter.future; + final WebResourceErrorType errorType = await errorCodeCompleter.future; - if (defaultTargetPlatform == TargetPlatform.android) { - expect(code, -2); - } else if (defaultTargetPlatform == TargetPlatform.iOS || - defaultTargetPlatform == TargetPlatform.macOS) { - expect(code, -1003); - } + expect(errorType, WebResourceErrorType.HOST_LOOKUP); expect(url, TEST_NOT_A_WEBSITE_URL.toString()); }); + testWidgets('file not found', (WidgetTester tester) async { + final Completer errorCodeCompleter = Completer(); + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: InAppWebView( + key: GlobalKey(), + initialUrlRequest: URLRequest(url: Uri.parse('file:flutter.dev')), + onReceivedError: (controller, request, error) { + errorCodeCompleter.complete(error.type); + }, + ), + ), + ); + + final WebResourceErrorType errorType = await errorCodeCompleter.future; + + expect(errorType, WebResourceErrorType.FILE_NOT_FOUND); + }); + testWidgets('event is not called with valid url', (WidgetTester tester) async { - final Completer errorUrlCompleter = Completer(); - final Completer errorCodeCompleter = Completer(); - final Completer errorMessageCompleter = Completer(); + final Completer onReceivedErrorCompleter = Completer(); await tester.pumpWidget( Directionality( @@ -61,18 +75,14 @@ void onLoadError() { initialUrlRequest: URLRequest( url: Uri.parse( 'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+')), - onLoadError: (controller, url, code, message) { - errorUrlCompleter.complete(url.toString()); - errorCodeCompleter.complete(code); - errorMessageCompleter.complete(message); + onReceivedError: (controller, request, error) { + onReceivedErrorCompleter.complete(); }, ), ), ); - expect(errorUrlCompleter.future, doesNotComplete); - expect(errorCodeCompleter.future, doesNotComplete); - expect(errorMessageCompleter.future, doesNotComplete); + expect(onReceivedErrorCompleter.future, doesNotComplete); }); }, skip: shouldSkip); } diff --git a/example/integration_test/in_app_webview/on_load_http_error.dart b/example/integration_test/in_app_webview/on_received_http_error.dart similarity index 77% rename from example/integration_test/in_app_webview/on_load_http_error.dart rename to example/integration_test/in_app_webview/on_received_http_error.dart index e6795d96..5e6396a4 100644 --- a/example/integration_test/in_app_webview/on_load_http_error.dart +++ b/example/integration_test/in_app_webview/on_received_http_error.dart @@ -7,7 +7,7 @@ import 'package:flutter_test/flutter_test.dart'; import '../constants.dart'; -void onLoadHttpError() { +void onReceivedHttpError() { final shouldSkip = kIsWeb ? true : ![ @@ -16,7 +16,7 @@ void onLoadHttpError() { TargetPlatform.macOS, ].contains(defaultTargetPlatform); - testWidgets('onLoadHttpError', (WidgetTester tester) async { + testWidgets('onReceivedHttpError', (WidgetTester tester) async { final Completer errorUrlCompleter = Completer(); final Completer statusCodeCompleter = Completer(); @@ -26,9 +26,9 @@ void onLoadHttpError() { child: InAppWebView( key: GlobalKey(), initialUrlRequest: URLRequest(url: TEST_URL_404), - onLoadHttpError: (controller, url, statusCode, description) async { - errorUrlCompleter.complete(url.toString()); - statusCodeCompleter.complete(statusCode); + onReceivedHttpError: (controller, request, errorResponse) async { + errorUrlCompleter.complete(request.url.toString()); + statusCodeCompleter.complete(errorResponse.statusCode); }, ), ), diff --git a/example/lib/in_app_webiew_example.screen.dart b/example/lib/in_app_webiew_example.screen.dart index b2e567d7..84f407c8 100755 --- a/example/lib/in_app_webiew_example.screen.dart +++ b/example/lib/in_app_webiew_example.screen.dart @@ -114,7 +114,7 @@ class _InAppWebViewExampleScreenState extends State { InAppWebView( key: webViewKey, initialUrlRequest: - URLRequest(url: Uri.parse("https://www.youtube.com/watch?v=CylXr3AF3uU")), + URLRequest(url: Uri.parse('https://flutter.dev')), // initialUrlRequest: // URLRequest(url: Uri.parse(Uri.base.toString().replaceFirst("/#/", "/") + 'page.html')), // initialFile: "assets/index.html", @@ -168,7 +168,7 @@ class _InAppWebViewExampleScreenState extends State { urlController.text = this.url; }); }, - onLoadError: (controller, url, code, message) { + onReceivedError: (controller, request, error) { pullToRefreshController?.endRefreshing(); }, onProgressChanged: (controller, progress) { diff --git a/ios/Classes/InAppWebView/InAppWebView.swift b/ios/Classes/InAppWebView/InAppWebView.swift index 1fbcabe7..136d21e7 100755 --- a/ios/Classes/InAppWebView/InAppWebView.swift +++ b/ios/Classes/InAppWebView/InAppWebView.swift @@ -1684,10 +1684,17 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) { - if navigationResponse.isForMainFrame, let response = navigationResponse.response as? HTTPURLResponse { - if response.statusCode >= 400 { - onLoadHttpError(url: response.url?.absoluteString, statusCode: response.statusCode, description: "") - } + if let response = navigationResponse.response as? HTTPURLResponse, response.statusCode >= 400 { + let request = WebResourceRequest(url: response.url ?? URL(string: "about:blank")!, + headers: response.allHeaderFields, + isForMainFrame: navigationResponse.isForMainFrame) + let errorResponse = WebResourceResponse(contentType: response.mimeType ?? "", + contentEncoding: response.textEncodingName ?? "", + data: nil, + headers: response.allHeaderFields, + statusCode: response.statusCode, + reasonPhrase: nil) + onReceivedHttpError(request: request, errorResponse: errorResponse) } let useOnNavigationResponse = settings?.useOnNavigationResponse @@ -1793,17 +1800,31 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { InAppWebView.credentialsProposed = [] - var urlError = url?.absoluteString - if let info = error._userInfo as? [String: Any] { - if let failingUrl = info[NSURLErrorFailingURLErrorKey] as? URL { - urlError = failingUrl.absoluteString + var urlError: URL = url ?? URL(string: "about:blank")! + var errorCode = error._code + var errorDescription = error.localizedDescription + + if let info = error as? URLError { + if let failingURL = info.failingURL { + urlError = failingURL } - if let failingUrlString = info[NSURLErrorFailingURLStringErrorKey] as? String { - urlError = failingUrlString + errorCode = info.code.rawValue + errorDescription = info.localizedDescription + } + else if let info = error._userInfo as? [String: Any] { + if let failingUrl = info[NSURLErrorFailingURLErrorKey] as? URL { + urlError = failingUrl + } + if let failingUrlString = info[NSURLErrorFailingURLStringErrorKey] as? String, + let failingUrl = URL(string: failingUrlString) { + urlError = failingUrl } } - onLoadError(url: urlError, error: error) + let webResourceRequest = WebResourceRequest(url: urlError, headers: nil) + let webResourceError = WebResourceError(errorCode: errorCode, errorDescription: errorDescription) + + onReceivedError(request: webResourceRequest, error: webResourceError) inAppBrowserDelegate?.didFailNavigation(url: url, error: error) } @@ -2514,14 +2535,20 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, channel?.invokeMethod("onLoadStop", arguments: arguments) } - public func onLoadError(url: String?, error: Error) { - let arguments: [String: Any?] = ["url": url, "code": error._code, "message": error.localizedDescription] - channel?.invokeMethod("onLoadError", arguments: arguments) + public func onReceivedError(request: WebResourceRequest, error: WebResourceError) { + let arguments: [String: Any?] = [ + "request": request.toMap(), + "error": error.toMap() + ] + channel?.invokeMethod("onReceivedError", arguments: arguments) } - public func onLoadHttpError(url: String?, statusCode: Int, description: String) { - let arguments: [String: Any?] = ["url": url, "statusCode": statusCode, "description": description] - channel?.invokeMethod("onLoadHttpError", arguments: arguments) + public func onReceivedHttpError(request: WebResourceRequest, errorResponse: WebResourceResponse) { + let arguments: [String: Any?] = [ + "request": request.toMap(), + "errorResponse": errorResponse.toMap() + ] + channel?.invokeMethod("onReceivedHttpError", arguments: arguments) } public func onProgressChanged(progress: Int) { diff --git a/ios/Classes/Types/WebResourceError.swift b/ios/Classes/Types/WebResourceError.swift new file mode 100644 index 00000000..8f00e9c8 --- /dev/null +++ b/ios/Classes/Types/WebResourceError.swift @@ -0,0 +1,25 @@ +// +// WebResourceError.swift +// flutter_inappwebview +// +// Created by Lorenzo Pichilli on 01/05/22. +// + +import Foundation + +public class WebResourceError: NSObject { + var errorCode: Int + var errorDescription: String + + public init(errorCode: Int, errorDescription: String) { + self.errorCode = errorCode + self.errorDescription = errorDescription + } + + public func toMap () -> [String:Any?] { + return [ + "errorCode": errorCode, + "description": errorDescription + ] + } +} diff --git a/ios/Classes/Types/WebResourceRequest.swift b/ios/Classes/Types/WebResourceRequest.swift new file mode 100644 index 00000000..7bdb3df2 --- /dev/null +++ b/ios/Classes/Types/WebResourceRequest.swift @@ -0,0 +1,39 @@ +// +// WebResourceRequest.swift +// flutter_inappwebview +// +// Created by Lorenzo Pichilli on 01/05/22. +// + +import Foundation + +public class WebResourceRequest: NSObject { + var url: URL + var headers: [AnyHashable:Any]? + var isRedirect = false + var hasGesture = false + var isForMainFrame = true + var method = "GET" + + public init(url: URL, headers: [AnyHashable:Any]?) { + self.url = url + self.headers = headers + } + + public init(url: URL, headers: [AnyHashable:Any]?, isForMainFrame: Bool) { + self.url = url + self.headers = headers + self.isForMainFrame = isForMainFrame + } + + public func toMap () -> [String:Any?] { + return [ + "url": url.absoluteString, + "headers": headers, + "isRedirect": isRedirect, + "hasGesture": hasGesture, + "isForMainFrame": isForMainFrame, + "method": method + ] + } +} diff --git a/ios/Classes/Types/WebResourceResponse.swift b/ios/Classes/Types/WebResourceResponse.swift new file mode 100644 index 00000000..b7f1f55f --- /dev/null +++ b/ios/Classes/Types/WebResourceResponse.swift @@ -0,0 +1,38 @@ +// +// WebResourceResponse.swift +// flutter_inappwebview +// +// Created by Lorenzo Pichilli on 01/05/22. +// + +import Foundation + +public class WebResourceResponse: NSObject { + var contentType: String + var contentEncoding: String + var data: Data? + var headers: [AnyHashable:Any]? + var statusCode: Int? + var reasonPhrase: String? + + public init(contentType: String, contentEncoding: String, data: Data?, + headers: [AnyHashable:Any]?, statusCode: Int?, reasonPhrase: String?) { + self.contentType = contentType + self.contentEncoding = contentEncoding + self.data = data + self.headers = headers + self.statusCode = statusCode + self.reasonPhrase = reasonPhrase + } + + public func toMap () -> [String:Any?] { + return [ + "contentType": contentType, + "contentEncoding": contentEncoding, + "data": data, + "headers": headers, + "statusCode": statusCode, + "reasonPhrase": reasonPhrase + ] + } +} diff --git a/lib/src/in_app_browser/in_app_browser.dart b/lib/src/in_app_browser/in_app_browser.dart index 01d2d052..b7f1fd88 100755 --- a/lib/src/in_app_browser/in_app_browser.dart +++ b/lib/src/in_app_browser/in_app_browser.dart @@ -372,27 +372,34 @@ class InAppBrowser { ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455629-webview)) void onLoadStop(Uri? url) {} - ///Event fired when the [InAppBrowser] encounters an error loading an [url]. - /// - ///**Supported Platforms/Implementations**: - ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String))) - ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + ///Use [onReceivedError] instead. + @Deprecated("Use onReceivedError instead") void onLoadError(Uri? url, int code, String message) {} - ///Event fired when the [InAppBrowser] main page receives an HTTP error. + ///Event fired when the [InAppBrowser] encounters an [error] loading a [request]. /// - ///[url] represents the url of the main page that received the HTTP error. + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + void onReceivedError(WebResourceRequest request, WebResourceError error) {} + + ///Use [onReceivedHttpError] instead. + @Deprecated("Use onReceivedHttpError instead") + void onLoadHttpError(Uri? url, int statusCode, String description) {} + + ///Event fired when the [InAppBrowser] receives an HTTP error. /// - ///[statusCode] represents the status code of the response. HTTP errors have status codes >= 400. + ///[request] represents the originating request. /// - ///[description] represents the description of the HTTP error. On iOS, it is always an empty string. + ///[errorResponse] represents the information about the error occurred. /// ///**NOTE**: available on Android 23+. /// ///**Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) - void onLoadHttpError(Uri? url, int statusCode, String description) {} + void onReceivedHttpError( + WebResourceRequest request, WebResourceResponse errorResponse) {} ///Event fired when the current [progress] (range 0-100) of loading a page is changed. /// diff --git a/lib/src/in_app_webview/headless_in_app_webview.dart b/lib/src/in_app_webview/headless_in_app_webview.dart index a6354488..64648abe 100644 --- a/lib/src/in_app_webview/headless_in_app_webview.dart +++ b/lib/src/in_app_webview/headless_in_app_webview.dart @@ -63,8 +63,10 @@ class HeadlessInAppWebView implements WebView { this.onWebViewCreated, this.onLoadStart, this.onLoadStop, - this.onLoadError, - this.onLoadHttpError, + @Deprecated("Use onReceivedError instead") this.onLoadError, + this.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") this.onLoadHttpError, + this.onReceivedHttpError, this.onProgressChanged, this.onConsoleMessage, this.shouldOverrideUrlLoading, @@ -425,14 +427,27 @@ class HeadlessInAppWebView implements WebView { InAppWebViewController controller, JsPromptRequest jsPromptRequest)? onJsPrompt; + ///Use [onReceivedError] instead. + @Deprecated("Use onReceivedError instead") @override - void Function(InAppWebViewController controller, Uri? url, int code, + final void Function(InAppWebViewController controller, Uri? url, int code, String message)? onLoadError; + @override + final void Function(InAppWebViewController controller, + WebResourceRequest request, WebResourceError error)? onReceivedError; + + ///Use [onReceivedHttpError] instead. + @Deprecated("Use onReceivedHttpError instead") @override void Function(InAppWebViewController controller, Uri? url, int statusCode, String description)? onLoadHttpError; + final void Function( + InAppWebViewController controller, + WebResourceRequest request, + WebResourceResponse errorResponse)? onReceivedHttpError; + @override void Function(InAppWebViewController controller, LoadedResource resource)? onLoadResource; diff --git a/lib/src/in_app_webview/in_app_webview.dart b/lib/src/in_app_webview/in_app_webview.dart index 43fedccc..1c11c7de 100755 --- a/lib/src/in_app_webview/in_app_webview.dart +++ b/lib/src/in_app_webview/in_app_webview.dart @@ -54,8 +54,10 @@ class InAppWebView extends StatefulWidget implements WebView { this.onWebViewCreated, this.onLoadStart, this.onLoadStop, - this.onLoadError, - this.onLoadHttpError, + @Deprecated("Use onReceivedError instead") this.onLoadError, + this.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") this.onLoadHttpError, + this.onReceivedHttpError, this.onConsoleMessage, this.onProgressChanged, this.shouldOverrideUrlLoading, @@ -305,14 +307,28 @@ class InAppWebView extends StatefulWidget implements WebView { InAppWebViewController controller, JsPromptRequest jsPromptRequest)? onJsPrompt; + ///Use [onReceivedError] instead. + @Deprecated("Use onReceivedError instead") @override final void Function(InAppWebViewController controller, Uri? url, int code, String message)? onLoadError; + @override + final void Function(InAppWebViewController controller, + WebResourceRequest request, WebResourceError error)? onReceivedError; + + ///Use [onReceivedHttpError] instead. + @Deprecated("Use onReceivedHttpError instead") @override final void Function(InAppWebViewController controller, Uri? url, int statusCode, String description)? onLoadHttpError; + @override + final void Function( + InAppWebViewController controller, + WebResourceRequest request, + WebResourceResponse errorResponse)? onReceivedHttpError; + @override final void Function( InAppWebViewController controller, LoadedResource resource)? diff --git a/lib/src/in_app_webview/in_app_webview_controller.dart b/lib/src/in_app_webview/in_app_webview_controller.dart index f5f83272..82f3fa47 100644 --- a/lib/src/in_app_webview/in_app_webview_controller.dart +++ b/lib/src/in_app_webview/in_app_webview_controller.dart @@ -106,12 +106,14 @@ class InAppWebViewController { _debugLog(String method, dynamic args) { if (WebView.debugLogging) { String viewId = (getViewId() ?? _inAppBrowser?.id).toString(); - String message = - (_inAppBrowser == null ? "WebView" : "InAppBrowser") - + " ID " + viewId + " calling \"" + - method.toString() + "\" using " + args.toString(); - developer.log(message, - name: this.runtimeType.toString()); + String message = (_inAppBrowser == null ? "WebView" : "InAppBrowser") + + " ID " + + viewId + + " calling \"" + + method.toString() + + "\" using " + + args.toString(); + developer.log(message, name: this.runtimeType.toString()); } } @@ -144,30 +146,69 @@ class InAppWebViewController { _inAppBrowser!.onLoadStop(uri); } break; - case "onLoadError": - if ((_webview != null && _webview!.onLoadError != null) || + case "onReceivedError": + if ((_webview != null && + (_webview!.onReceivedError != null || + // ignore: deprecated_member_use_from_same_package + _webview!.onLoadError != null)) || _inAppBrowser != null) { - String? url = call.arguments["url"]; - int code = call.arguments["code"]; - String message = call.arguments["message"]; - Uri? uri = url != null ? Uri.parse(url) : null; - if (_webview != null && _webview!.onLoadError != null) - _webview!.onLoadError!(this, uri, code, message); - else - _inAppBrowser!.onLoadError(uri, code, message); + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceError error = WebResourceError.fromMap( + call.arguments["error"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (_webview != null) { + if (_webview!.onReceivedError != null) + _webview!.onReceivedError!(this, request, error); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + _webview!.onLoadError!(this, request.url, error.type.toIntValue(), + error.description); + } + } else { + if (isForMainFrame) { + _inAppBrowser! + // ignore: deprecated_member_use_from_same_package + .onLoadError( + request.url, error.type.toIntValue(), error.description); + } + _inAppBrowser!.onReceivedError(request, error); + } } break; - case "onLoadHttpError": - if ((_webview != null && _webview!.onLoadHttpError != null) || + case "onReceivedHttpError": + if ((_webview != null && + (_webview!.onReceivedHttpError != null || + // ignore: deprecated_member_use_from_same_package + _webview!.onLoadHttpError != null)) || _inAppBrowser != null) { - String? url = call.arguments["url"]; - int statusCode = call.arguments["statusCode"]; - String description = call.arguments["description"]; - Uri? uri = url != null ? Uri.parse(url) : null; - if (_webview != null && _webview!.onLoadHttpError != null) - _webview!.onLoadHttpError!(this, uri, statusCode, description); - else - _inAppBrowser!.onLoadHttpError(uri, statusCode, description); + WebResourceRequest request = WebResourceRequest.fromMap( + call.arguments["request"].cast())!; + WebResourceResponse errorResponse = WebResourceResponse.fromMap( + call.arguments["errorResponse"].cast())!; + var isForMainFrame = request.isForMainFrame ?? false; + + if (_webview != null) { + if (_webview!.onReceivedHttpError != null) + _webview!.onReceivedHttpError!(this, request, errorResponse); + else if (isForMainFrame) { + // ignore: deprecated_member_use_from_same_package + _webview!.onLoadHttpError!( + this, + request.url, + errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + } else { + if (isForMainFrame) { + _inAppBrowser! + // ignore: deprecated_member_use_from_same_package + .onLoadHttpError(request.url, errorResponse.statusCode ?? -1, + errorResponse.reasonPhrase ?? ''); + } + _inAppBrowser!.onReceivedHttpError(request, errorResponse); + } } break; case "onProgressChanged": @@ -2200,7 +2241,8 @@ class InAppWebViewController { var height = await _channel.invokeMethod('getContentHeight', args); if (height == null || height == 0) { // try to use javascript - var scrollHeight = await evaluateJavascript(source: "document.documentElement.scrollHeight;"); + var scrollHeight = await evaluateJavascript( + source: "document.documentElement.scrollHeight;"); if (scrollHeight != null && scrollHeight is num) { height = scrollHeight.toInt(); } @@ -3111,12 +3153,12 @@ class InAppWebViewController { ///- iOS ([Official API - WKWebView.createPdf](https://developer.apple.com/documentation/webkit/wkwebview/3650490-createpdf)) Future createPdf( {@Deprecated("Use pdfConfiguration instead") - // ignore: deprecated_member_use_from_same_package - IOSWKPDFConfiguration? iosWKPdfConfiguration, - PDFConfiguration? pdfConfiguration}) async { + // ignore: deprecated_member_use_from_same_package + IOSWKPDFConfiguration? iosWKPdfConfiguration, + PDFConfiguration? pdfConfiguration}) async { Map args = {}; args.putIfAbsent('pdfConfiguration', - () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap()); + () => pdfConfiguration?.toMap() ?? iosWKPdfConfiguration?.toMap()); return await _channel.invokeMethod('createPdf', args); } @@ -3332,7 +3374,7 @@ class InAppWebViewController { ///- Android native WebView ///- iOS static Future get tRexRunnerHtml async => await rootBundle.loadString( - 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); + 'packages/flutter_inappwebview/assets/t_rex_runner/t-rex.html'); ///Gets the css of the Chromium's t-rex runner game. Used in combination with [tRexRunnerHtml]. /// diff --git a/lib/src/in_app_webview/webview.dart b/lib/src/in_app_webview/webview.dart index 32bafebb..bd01bedc 100644 --- a/lib/src/in_app_webview/webview.dart +++ b/lib/src/in_app_webview/webview.dart @@ -56,29 +56,39 @@ abstract class WebView { ///- Web ([Official API - Window.onload](https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event)) final void Function(InAppWebViewController controller, Uri? url)? onLoadStop; - ///Event fired when the [WebView] encounters an error loading an [url]. - /// - ///**Supported Platforms/Implementations**: - ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20int,%20java.lang.String,%20java.lang.String))) - ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + ///Use [onReceivedError] instead. + @Deprecated("Use onReceivedError instead") final void Function(InAppWebViewController controller, Uri? url, int code, String message)? onLoadError; - ///Event fired when the [WebView] main page receives an HTTP error. + ///Event fired when the [WebView] encounters an [error] loading a [request]. /// - ///[url] represents the url of the main page that received the HTTP error. + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.onReceivedError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceError))) + ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview)) + final void Function(InAppWebViewController controller, + WebResourceRequest request, WebResourceError error)? onReceivedError; + + ///Use [onReceivedHttpError] instead. + @Deprecated("Use onReceivedHttpError instead") + final void Function(InAppWebViewController controller, Uri? url, + int statusCode, String description)? onLoadHttpError; + + ///Event fired when the [WebView] receives an HTTP error. /// - ///[statusCode] represents the status code of the response. HTTP errors have status codes >= 400. + ///[request] represents the originating request. /// - ///[description] represents the description of the HTTP error. On iOS, it is always an empty string. + ///[errorResponse] represents the information about the error occurred. /// ///**NOTE**: available on Android 23+. /// ///**Supported Platforms/Implementations**: ///- Android native WebView ([Official API - WebViewClient.onReceivedHttpError](https://developer.android.com/reference/android/webkit/WebViewClient#onReceivedHttpError(android.webkit.WebView,%20android.webkit.WebResourceRequest,%20android.webkit.WebResourceResponse))) ///- iOS ([Official API - WKNavigationDelegate.webView](https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455643-webview)) - final void Function(InAppWebViewController controller, Uri? url, - int statusCode, String description)? onLoadHttpError; + final void Function( + InAppWebViewController controller, + WebResourceRequest request, + WebResourceResponse errorResponse)? onReceivedHttpError; ///Event fired when the current [progress] of loading a page is changed. /// @@ -914,8 +924,11 @@ abstract class WebView { this.onWebViewCreated, this.onLoadStart, this.onLoadStop, - this.onLoadError, - this.onLoadHttpError, + @Deprecated('Use onReceivedError instead') + this.onLoadError, + this.onReceivedError, + @Deprecated("Use onReceivedHttpError instead") this.onLoadHttpError, + this.onReceivedHttpError, this.onProgressChanged, this.onConsoleMessage, this.shouldOverrideUrlLoading, diff --git a/lib/src/types/main.dart b/lib/src/types/main.dart index 5aa7f99e..59074a9e 100644 --- a/lib/src/types/main.dart +++ b/lib/src/types/main.dart @@ -142,3 +142,5 @@ export 'webview_implementation.dart'; export 'webview_package_info.dart'; export 'webview_render_process_action.dart'; export 'window_features.dart'; +export 'web_resource_error.dart'; +export 'web_resource_error_type.dart'; \ No newline at end of file diff --git a/lib/src/types/web_resource_error.dart b/lib/src/types/web_resource_error.dart new file mode 100644 index 00000000..21349104 --- /dev/null +++ b/lib/src/types/web_resource_error.dart @@ -0,0 +1,42 @@ +import 'web_resource_error_type.dart'; + +///Encapsulates information about errors occurred during loading of web resources. +class WebResourceError { + ///The type of the error. + WebResourceErrorType type; + + ///The string describing the error. + String description; + + WebResourceError({required this.type, required this.description}); + + ///Gets a possible [WebResourceError] instance from a [Map] value. + static WebResourceError? fromMap(Map? map) { + if (map == null) { + return null; + } + + return WebResourceError( + type: WebResourceErrorType.fromIntValue(map["errorCode"])!, + description: map["description"] + ); + } + + ///Converts instance to a map. + Map toMap() { + return { + "type": type, + "description": description + }; + } + + ///Converts instance to a map. + Map toJson() { + return this.toMap(); + } + + @override + String toString() { + return toMap().toString(); + } +} \ No newline at end of file diff --git a/lib/src/types/web_resource_error_type.dart b/lib/src/types/web_resource_error_type.dart new file mode 100644 index 00000000..d544563d --- /dev/null +++ b/lib/src/types/web_resource_error_type.dart @@ -0,0 +1,672 @@ +import 'package:flutter/foundation.dart'; + +///Class that represents the error types returned by URL loading APIs. +class WebResourceErrorType { + final String _value; + final int _intValue; + + const WebResourceErrorType._internal(this._value, this._intValue); + + ///Set of all values of [WebResourceErrorType]. + static final Set values = [ + WebResourceErrorType.USER_AUTHENTICATION_FAILED, + WebResourceErrorType.BAD_URL, + ].toSet(); + + ///Gets a possible [WebResourceErrorType] instance from a [String] value. + static WebResourceErrorType? fromValue(String? value) { + if (value != null) { + try { + return WebResourceErrorType.values + .firstWhere((element) => element.toValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets a possible [WebResourceErrorType] instance from an [int] value. + static WebResourceErrorType? fromIntValue(int? value) { + if (value != null) { + try { + return WebResourceErrorType.values + .firstWhere((element) => element.toIntValue() == value); + } catch (e) { + return null; + } + } + return null; + } + + ///Gets [String] value. + String toValue() => _value; + + ///Gets [int] value. + int toIntValue() => _intValue; + + @override + String toString() => _value; + + ///User authentication failed on server. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_AUTHENTICATION](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_AUTHENTICATION)) + static final USER_AUTHENTICATION_FAILED = WebResourceErrorType._internal( + "USER_AUTHENTICATION_FAILED", + (defaultTargetPlatform != TargetPlatform.android) + ? -4 + : UNKNOWN._intValue); + + ///A malformed URL prevented a URL request from being initiated. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_BAD_URL](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_BAD_URL)) + ///- iOS ([Official API - URLError.badURL](https://developer.apple.com/documentation/foundation/urlerror/2293516-badurl)) + static final BAD_URL = WebResourceErrorType._internal( + "BAD_URL", + (defaultTargetPlatform != TargetPlatform.android) + ? -12 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1000 + : UNKNOWN._intValue)); + + ///Failed to connect to the server. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_CONNECT](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_CONNECT)) + ///- iOS ([Official API - URLError.cannotConnectToHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883001-cannotconnecttohost)) + static final CANNOT_CONNECT_TO_HOST = WebResourceErrorType._internal( + "CANNOT_CONNECT_TO_HOST", + (defaultTargetPlatform != TargetPlatform.android) + ? -6 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1004 + : UNKNOWN._intValue)); + + ///Failed to perform SSL handshake. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_FAILED_SSL_HANDSHAKE](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_FAILED_SSL_HANDSHAKE)) + static final FAILED_SSL_HANDSHAKE = WebResourceErrorType._internal( + "FAILED_SSL_HANDSHAKE", + (defaultTargetPlatform != TargetPlatform.android) + ? -11 + : UNKNOWN._intValue); + + ///Generic file error. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_FILE](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_FILE)) + static final GENERIC_FILE_ERROR = WebResourceErrorType._internal( + "GENERIC_FILE_ERROR", + (defaultTargetPlatform != TargetPlatform.android) + ? -13 + : UNKNOWN._intValue); + + ///File not found. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_FILE_NOT_FOUND](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_FILE_NOT_FOUND)) + ///- iOS ([Official API - URLError.fileDoesNotExist](https://developer.apple.com/documentation/foundation/urlerror/code/2883074-filedoesnotexist)) + static final FILE_NOT_FOUND = WebResourceErrorType._internal( + "FILE_NOT_FOUND", + (defaultTargetPlatform != TargetPlatform.android) + ? -14 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1100 + : UNKNOWN._intValue)); + + ///Server or proxy hostname lookup failed. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_HOST_LOOKUP](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_HOST_LOOKUP)) + ///- iOS ([Official API - URLError.cannotFindHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883157-cannotfindhost)) + static final HOST_LOOKUP = WebResourceErrorType._internal( + "HOST_LOOKUP", + (defaultTargetPlatform != TargetPlatform.android) + ? -2 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1003 + : UNKNOWN._intValue)); + + ///Failed to read or write to the server. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_IO](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_IO)) + static final IO = WebResourceErrorType._internal( + "IO", + (defaultTargetPlatform != TargetPlatform.android) + ? -7 + : UNKNOWN._intValue); + + ///User authentication failed on proxy. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_PROXY_AUTHENTICATION](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_PROXY_AUTHENTICATION)) + static final PROXY_AUTHENTICATION = WebResourceErrorType._internal( + "PROXY_AUTHENTICATION", + (defaultTargetPlatform != TargetPlatform.android) + ? -5 + : UNKNOWN._intValue); + + ///A redirect loop has been detected or the threshold for number of allowable redirects has been exceeded (currently `16` on iOS). + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_REDIRECT_LOOP](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_REDIRECT_LOOP)) + ///- iOS ([Official API - URLError.cannotFindHost](https://developer.apple.com/documentation/foundation/urlerror/code/2883157-cannotfindhost)) + static final TOO_MANY_REDIRECTS = WebResourceErrorType._internal( + "TOO_MANY_REDIRECTS", + (defaultTargetPlatform != TargetPlatform.android) + ? -9 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1007 + : UNKNOWN._intValue)); + + ///Connection timed out. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_TIMEOUT](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_TIMEOUT)) + ///- iOS ([Official API - URLError.timedOut](https://developer.apple.com/documentation/foundation/urlerror/code/2883027-timedout)) + static final TIMEOUT = WebResourceErrorType._internal( + "TIMEOUT", + (defaultTargetPlatform != TargetPlatform.android) + ? -8 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1001 + : UNKNOWN._intValue)); + + ///Too many requests during this load. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_TOO_MANY_REQUESTS](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_TOO_MANY_REQUESTS)) + static final TOO_MANY_REQUESTS = WebResourceErrorType._internal( + "TOO_MANY_REQUESTS", + (defaultTargetPlatform != TargetPlatform.android) + ? -15 + : UNKNOWN._intValue); + + ///The URL Loading System encountered an error that it can’t interpret. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_UNKNOWN](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNKNOWN)) + ///- iOS ([Official API - URLError.unknown](https://developer.apple.com/documentation/foundation/urlerror/2293357-unknown)) + static final UNKNOWN = WebResourceErrorType._internal("UNKNOWN", -1); + + ///Resource load was canceled by Safe Browsing. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_UNSAFE_RESOURCE](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNSAFE_RESOURCE)) + static final UNSAFE_RESOURCE = WebResourceErrorType._internal( + "UNSAFE_RESOURCE", + (defaultTargetPlatform != TargetPlatform.android) + ? -16 + : UNKNOWN._intValue); + + ///Unsupported authentication scheme (not basic or digest). + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_UNSUPPORTED_AUTH_SCHEME](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNSUPPORTED_AUTH_SCHEME)) + static final UNSUPPORTED_AUTH_SCHEME = WebResourceErrorType._internal( + "UNSUPPORTED_AUTH_SCHEME", + (defaultTargetPlatform != TargetPlatform.android) + ? -3 + : UNKNOWN._intValue); + + ///Unsupported URI scheme. + ///Typically this occurs when there is no available protocol handler for the URL. + /// + ///**Supported Platforms/Implementations**: + ///- Android native WebView ([Official API - WebViewClient.ERROR_UNSUPPORTED_SCHEME](https://developer.android.com/reference/android/webkit/WebViewClient#ERROR_UNSUPPORTED_SCHEME)) + ///- iOS ([Official API - URLError.unsupportedURL](https://developer.apple.com/documentation/foundation/urlerror/code/2883043-unsupportedurl)) + static final UNSUPPORTED_SCHEME = WebResourceErrorType._internal( + "UNSUPPORTED_SCHEME", + (defaultTargetPlatform != TargetPlatform.android) + ? -10 + : ((defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1002 + : UNKNOWN._intValue)); + + ///An asynchronous load has been canceled. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cancelled](https://developer.apple.com/documentation/foundation/urlerror/code/2883178-cancelled)) + static final CANCELLED = WebResourceErrorType._internal( + "CANCELLED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -999 + : UNKNOWN._intValue); + + ///A client or server connection was severed in the middle of an in-progress load. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.networkConnectionLost](https://developer.apple.com/documentation/foundation/urlerror/2293759-networkconnectionlost)) + static final NETWORK_CONNECTION_LOST = + WebResourceErrorType._internal("NETWORK_CONNECTION_LOST", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1005 + : UNKNOWN._intValue); + + ///A requested resource couldn't be retrieved. + ///This error can indicate a file-not-found situation, or decoding problems that prevent data from being processed correctly. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.resourceUnavailable](https://developer.apple.com/documentation/foundation/urlerror/2293555-resourceunavailable)) + static final RESOURCE_UNAVAILABLE = + WebResourceErrorType._internal("RESOURCE_UNAVAILABLE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1008 + : UNKNOWN._intValue); + + ///A network resource was requested, but an internet connection hasn’t been established and can’t be established automatically. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.notConnectedToInternet](https://developer.apple.com/documentation/foundation/urlerror/2293104-notconnectedtointernet)) + static final NOT_CONNECTED_TO_INTERNET = + WebResourceErrorType._internal("NOT_CONNECTED_TO_INTERNET", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1009 + : UNKNOWN._intValue); + + ///A redirect was specified by way of server response code, but the server didn’t accompany this code with a redirect URL. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.redirectToNonExistentLocation](https://developer.apple.com/documentation/foundation/urlerror/2293066-redirecttononexistentlocation)) + static final REDIRECT_TO_NON_EXISTENT_LOCATION = + WebResourceErrorType._internal("REDIRECT_TO_NON_EXISTENT_LOCATION", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1010 + : UNKNOWN._intValue); + + ///The URL Loading System received bad data from the server. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.badServerResponse](https://developer.apple.com/documentation/foundation/urlerror/2293606-badserverresponse)) + static final BAD_SERVER_RESPONSE = + WebResourceErrorType._internal("BAD_SERVER_RESPONSE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1011 + : UNKNOWN._intValue); + + ///An asynchronous request for authentication has been canceled by the user. + ///This error typically occurs when a user clicks a "Cancel" button in a username/password dialog, rather than attempting to authenticate. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.userCancelledAuthentication](https://developer.apple.com/documentation/foundation/urlerror/2293330-usercancelledauthentication)) + static final USER_CANCELLED_AUTHENTICATION = + WebResourceErrorType._internal("USER_CANCELLED_AUTHENTICATION", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1012 + : UNKNOWN._intValue); + + ///Authentication is required to access a resource. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.userAuthenticationRequired](https://developer.apple.com/documentation/foundation/urlerror/2293560-userauthenticationrequired)) + static final USER_AUTHENTICATION_REQUIRED = + WebResourceErrorType._internal("USER_AUTHENTICATION_REQUIRED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1013 + : UNKNOWN._intValue); + + ///A server reported that a URL has a non-zero content length, but terminated the network connection gracefully without sending any data. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.zeroByteResource](https://developer.apple.com/documentation/foundation/urlerror/2293773-zerobyteresource)) + static final ZERO_BYTE_RESOURCE = + WebResourceErrorType._internal("ZERO_BYTE_RESOURCE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1014 + : UNKNOWN._intValue); + + ///Content data received during a connection request couldn’t be decoded for a known content encoding. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotDecodeRawData](https://developer.apple.com/documentation/foundation/urlerror/2293573-cannotdecoderawdata)) + static final CANNOT_DECODE_RAW_DATA = + WebResourceErrorType._internal("CANNOT_DECODE_RAW_DATA", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1015 + : UNKNOWN._intValue); + + ///Content data received during a connection request couldn’t be decoded for a known content encoding. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotDecodeContentData](https://developer.apple.com/documentation/foundation/urlerror/2292983-cannotdecodecontentdata)) + static final CANNOT_DECODE_CONTENT_DATA = + WebResourceErrorType._internal("CANNOT_DECODE_CONTENT_DATA", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1016 + : UNKNOWN._intValue); + + ///A task could not parse a response. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotParseResponse](https://developer.apple.com/documentation/foundation/urlerror/code/2882919-cannotparseresponse)) + static final CANNOT_PARSE_RESPONSE = + WebResourceErrorType._internal("CANNOT_PARSE_RESPONSE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1017 + : UNKNOWN._intValue); + + ///App Transport Security disallowed a connection because there is no secure network connection. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.appTransportSecurityRequiresSecureConnection](https://developer.apple.com/documentation/foundation/urlerror/code/2882980-apptransportsecurityrequiressecu)) + static final APP_TRANSPORT_SECURITY_REQUIRES_SECURE_CONNECTION = + WebResourceErrorType._internal("APP_TRANSPORT_SECURITY_REQUIRES_SECURE_CONNECTION", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1022 + : UNKNOWN._intValue); + + ///A request for an FTP file resulted in the server responding that the file is not a plain file, but a directory. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.fileIsDirectory](https://developer.apple.com/documentation/foundation/urlerror/code/2883220-fileisdirectory)) + static final FILE_IS_DIRECTORY = + WebResourceErrorType._internal("FILE_IS_DIRECTORY", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1101 + : UNKNOWN._intValue); + + ///A resource couldn’t be read because of insufficient permissions. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.noPermissionsToReadFile](https://developer.apple.com/documentation/foundation/urlerror/code/2882941-nopermissionstoreadfile)) + static final NO_PERMISSIONS_TO_READ_FILE = + WebResourceErrorType._internal("NO_PERMISSIONS_TO_READ_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1102 + : UNKNOWN._intValue); + + ///The length of the resource data exceeds the maximum allowed. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.dataLengthExceedsMaximum](https://developer.apple.com/documentation/foundation/urlerror/code/2882930-datalengthexceedsmaximum)) + static final DATA_LENGTH_EXCEEDS_MAXIMUM = + WebResourceErrorType._internal("DATA_LENGTH_EXCEEDS_MAXIMUM", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1103 + : UNKNOWN._intValue); + + ///An attempt to establish a secure connection failed for reasons that can’t be expressed more specifically. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.secureConnectionFailed](https://developer.apple.com/documentation/foundation/urlerror/code/2883122-secureconnectionfailed)) + static final SECURE_CONNECTION_FAILED = + WebResourceErrorType._internal("SECURE_CONNECTION_FAILED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1200 + : UNKNOWN._intValue); + + ///A server certificate had a date which indicates it has expired, or is not yet valid. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.serverCertificateHasBadDate](https://developer.apple.com/documentation/foundation/urlerror/code/2883088-servercertificatehasbaddate)) + static final SERVER_CERTIFICATE_HAS_BAD_DATE = + WebResourceErrorType._internal("SERVER_CERTIFICATE_HAS_BAD_DATE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1201 + : UNKNOWN._intValue); + + ///A server certificate was signed by a root server that isn’t trusted. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.serverCertificateUntrusted](https://developer.apple.com/documentation/foundation/urlerror/code/2882976-servercertificateuntrusted)) + static final SERVER_CERTIFICATE_UNTRUSTED = + WebResourceErrorType._internal("SERVER_CERTIFICATE_UNTRUSTED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1202 + : UNKNOWN._intValue); + + ///A server certificate was not signed by any root server. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.serverCertificateHasUnknownRoot](https://developer.apple.com/documentation/foundation/urlerror/code/2883085-servercertificatehasunknownroot)) + static final SERVER_CERTIFICATE_HAS_UNKNOWN_ROOT = + WebResourceErrorType._internal("SERVER_CERTIFICATE_HAS_UNKNOWN_ROOT", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1203 + : UNKNOWN._intValue); + + ///A server certificate is not yet valid. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.serverCertificateNotYetValid](https://developer.apple.com/documentation/foundation/urlerror/code/2882991-servercertificatenotyetvalid)) + static final SERVER_CERTIFICATE_NOT_YET_VALID = + WebResourceErrorType._internal("SERVER_CERTIFICATE_NOT_YET_VALID", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1204 + : UNKNOWN._intValue); + + ///A server certificate was rejected. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.clientCertificateRejected](https://developer.apple.com/documentation/foundation/urlerror/code/2883091-clientcertificaterejected)) + static final CLIENT_CERTIFICATE_REJECTED = + WebResourceErrorType._internal("CLIENT_CERTIFICATE_REJECTED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1205 + : UNKNOWN._intValue); + + ///A client certificate was required to authenticate an SSL connection during a request. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.clientCertificateRequired](https://developer.apple.com/documentation/foundation/urlerror/code/2883199-clientcertificaterequired)) + static final CLIENT_CERTIFICATE_REQUIRED = + WebResourceErrorType._internal("CLIENT_CERTIFICATE_REQUIRED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1206 + : UNKNOWN._intValue); + + ///A request to load an item only from the cache could not be satisfied. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotLoadFromNetwork](https://developer.apple.com/documentation/foundation/urlerror/code/2882968-cannotloadfromnetwork)) + static final CANNOT_LOAD_FROM_NETWORK = + WebResourceErrorType._internal("CANNOT_LOAD_FROM_NETWORK", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -2000 + : UNKNOWN._intValue); + + ///A download task couldn’t create the downloaded file on disk because of an I/O failure. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotCreateFile](https://developer.apple.com/documentation/foundation/urlerror/code/2883204-cannotcreatefile)) + static final CANNOT_CREATE_FILE = + WebResourceErrorType._internal("CANNOT_CREATE_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3000 + : UNKNOWN._intValue); + + ///A download task was unable to open the downloaded file on disk. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotOpenFile](https://developer.apple.com/documentation/foundation/urlerror/code/2883034-cannotopenfile)) + static final CANNOT_OPEN_FILE = + WebResourceErrorType._internal("CANNOT_OPEN_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3001 + : UNKNOWN._intValue); + + ///A download task couldn’t close the downloaded file on disk. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotCloseFile](https://developer.apple.com/documentation/foundation/urlerror/code/2883215-cannotclosefile)) + static final CANNOT_CLOSE_FILE = + WebResourceErrorType._internal("CANNOT_CLOSE_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3002 + : UNKNOWN._intValue); + + ///A download task was unable to write to the downloaded file on disk. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotWriteToFile](https://developer.apple.com/documentation/foundation/urlerror/code/2883098-cannotwritetofile)) + static final CANNOT_WRITE_TO_FILE = + WebResourceErrorType._internal("CANNOT_WRITE_TO_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3003 + : UNKNOWN._intValue); + + ///A download task was unable to remove a downloaded file from disk. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotRemoveFile](https://developer.apple.com/documentation/foundation/urlerror/code/2883202-cannotremovefile)) + static final CANNOT_REMOVE_FILE = + WebResourceErrorType._internal("CANNOT_REMOVE_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3004 + : UNKNOWN._intValue); + + ///A download task was unable to move a downloaded file on disk. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.cannotMoveFile](https://developer.apple.com/documentation/foundation/urlerror/code/2883180-cannotmovefile)) + static final CANNOT_MOVE_FILE = + WebResourceErrorType._internal("CANNOT_MOVE_FILE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3005 + : UNKNOWN._intValue); + + ///A download task failed to decode an encoded file during the download. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.downloadDecodingFailedMidStream](https://developer.apple.com/documentation/foundation/urlerror/code/2883224-downloaddecodingfailedmidstream)) + static final DOWNLOAD_DECODING_FAILED_MID_STREAM = + WebResourceErrorType._internal("DOWNLOAD_DECODING_FAILED_MID_STREAM", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3006 + : UNKNOWN._intValue); + + ///A download task failed to decode an encoded file after downloading. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.downloadDecodingFailedToComplete](https://developer.apple.com/documentation/foundation/urlerror/code/2882936-downloaddecodingfailedtocomplete)) + static final DOWNLOAD_DECODING_FAILED_TO_COMPLETE = + WebResourceErrorType._internal("DOWNLOAD_DECODING_FAILED_TO_COMPLETE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -3007 + : UNKNOWN._intValue); + + ///The attempted connection required activating a data context while roaming, but international roaming is disabled. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.internationalRoamingOff](https://developer.apple.com/documentation/foundation/urlerror/code/2883134-internationalroamingoff)) + static final INTERNATIONAL_ROAMING_OFF = + WebResourceErrorType._internal("INTERNATIONAL_ROAMING_OFF", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1018 + : UNKNOWN._intValue); + + ///A connection was attempted while a phone call is active on a network that does not support simultaneous phone and data communication (EDGE or GPRS). + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.callIsActive](https://developer.apple.com/documentation/foundation/urlerror/code/2883170-callisactive)) + static final CALL_IS_ACTIVE = + WebResourceErrorType._internal("CALL_IS_ACTIVE", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1019 + : UNKNOWN._intValue); + + ///The cellular network disallowed a connection. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.dataNotAllowed](https://developer.apple.com/documentation/foundation/urlerror/code/2883217-datanotallowed)) + static final DATA_NOT_ALLOWED = + WebResourceErrorType._internal("DATA_NOT_ALLOWED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1020 + : UNKNOWN._intValue); + + ///A body stream is needed but the client did not provide one. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.requestBodyStreamExhausted](https://developer.apple.com/documentation/foundation/urlerror/code/2883176-requestbodystreamexhausted)) + static final REQUEST_BODY_STREAM_EXHAUSTED = + WebResourceErrorType._internal("REQUEST_BODY_STREAM_EXHAUSTED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -1021 + : UNKNOWN._intValue); + + ///The shared container identifier of the URL session configuration is needed but has not been set. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.backgroundSessionRequiresSharedContainer](https://developer.apple.com/documentation/foundation/urlerror/code/2883169-backgroundsessionrequiressharedc)) + static final BACKGROUND_SESSION_REQUIRES_SHARED_CONTAINER = + WebResourceErrorType._internal("BACKGROUND_SESSION_REQUIRES_SHARED_CONTAINER", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -995 + : UNKNOWN._intValue); + + ///An app or app extension attempted to connect to a background session that is already connected to a process. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.backgroundSessionInUseByAnotherProcess](https://developer.apple.com/documentation/foundation/urlerror/code/2882923-backgroundsessioninusebyanotherp)) + static final BACKGROUND_SESSION_IN_USE_BY_ANOTHER_PROCESS = + WebResourceErrorType._internal("BACKGROUND_SESSION_IN_USE_BY_ANOTHER_PROCESS", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -996 + : UNKNOWN._intValue); + + ///The app is suspended or exits while a background data task is processing. + /// + ///**Supported Platforms/Implementations**: + ///- iOS ([Official API - URLError.backgroundSessionWasDisconnected](https://developer.apple.com/documentation/foundation/urlerror/code/2883075-backgroundsessionwasdisconnected)) + static final BACKGROUND_SESSION_WAS_DISCONNECTED = + WebResourceErrorType._internal("BACKGROUND_SESSION_WAS_DISCONNECTED", + (defaultTargetPlatform != TargetPlatform.iOS || + defaultTargetPlatform != TargetPlatform.macOS) + ? -997 + : UNKNOWN._intValue); + + bool operator ==(value) => value == _value; + + @override + int get hashCode => _value.hashCode; +} diff --git a/lib/src/types/web_resource_request.dart b/lib/src/types/web_resource_request.dart index 573347c3..6c74ec26 100644 --- a/lib/src/types/web_resource_request.dart +++ b/lib/src/types/web_resource_request.dart @@ -1,6 +1,6 @@ import '../in_app_webview/webview.dart'; -///Class representing a resource request of the WebView used by the event [WebView.shouldInterceptRequest]. +///Class representing a resource request of the [WebView]. class WebResourceRequest { ///The URL for which the resource request was made. Uri url; diff --git a/lib/src/types/web_resource_response.dart b/lib/src/types/web_resource_response.dart index 9d07b218..777b7d69 100644 --- a/lib/src/types/web_resource_response.dart +++ b/lib/src/types/web_resource_response.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import '../in_app_webview/webview.dart'; -///Class representing a resource response of the WebView used by the event [WebView.shouldInterceptRequest]. +///Class representing a resource response of the [WebView]. class WebResourceResponse { ///The resource response's MIME type, for example `text/html`. String contentType; @@ -32,11 +32,26 @@ class WebResourceResponse { WebResourceResponse( {this.contentType = "", - this.contentEncoding = "utf-8", - this.data, - this.headers, - this.statusCode, - this.reasonPhrase}); + this.contentEncoding = "utf-8", + this.data, + this.headers, + this.statusCode, + this.reasonPhrase}); + + ///Gets a possible [WebResourceResponse] instance from a [Map] value. + static WebResourceResponse? fromMap(Map? map) { + if (map == null) { + return null; + } + + return WebResourceResponse( + contentType: map["contentType"], + contentEncoding: map["contentEncoding"], + data: map["data"], + headers: map["headers"]?.cast(), + statusCode: map["statusCode"], + reasonPhrase: map["reasonPhrase"]); + } ///Converts instance to a map. Map toMap() { @@ -59,4 +74,4 @@ class WebResourceResponse { String toString() { return toMap().toString(); } -} \ No newline at end of file +}