Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Conversation

bparrishMines
Copy link
Contributor

@bparrishMines bparrishMines commented Apr 7, 2020

Description

This adds the feature where the webview can report an error when loading page resources.

After looking for error reporting in the Webview implementation of each platform I found Android uses onReceivedError in its WebviewClient and iOS uses webView:didFailNavigation:withError: and webView:didFailProvisionalNavigation:withError: in its WKNavigationDelegate.

However, each platform handles error codes differently. Android provides a WebResourceError which has an int code that is defined as a constant in WebViewClient. iOS uses a generic NSError and doesn't specify the possible values for the error code.

Related Issues

Fixes flutter/flutter#53023

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • My PR includes unit or integration tests for all changed/updated/fixed behaviors (See Contributor Guide).
  • All existing and new tests are passing.
  • I updated/added relevant documentation (doc comments with ///).
  • The analyzer (flutter analyze) does not report any problems on my PR.
  • I read and followed the Flutter Style Guide.
  • The title of the PR starts with the name of the plugin surrounded by square brackets, e.g. [shared_preferences]
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy.
  • I updated CHANGELOG.md to add a description of the change.
  • I signed the CLA.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Does your PR require plugin users to manually update their apps to accommodate your change?

  • Yes, this is a breaking change (please indicate a breaking change in CHANGELOG.md and increment major revision).
  • No, this is not a breaking change.

@bparrishMines bparrishMines changed the title [webview_flutter] Add onReceivedError callback [WIP][webview_flutter] Add onReceivedError callback Apr 7, 2020
@bparrishMines bparrishMines changed the title [WIP][webview_flutter] Add onReceivedError callback [webview_flutter] Add onReceivedError callback Apr 7, 2020
@bparrishMines bparrishMines requested a review from amirh April 7, 2020 05:36
Copy link
Contributor

@amirh amirh left a comment

Choose a reason for hiding this comment

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

Thanks!
Overall looks good, left a few nits and questions.

final Map<String, String> errorResult = await errorCompleter.future;
expect(errorResult['errorCode'], isNotNull);
expect(errorResult['description'], isNotNull);
});
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add a test with a valid url that verifies the error callback is not invoked on a valid url. I'd use a data url like:

'data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWw+',

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

}

final String message =
String.format(Locale.getDefault(), "Could not find a string for errorCode: %d", errorCode);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit:

Is %d formatting affected by the locale?
Also seems like you can omit specifying Locale.getDefault() as this is the default for the String.format version that doesn't take a locale.

Copy link
Contributor Author

@bparrishMines bparrishMines Apr 7, 2020

Choose a reason for hiding this comment

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

I only added the Locale.getDefault() to get rid of the lint warning. It does change the digit to whatever locale the device is set to. I don't think it would change the interpretation of the digit though.

Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, my read of https://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax is that %d formatting is not affected by the locale.

/// (e.g. The error code of `WebViewClient.ERROR_AUTHENTICATION` on Android
/// translates to `AUTHENTICATION` in Dart.
///
/// On iOS, the error code will represent the "domain" and the "code" for an
Copy link
Contributor

Choose a reason for hiding this comment

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

My preference would be to avoid having different semantic meaning to the same field on different platforms.
Instead I'd have different fields with a single semantic meaning which may not be set on all platforms.

Copy link
Contributor Author

@bparrishMines bparrishMines Apr 7, 2020

Choose a reason for hiding this comment

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

done. I added a domain only for iOS and a errorType that can be null on iOS.

/// translates to `AUTHENTICATION` in Dart.
///
/// On iOS, the error code will represent the "domain" and the "code" for an
/// `NSError` in Objective-C. The format will be `$domain: $code`. See
Copy link
Contributor

Choose a reason for hiding this comment

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

Is $code the http status code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think it's possible for it to be an http status code, but the documentation doesn't say anything about it:
https://developer.apple.com/documentation/webkit/wknavigationdelegate/1455623-webview?language=objc

However, their https://developer.apple.com/documentation/webkit/wkerrorcode?language=objc states that these are possible values. I can't tell if these are the ONLY possible values or not.

typedef void PageFinishedCallback(String url);

/// Signature for when a [WebView] has failed to load a resource.
typedef void ReceivedErrorCallback(String errorCode, 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.

When reading this my gut was that we should have an error class though I'm not finding good arguments any way 😄 (slight pro of an object is that it's easier for users to pass the error information around within their app)

I wonder if you had a reason to prefer not introducing an error class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I was in favor of a data class as well. I chose not to create one to be consistent with the style of the WebViewPlatformCallbacksHandler. Method

void onJavaScriptChannelMessage(String channel, String message);

and

FutureOr<bool> onNavigationRequest({String url, bool isForMainFrame});

don't use them either.

However, since I need more than 2 parameters now, I created a WebResourceError class.

this.gestureRecognizers,
this.onPageStarted,
this.onPageFinished,
this.onReceivedError,
Copy link
Contributor

Choose a reason for hiding this comment

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

another nit: why onReceivedError and not onError/onResourceLoadError?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was the name of the method in WebViewClient. Same as onPageStarted and onPageFinished. It was mostly to keep the pattern of method name origins, but I think WebResourceError is more expressive. (or something similar)

Copy link
Contributor

@amirh amirh left a comment

Choose a reason for hiding this comment

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

LGTM after addressing remaining comments

}

final String message =
String.format(Locale.getDefault(), "Could not find a string for errorCode: %d", errorCode);
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, my read of https://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax is that %d formatting is not affected by the locale.

expect(currentUrl, 'https://www.google.com/');
});

testWidgets('onReceivedError', (WidgetTester tester) async {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: onWebResourceError

if (Platform.isAndroid) expect(error.errorType, isNotNull);
});

testWidgets('onReceivedError is not called with valid url',
Copy link
Contributor

Choose a reason for hiding this comment

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

ditto

javaScriptResultTypeIsUnsupported,
}

/// On Android, the error code will be a constant from
Copy link
Contributor

Choose a reason for hiding this comment

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

This class comment probably needs an update as well?

/// for more information on error handling on iOS.
final String domain;

/// Describes the error.
Copy link
Contributor

Choose a reason for hiding this comment

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

Style guide says avoid useless documentation


/// The type of error.
///
/// This is will never be null on Android, but can be null on iOS.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/is//

? null
: WebResourceErrorType.values.firstWhere(
(WebResourceErrorType type) {
final String enumName = type.toString().replaceFirst(
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, may be easier to follow as:

return type.toString() == '$WebResourceErrorType.${call.arguments['errorType']}';

@amirh
Copy link
Contributor

amirh commented Apr 8, 2020

Don't forget to add a version bump and a changelog entry

@bparrishMines bparrishMines merged commit 8819b21 into flutter:master Apr 9, 2020
@bparrishMines bparrishMines deleted the onReceivedError branch April 9, 2020 00:54
jerryzhoujw pushed a commit to jerryzhoujw/plugins that referenced this pull request Apr 10, 2020
kevin-X-lee pushed a commit to kevin-X-lee/plugins that referenced this pull request May 6, 2020
This reverts commit 8819b21.

# Conflicts:
#	packages/webview_flutter/CHANGELOG.md
#	packages/webview_flutter/pubspec.yaml
EdwinRomelta pushed a commit to EdwinRomelta/plugins that referenced this pull request Jun 11, 2020
jorgefspereira pushed a commit to jorgefspereira/plugins_flutter that referenced this pull request Oct 10, 2020
FlutterSu pushed a commit to FlutterSu/flutter-plugins that referenced this pull request Nov 20, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Provide a way for a webview to report an error in the onPageFinished callback

3 participants