Skip to content

Commit 92a8b7a

Browse files
[webview_flutter_android][webview_flutter_wkwebview] Adds platform implementations for onHttpError (#6149)
Copy of #3695 since it doesn't contain permission to edit from contributors. Part of flutter/flutter#39502 Full PR #3278
1 parent e8ab632 commit 92a8b7a

File tree

50 files changed

+2196
-1092
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2196
-1092
lines changed

packages/webview_flutter/webview_flutter_android/CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
## NEXT
1+
## 3.16.0
22

3+
* Adds onReceivedHttpError WebViewClient callback to support
4+
`PlatformNavigationDelegate.onHttpError`.
35
* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1.
46
* Updates compileSdk to 34.
57

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/GeneratedAndroidWebView.java

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,58 @@ ArrayList<Object> toList() {
316316
}
317317
}
318318

319+
/** Generated class from Pigeon that represents data sent in messages. */
320+
public static final class WebResourceResponseData {
321+
private @NonNull Long statusCode;
322+
323+
public @NonNull Long getStatusCode() {
324+
return statusCode;
325+
}
326+
327+
public void setStatusCode(@NonNull Long setterArg) {
328+
if (setterArg == null) {
329+
throw new IllegalStateException("Nonnull field \"statusCode\" is null.");
330+
}
331+
this.statusCode = setterArg;
332+
}
333+
334+
/** Constructor is non-public to enforce null safety; use Builder. */
335+
WebResourceResponseData() {}
336+
337+
public static final class Builder {
338+
339+
private @Nullable Long statusCode;
340+
341+
public @NonNull Builder setStatusCode(@NonNull Long setterArg) {
342+
this.statusCode = setterArg;
343+
return this;
344+
}
345+
346+
public @NonNull WebResourceResponseData build() {
347+
WebResourceResponseData pigeonReturn = new WebResourceResponseData();
348+
pigeonReturn.setStatusCode(statusCode);
349+
return pigeonReturn;
350+
}
351+
}
352+
353+
@NonNull
354+
ArrayList<Object> toList() {
355+
ArrayList<Object> toListResult = new ArrayList<Object>(1);
356+
toListResult.add(statusCode);
357+
return toListResult;
358+
}
359+
360+
static @NonNull WebResourceResponseData fromList(@NonNull ArrayList<Object> list) {
361+
WebResourceResponseData pigeonResult = new WebResourceResponseData();
362+
Object statusCode = list.get(0);
363+
pigeonResult.setStatusCode(
364+
(statusCode == null)
365+
? null
366+
: ((statusCode instanceof Integer) ? (Integer) statusCode : (Long) statusCode));
367+
return pigeonResult;
368+
}
369+
}
370+
319371
/** Generated class from Pigeon that represents data sent in messages. */
320372
public static final class WebResourceErrorData {
321373
private @NonNull Long errorCode;
@@ -2388,6 +2440,8 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
23882440
return WebResourceErrorData.fromList((ArrayList<Object>) readValue(buffer));
23892441
case (byte) 129:
23902442
return WebResourceRequestData.fromList((ArrayList<Object>) readValue(buffer));
2443+
case (byte) 130:
2444+
return WebResourceResponseData.fromList((ArrayList<Object>) readValue(buffer));
23912445
default:
23922446
return super.readValueOfType(type, buffer);
23932447
}
@@ -2401,6 +2455,9 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
24012455
} else if (value instanceof WebResourceRequestData) {
24022456
stream.write(129);
24032457
writeValue(stream, ((WebResourceRequestData) value).toList());
2458+
} else if (value instanceof WebResourceResponseData) {
2459+
stream.write(130);
2460+
writeValue(stream, ((WebResourceResponseData) value).toList());
24042461
} else {
24052462
super.writeValue(stream, value);
24062463
}
@@ -2455,6 +2512,23 @@ public void onPageFinished(
24552512
channelReply -> callback.reply(null));
24562513
}
24572514

2515+
public void onReceivedHttpError(
2516+
@NonNull Long instanceIdArg,
2517+
@NonNull Long webViewInstanceIdArg,
2518+
@NonNull WebResourceRequestData requestArg,
2519+
@NonNull WebResourceResponseData responseArg,
2520+
@NonNull Reply<Void> callback) {
2521+
BasicMessageChannel<Object> channel =
2522+
new BasicMessageChannel<>(
2523+
binaryMessenger,
2524+
"dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError",
2525+
getCodec());
2526+
channel.send(
2527+
new ArrayList<Object>(
2528+
Arrays.asList(instanceIdArg, webViewInstanceIdArg, requestArg, responseArg)),
2529+
channelReply -> callback.reply(null));
2530+
}
2531+
24582532
public void onReceivedRequestError(
24592533
@NonNull Long instanceIdArg,
24602534
@NonNull Long webViewInstanceIdArg,

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientFlutterApiImpl.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import android.webkit.HttpAuthHandler;
1010
import android.webkit.WebResourceError;
1111
import android.webkit.WebResourceRequest;
12+
import android.webkit.WebResourceResponse;
1213
import android.webkit.WebView;
1314
import android.webkit.WebViewClient;
1415
import androidx.annotation.NonNull;
@@ -70,6 +71,16 @@ static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestDa
7071
return requestData.build();
7172
}
7273

74+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
75+
static GeneratedAndroidWebView.WebResourceResponseData createWebResourceResponseData(
76+
WebResourceResponse response) {
77+
final GeneratedAndroidWebView.WebResourceResponseData.Builder responseData =
78+
new GeneratedAndroidWebView.WebResourceResponseData.Builder()
79+
.setStatusCode((long) response.getStatusCode());
80+
81+
return responseData.build();
82+
}
83+
7384
/**
7485
* Creates a Flutter api that sends messages to Dart.
7586
*
@@ -110,6 +121,25 @@ public void onPageFinished(
110121
onPageFinished(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
111122
}
112123

124+
/** Passes arguments from {@link WebViewClient#onReceivedHttpError} to Dart. */
125+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
126+
public void onReceivedHttpError(
127+
@NonNull WebViewClient webViewClient,
128+
@NonNull WebView webView,
129+
@NonNull WebResourceRequest request,
130+
@NonNull WebResourceResponse response,
131+
@NonNull Reply<Void> callback) {
132+
webViewFlutterApi.create(webView, reply -> {});
133+
134+
final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
135+
onReceivedHttpError(
136+
getIdentifierForClient(webViewClient),
137+
webViewIdentifier,
138+
createWebResourceRequestData(request),
139+
createWebResourceResponseData(response),
140+
callback);
141+
}
142+
113143
/**
114144
* Passes arguments from {@link WebViewClient#onReceivedError(WebView, WebResourceRequest,
115145
* WebResourceError)} to Dart.

packages/webview_flutter/webview_flutter_android/android/src/main/java/io/flutter/plugins/webviewflutter/WebViewClientHostApiImpl.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import android.webkit.HttpAuthHandler;
1313
import android.webkit.WebResourceError;
1414
import android.webkit.WebResourceRequest;
15+
import android.webkit.WebResourceResponse;
1516
import android.webkit.WebView;
1617
import android.webkit.WebViewClient;
1718
import androidx.annotation.NonNull;
@@ -55,6 +56,14 @@ public void onPageFinished(@NonNull WebView view, @NonNull String url) {
5556
flutterApi.onPageFinished(this, view, url, reply -> {});
5657
}
5758

59+
@Override
60+
public void onReceivedHttpError(
61+
@NonNull WebView view,
62+
@NonNull WebResourceRequest request,
63+
@NonNull WebResourceResponse response) {
64+
flutterApi.onReceivedHttpError(this, view, request, response, reply -> {});
65+
}
66+
5867
@Override
5968
public void onReceivedError(
6069
@NonNull WebView view,
@@ -140,6 +149,15 @@ public void onPageFinished(@NonNull WebView view, @NonNull String url) {
140149
flutterApi.onPageFinished(this, view, url, reply -> {});
141150
}
142151

152+
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
153+
@Override
154+
public void onReceivedHttpError(
155+
@NonNull WebView view,
156+
@NonNull WebResourceRequest request,
157+
@NonNull WebResourceResponse response) {
158+
flutterApi.onReceivedHttpError(this, view, request, response, reply -> {});
159+
}
160+
143161
// This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is
144162
// enabled. The deprecated method is called when a device doesn't support this.
145163
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)

packages/webview_flutter/webview_flutter_android/android/src/test/java/io/flutter/plugins/webviewflutter/WebViewClientTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
import android.net.Uri;
1515
import android.webkit.WebResourceRequest;
16+
import android.webkit.WebResourceResponse;
1617
import android.webkit.WebView;
1718
import android.webkit.WebViewClient;
1819
import androidx.annotation.NonNull;
@@ -136,4 +137,28 @@ public void doUpdateVisitedHistory() {
136137
.doUpdateVisitedHistory(
137138
eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), eq(true), any());
138139
}
140+
141+
@Test
142+
public void onReceivedHttpError() {
143+
final Uri mockUri = mock(Uri.class);
144+
when(mockUri.toString()).thenReturn("");
145+
146+
final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
147+
when(mockRequest.getMethod()).thenReturn("method");
148+
when(mockRequest.getUrl()).thenReturn(mockUri);
149+
when(mockRequest.isForMainFrame()).thenReturn(true);
150+
when(mockRequest.getRequestHeaders()).thenReturn(null);
151+
152+
final WebResourceResponse mockResponse = mock(WebResourceResponse.class);
153+
when(mockResponse.getStatusCode()).thenReturn(404);
154+
155+
webViewClient.onReceivedHttpError(mockWebView, mockRequest, mockResponse);
156+
verify(mockFlutterApi)
157+
.onReceivedHttpError(
158+
eq(webViewClient),
159+
eq(mockWebView),
160+
any(WebResourceRequest.class),
161+
any(WebResourceResponse.class),
162+
any());
163+
}
139164
}

packages/webview_flutter/webview_flutter_android/example/integration_test/webview_flutter_test.dart

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,6 +978,81 @@ Future<void> main() async {
978978
await pageFinishCompleter.future;
979979
});
980980

981+
testWidgets('onHttpError', (WidgetTester tester) async {
982+
final Completer<HttpResponseError> errorCompleter =
983+
Completer<HttpResponseError>();
984+
985+
final PlatformWebViewController controller = PlatformWebViewController(
986+
const PlatformWebViewControllerCreationParams(),
987+
);
988+
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
989+
final PlatformNavigationDelegate delegate = PlatformNavigationDelegate(
990+
const PlatformNavigationDelegateCreationParams(),
991+
);
992+
unawaited(delegate.setOnHttpError((HttpResponseError error) {
993+
errorCompleter.complete(error);
994+
}));
995+
unawaited(controller.setPlatformNavigationDelegate(delegate));
996+
unawaited(controller.loadRequest(
997+
LoadRequestParams(uri: Uri.parse('$prefixUrl/favicon.ico')),
998+
));
999+
1000+
await tester.pumpWidget(Builder(
1001+
builder: (BuildContext context) {
1002+
return PlatformWebViewWidget(
1003+
PlatformWebViewWidgetCreationParams(controller: controller),
1004+
).build(context);
1005+
},
1006+
));
1007+
1008+
final HttpResponseError error = await errorCompleter.future;
1009+
1010+
expect(error, isNotNull);
1011+
expect(error.response?.statusCode, 404);
1012+
});
1013+
1014+
testWidgets('onHttpError is not called when no HTTP error is received',
1015+
(WidgetTester tester) async {
1016+
const String testPage = '''
1017+
<!DOCTYPE html><html>
1018+
</head>
1019+
<body>
1020+
</body>
1021+
</html>
1022+
''';
1023+
1024+
final Completer<HttpResponseError> errorCompleter =
1025+
Completer<HttpResponseError>();
1026+
final Completer<void> pageFinishCompleter = Completer<void>();
1027+
1028+
final PlatformWebViewController controller = PlatformWebViewController(
1029+
const PlatformWebViewControllerCreationParams(),
1030+
);
1031+
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
1032+
final PlatformNavigationDelegate delegate = PlatformNavigationDelegate(
1033+
const PlatformNavigationDelegateCreationParams(),
1034+
);
1035+
unawaited(delegate.setOnHttpError((HttpResponseError error) {
1036+
errorCompleter.complete(error);
1037+
}));
1038+
unawaited(delegate.setOnPageFinished(
1039+
(_) => pageFinishCompleter.complete(),
1040+
));
1041+
unawaited(controller.setPlatformNavigationDelegate(delegate));
1042+
unawaited(controller.loadHtmlString(testPage));
1043+
1044+
await tester.pumpWidget(Builder(
1045+
builder: (BuildContext context) {
1046+
return PlatformWebViewWidget(
1047+
PlatformWebViewWidgetCreationParams(controller: controller),
1048+
).build(context);
1049+
},
1050+
));
1051+
1052+
expect(errorCompleter.future, doesNotComplete);
1053+
await pageFinishCompleter.future;
1054+
});
1055+
9811056
testWidgets('can block requests', (WidgetTester tester) async {
9821057
Completer<void> pageLoaded = Completer<void>();
9831058

packages/webview_flutter/webview_flutter_android/example/lib/main.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,11 @@ class _WebViewExampleState extends State<WebViewExample> {
173173
..setOnPageFinished((String url) {
174174
debugPrint('Page finished loading: $url');
175175
})
176+
..setOnHttpError((HttpResponseError error) {
177+
debugPrint(
178+
'HTTP error occured on page: ${error.response?.statusCode}',
179+
);
180+
})
176181
..setOnWebResourceError((WebResourceError error) {
177182
debugPrint('''
178183
Page resource error:

packages/webview_flutter/webview_flutter_android/lib/src/android_proxy.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ class AndroidWebViewProxy {
6565
final android_webview.WebViewClient Function({
6666
void Function(android_webview.WebView webView, String url)? onPageStarted,
6767
void Function(android_webview.WebView webView, String url)? onPageFinished,
68+
void Function(
69+
android_webview.WebView webView,
70+
android_webview.WebResourceRequest request,
71+
android_webview.WebResourceResponse response,
72+
)? onReceivedHttpError,
6873
void Function(
6974
android_webview.WebView webView,
7075
android_webview.WebResourceRequest request,

0 commit comments

Comments
 (0)