Skip to content

Commit

Permalink
[webview_flutter] Fixes bug when onNavigationRequestCallback returns …
Browse files Browse the repository at this point in the history
…false (flutter#5981)
  • Loading branch information
mvanbeusekom authored Jun 21, 2022
1 parent 42aebbe commit c3ba213
Show file tree
Hide file tree
Showing 5 changed files with 229 additions and 5 deletions.
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.8.13

* Fixes a bug which causes an exception when the `onNavigationRequestCallback` return `false`.

## 2.8.12

* Bumps mockito-inline from 3.11.1 to 4.6.1.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -652,8 +652,8 @@ class WebViewAndroidWebViewClient extends android_webview.WebViewClient {

if (returnValue is bool && returnValue) {
loadUrl!(url, <String, String>{});
} else {
(returnValue as Future<bool>).then((bool shouldLoadUrl) {
} else if (returnValue is Future<bool>) {
returnValue.then((bool shouldLoadUrl) {
if (shouldLoadUrl) {
loadUrl!(url, <String, String>{});
}
Expand All @@ -677,8 +677,8 @@ class WebViewAndroidWebViewClient extends android_webview.WebViewClient {

if (returnValue is bool && returnValue) {
loadUrl!(request.url, <String, String>{});
} else {
(returnValue as Future<bool>).then((bool shouldLoadUrl) {
} else if (returnValue is Future<bool>) {
returnValue.then((bool shouldLoadUrl) {
if (shouldLoadUrl) {
loadUrl!(request.url, <String, String>{});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: webview_flutter_android
description: A Flutter plugin that provides a WebView widget on Android.
repository: https://github.com/flutter/plugins/tree/main/packages/webview_flutter/webview_flutter_android
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22
version: 2.8.12
version: 2.8.13

environment:
sdk: ">=2.14.0 <3.0.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';
import 'dart:typed_data';

import 'package:flutter/widgets.dart';
Expand All @@ -24,6 +25,7 @@ import 'webview_android_widget_test.mocks.dart';
android_webview.WebSettings,
android_webview.WebStorage,
android_webview.WebView,
android_webview.WebResourceRequest,
WebViewAndroidDownloadListener,
WebViewAndroidJavaScriptChannel,
WebViewAndroidWebChromeClient,
Expand Down Expand Up @@ -843,4 +845,192 @@ void main() {
verify(mockPlatformHostApi.setWebContentsDebuggingEnabled(false));
});
});

group('WebViewAndroidWebViewClient', () {
test(
'urlLoading should call loadUrl when onNavigationRequestCallback returns true',
() {
final Completer<void> completer = Completer<void>();
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
true,
loadUrl: (String url, Map<String, String>? headers) async {
completer.complete();
});

webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
expect(completer.isCompleted, isTrue);
});

test(
'urlLoading should call loadUrl when onNavigationRequestCallback returns a Future true',
() async {
final Completer<void> completer = Completer<void>();
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
Future<bool>.value(true),
loadUrl: (String url, Map<String, String>? headers) async {
completer.complete();
});

webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
expect(completer.future, completes);
});

test(
'urlLoading should not call laodUrl when onNavigationRequestCallback returns false',
() async {
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
false,
loadUrl: (String url, Map<String, String>? headers) async {
fail(
'loadUrl should not be called if onNavigationRequestCallback returns false.');
});

webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
});

test(
'urlLoading should not call loadUrl when onNavigationRequestCallback returns a Future false',
() {
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
Future<bool>.value(false),
loadUrl: (String url, Map<String, String>? headers) async {
fail(
'loadUrl should not be called if onNavigationRequestCallback returns false.');
});

webViewClient.urlLoading(MockWebView(), 'https://flutter.dev');
});

test(
'requestLoading should call loadUrl when onNavigationRequestCallback returns true',
() {
final Completer<void> completer = Completer<void>();
final MockWebResourceRequest mockRequest = MockWebResourceRequest();
when(mockRequest.isForMainFrame).thenReturn(true);
when(mockRequest.url).thenReturn('https://flutter.dev');
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
true,
loadUrl: (String url, Map<String, String>? headers) async {
expect(url, 'https://flutter.dev');
completer.complete();
});

webViewClient.requestLoading(MockWebView(), mockRequest);
expect(completer.isCompleted, isTrue);
});

test(
'requestLoading should call loadUrl when onNavigationRequestCallback returns a Future true',
() async {
final Completer<void> completer = Completer<void>();
final MockWebResourceRequest mockRequest = MockWebResourceRequest();
when(mockRequest.isForMainFrame).thenReturn(true);
when(mockRequest.url).thenReturn('https://flutter.dev');
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
Future<bool>.value(true),
loadUrl: (String url, Map<String, String>? headers) async {
expect(url, 'https://flutter.dev');
completer.complete();
});

webViewClient.requestLoading(MockWebView(), mockRequest);
expect(completer.future, completes);
});

test(
'requestLoading should not call loadUrl when onNavigationRequestCallback returns false',
() {
final MockWebResourceRequest mockRequest = MockWebResourceRequest();
when(mockRequest.isForMainFrame).thenReturn(true);
when(mockRequest.url).thenReturn('https://flutter.dev');
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
false,
loadUrl: (String url, Map<String, String>? headers) {
fail(
'loadUrl should not be called if onNavigationRequestCallback returns false.');
});

webViewClient.requestLoading(MockWebView(), mockRequest);
});

test(
'requestLoading should not call loadUrl when onNavigationRequestCallback returns a Future false',
() {
final MockWebResourceRequest mockRequest = MockWebResourceRequest();
when(mockRequest.isForMainFrame).thenReturn(true);
when(mockRequest.url).thenReturn('https://flutter.dev');
final WebViewAndroidWebViewClient webViewClient =
WebViewAndroidWebViewClient.handlesNavigation(
onPageStartedCallback: (_) {},
onPageFinishedCallback: (_) {},
onWebResourceErrorCallback: (_) {},
onNavigationRequestCallback: ({
required bool isForMainFrame,
required String url,
}) =>
Future<bool>.value(false),
loadUrl: (String url, Map<String, String>? headers) {
fail(
'loadUrl should not be called if onNavigationRequestCallback returns false.');
});

webViewClient.requestLoading(MockWebView(), mockRequest);
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,36 @@ class MockWebView extends _i1.Mock implements _i2.WebView {
returnValueForMissingStub: Future<void>.value()) as _i4.Future<void>);
}

/// A class which mocks [WebResourceRequest].
///
/// See the documentation for Mockito's code generation for more information.
class MockWebResourceRequest extends _i1.Mock
implements _i2.WebResourceRequest {
MockWebResourceRequest() {
_i1.throwOnMissingStub(this);
}

@override
String get url =>
(super.noSuchMethod(Invocation.getter(#url), returnValue: '') as String);
@override
bool get isForMainFrame => (super
.noSuchMethod(Invocation.getter(#isForMainFrame), returnValue: false)
as bool);
@override
bool get hasGesture =>
(super.noSuchMethod(Invocation.getter(#hasGesture), returnValue: false)
as bool);
@override
String get method =>
(super.noSuchMethod(Invocation.getter(#method), returnValue: '')
as String);
@override
Map<String, String> get requestHeaders =>
(super.noSuchMethod(Invocation.getter(#requestHeaders),
returnValue: <String, String>{}) as Map<String, String>);
}

/// A class which mocks [WebViewAndroidDownloadListener].
///
/// See the documentation for Mockito's code generation for more information.
Expand Down

0 comments on commit c3ba213

Please sign in to comment.