Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@
package io.flutter.plugins.webviewflutter;

import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.util.Log;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.webkit.WebResourceErrorCompat;
import androidx.webkit.WebViewClientCompat;
import io.flutter.plugin.common.MethodChannel;
import java.util.HashMap;
Expand Down Expand Up @@ -85,6 +89,20 @@ private void notifyOnNavigationRequest(
}
}

private void onReceiveError(WebView view, int code, String description, String url) {
Map<String, Object> args = new HashMap<>();
args.put("url", url);
args.put("code", code);
args.put("description", description);
methodChannel.invokeMethod("onPageReceiveError", args);
}

private void onPageStarted(WebView view, String url) {
Map<String, Object> args = new HashMap<>();
args.put("url", url);
methodChannel.invokeMethod("onPageStarted", args);
}

// This method attempts to avoid using WebViewClientCompat due to bug
// https://bugs.chromium.org/p/chromium/issues/detail?id=925887. Also, see
// https://github.com/flutter/flutter/issues/29446.
Expand All @@ -110,6 +128,37 @@ public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request
public void onPageFinished(WebView view, String url) {
FlutterWebViewClient.this.onPageFinished(view, url);
}

@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceivedError(
WebView view, WebResourceRequest request, WebResourceError error) {
FlutterWebViewClient.this.onReceiveError(
view,
error.getErrorCode(),
error.getDescription().toString(),
request.getUrl().toString());
}

@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceivedHttpError(
WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
FlutterWebViewClient.this.onReceiveError(
view, errorResponse.getStatusCode(), null, request.getUrl().toString());
}

@SuppressWarnings("deprecation")
@Override
public void onReceivedError(
WebView view, int errorCode, String description, String failingUrl) {
FlutterWebViewClient.this.onReceiveError(view, errorCode, description, failingUrl);
}

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
FlutterWebViewClient.this.onPageStarted(view, url);
}
};
}

Expand All @@ -130,6 +179,42 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
public void onPageFinished(WebView view, String url) {
FlutterWebViewClient.this.onPageFinished(view, url);
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceivedHttpError(
@NonNull WebView view,
@NonNull WebResourceRequest request,
@NonNull WebResourceResponse errorResponse) {
FlutterWebViewClient.this.onReceiveError(
view, errorResponse.getStatusCode(), null, request.getUrl().toString());
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceivedError(
@NonNull WebView view,
@NonNull WebResourceRequest request,
@NonNull WebResourceErrorCompat error) {
//TODO: is really need to check WebViewFeature.isFeatureSupported() and api version.
FlutterWebViewClient.this.onReceiveError(
view,
error.getErrorCode(),
error.getDescription().toString(),
request.getUrl().toString());
}

@SuppressWarnings("deprecation")
@Override
public void onReceivedError(
WebView view, int errorCode, String description, String failingUrl) {
FlutterWebViewClient.this.onReceiveError(view, errorCode, description, failingUrl);
}

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
FlutterWebViewClient.this.onPageStarted(view, url);
}
};
}

Expand Down
65 changes: 58 additions & 7 deletions packages/webview_flutter/ios/Classes/FLTWKNavigationDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@
#import "FLTWKNavigationDelegate.h"

@implementation FLTWKNavigationDelegate {
FlutterMethodChannel* _methodChannel;
FlutterMethodChannel *_methodChannel;
}

- (instancetype)initWithChannel:(FlutterMethodChannel*)channel {
- (instancetype)initWithChannel:(FlutterMethodChannel *)channel {
self = [super init];
if (self) {
_methodChannel = channel;
}
return self;
}

- (void)webView:(WKWebView*)webView
decidePolicyForNavigationAction:(WKNavigationAction*)navigationAction
- (void)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (!self.hasDartNavigationDelegate) {
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
NSDictionary* arguments = @{
NSDictionary *arguments = @{
@"url" : navigationAction.request.URL.absoluteString,
@"isForMainFrame" : @(navigationAction.targetFrame.isMainFrame)
};
Expand All @@ -50,13 +50,64 @@ - (void)webView:(WKWebView*)webView
decisionHandler(WKNavigationActionPolicyAllow);
return;
}
NSNumber* typedResult = result;
NSNumber *typedResult = result;
decisionHandler([typedResult boolValue] ? WKNavigationActionPolicyAllow
: WKNavigationActionPolicyCancel);
}];
}

- (void)webView:(WKWebView*)webView didFinishNavigation:(WKNavigation*)navigation {
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
[_methodChannel invokeMethod:@"onPageFinished" arguments:@{@"url" : webView.URL.absoluteString}];
}

- (void)webView:(WKWebView *)webView
didFailNavigation:(WKNavigation *)navigation
withError:(NSError *)error {
NSDictionary *arguments = @{
@"url" : webView.URL.absoluteString ?: [NSNull null],
@"code" : [NSNumber numberWithLong:error.code],
@"description" : [error localizedDescription],
};

[_methodChannel invokeMethod:@"onPageReceiveError" arguments:arguments];
}

- (void)webView:(WKWebView *)webView
didFailProvisionalNavigation:(WKNavigation *)navigation
withError:(NSError *)error {
NSDictionary *arguments = @{
@"url" : error.userInfo[NSURLErrorFailingURLStringErrorKey],
@"code" : [NSNumber numberWithLong:error.code],
@"description" : [error localizedDescription],
};

[_methodChannel invokeMethod:@"onPageReceiveError" arguments:arguments];
}

- (void)webView:(WKWebView *)webView
decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler {
if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) {
NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
if (response.statusCode >= 400 && response.statusCode < 600) {
NSDictionary *arguments = @{
@"url" : response.URL.absoluteString ?: [NSNull null],
@"code" : [NSNumber numberWithLong:response.statusCode],
@"description" : [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode],
};

[_methodChannel invokeMethod:@"onPageReceiveError" arguments:arguments];
}
}

decisionHandler(WKNavigationResponsePolicyAllow);
}

- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation {
[_methodChannel invokeMethod:@"onPageStarted"
arguments:@{
@"url" : webView.URL.absoluteString ?: [NSNull null],
}];
}

@end
4 changes: 4 additions & 0 deletions packages/webview_flutter/lib/platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ abstract class WebViewPlatformCallbacksHandler {

/// Invoked by [WebViewPlatformController] when a page has finished loading.
void onPageFinished(String url);

void onPageReceiveError({String url, int code, String description});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs Dartdoc


void onPageStarted(String url);
}

/// Interface for talking to the webview's platform implementation.
Expand Down
9 changes: 9 additions & 0 deletions packages/webview_flutter/lib/src/webview_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
case 'onPageFinished':
_platformCallbacksHandler.onPageFinished(call.arguments['url']);
return null;
case 'onPageReceiveError':
_platformCallbacksHandler.onPageReceiveError(
url: call.arguments['url'],
code: call.arguments['code'],
description: call.arguments['description']);
return null;
case 'onPageStarted':
_platformCallbacksHandler.onPageStarted(call.arguments['url']);
return null;
}
throw MissingPluginException(
'${call.method} was invoked but has no handler');
Expand Down
28 changes: 28 additions & 0 deletions packages/webview_flutter/lib/webview_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ typedef NavigationDecision NavigationDelegate(NavigationRequest navigation);
/// Signature for when a [WebView] has finished loading a page.
typedef void PageFinishedCallback(String url);

/// Signature for when a [WebView] receive a error.
/// Code may be NSURLErrorDomain code or const from Android WebViewClient or http status code.
/// Description is optional
typedef void PageReceiveErrorCallback(String url, int code, String description);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have a guarantee that no WebResourceError code will ever be a valid HTTP response code?

I think we should give developers a single way to detect errors that are the same on all platforms (e.g too many redirects).


/// Signature for when a [WebView] has started loading a page.
typedef void PageStartedCallback(String url);

final RegExp _validChannelNames = RegExp('^[a-zA-Z_][a-zA-Z0-9]*\$');

/// A named channel for receiving messaged from JavaScript code running inside a web view.
Expand Down Expand Up @@ -121,6 +129,8 @@ class WebView extends StatefulWidget {
this.gestureRecognizers,
this.onPageFinished,
this.debuggingEnabled = false,
this.onPageReceiveError,
this.onPageStarted,
}) : assert(javascriptMode != null),
super(key: key);

Expand Down Expand Up @@ -255,6 +265,10 @@ class WebView extends StatefulWidget {
/// By default `debuggingEnabled` is false.
final bool debuggingEnabled;

final PageReceiveErrorCallback onPageReceiveError;

final PageStartedCallback onPageStarted;

@override
State<StatefulWidget> createState() => _WebViewState();
}
Expand Down Expand Up @@ -397,6 +411,20 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler {
}
}

@override
void onPageReceiveError({String url, int code, String description}) {
if (_widget.onPageReceiveError != null) {
_widget.onPageReceiveError(url, code, description);
}
}

@override
void onPageStarted(String url) {
if (_widget.onPageStarted != null) {
_widget.onPageStarted(url);
}
}

void _updateJavascriptChannelsFromSet(Set<JavascriptChannel> channels) {
_javascriptChannels.clear();
if (channels == null) {
Expand Down
Loading