Skip to content

[webview_flutter_android][webview_flutter_wkwebview] Adds platform implementations for onHttpError #6149

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Mar 14, 2024
Merged
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
@@ -1,5 +1,7 @@
## NEXT
## 3.16.0

* Adds onReceivedHttpError WebViewClient callback to support
`PlatformNavigationDelegate.onHttpError`.
* Updates minimum supported SDK version to Flutter 3.13/Dart 3.1.
* Updates compileSdk to 34.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,58 @@ ArrayList<Object> toList() {
}
}

/** Generated class from Pigeon that represents data sent in messages. */
public static final class WebResourceResponseData {
private @NonNull Long statusCode;

public @NonNull Long getStatusCode() {
return statusCode;
}

public void setStatusCode(@NonNull Long setterArg) {
if (setterArg == null) {
throw new IllegalStateException("Nonnull field \"statusCode\" is null.");
}
this.statusCode = setterArg;
}

/** Constructor is non-public to enforce null safety; use Builder. */
WebResourceResponseData() {}

public static final class Builder {

private @Nullable Long statusCode;

public @NonNull Builder setStatusCode(@NonNull Long setterArg) {
this.statusCode = setterArg;
return this;
}

public @NonNull WebResourceResponseData build() {
WebResourceResponseData pigeonReturn = new WebResourceResponseData();
pigeonReturn.setStatusCode(statusCode);
return pigeonReturn;
}
}

@NonNull
ArrayList<Object> toList() {
ArrayList<Object> toListResult = new ArrayList<Object>(1);
toListResult.add(statusCode);
return toListResult;
}

static @NonNull WebResourceResponseData fromList(@NonNull ArrayList<Object> list) {
WebResourceResponseData pigeonResult = new WebResourceResponseData();
Object statusCode = list.get(0);
pigeonResult.setStatusCode(
(statusCode == null)
? null
: ((statusCode instanceof Integer) ? (Integer) statusCode : (Long) statusCode));
return pigeonResult;
}
}

/** Generated class from Pigeon that represents data sent in messages. */
public static final class WebResourceErrorData {
private @NonNull Long errorCode;
Expand Down Expand Up @@ -2388,6 +2440,8 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
return WebResourceErrorData.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 129:
return WebResourceRequestData.fromList((ArrayList<Object>) readValue(buffer));
case (byte) 130:
return WebResourceResponseData.fromList((ArrayList<Object>) readValue(buffer));
default:
return super.readValueOfType(type, buffer);
}
Expand All @@ -2401,6 +2455,9 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, Object value) {
} else if (value instanceof WebResourceRequestData) {
stream.write(129);
writeValue(stream, ((WebResourceRequestData) value).toList());
} else if (value instanceof WebResourceResponseData) {
stream.write(130);
writeValue(stream, ((WebResourceResponseData) value).toList());
} else {
super.writeValue(stream, value);
}
Expand Down Expand Up @@ -2455,6 +2512,23 @@ public void onPageFinished(
channelReply -> callback.reply(null));
}

public void onReceivedHttpError(
@NonNull Long instanceIdArg,
@NonNull Long webViewInstanceIdArg,
@NonNull WebResourceRequestData requestArg,
@NonNull WebResourceResponseData responseArg,
@NonNull Reply<Void> callback) {
BasicMessageChannel<Object> channel =
new BasicMessageChannel<>(
binaryMessenger,
"dev.flutter.pigeon.webview_flutter_android.WebViewClientFlutterApi.onReceivedHttpError",
getCodec());
channel.send(
new ArrayList<Object>(
Arrays.asList(instanceIdArg, webViewInstanceIdArg, requestArg, responseArg)),
channelReply -> callback.reply(null));
}

public void onReceivedRequestError(
@NonNull Long instanceIdArg,
@NonNull Long webViewInstanceIdArg,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.webkit.HttpAuthHandler;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -70,6 +71,16 @@ static GeneratedAndroidWebView.WebResourceRequestData createWebResourceRequestDa
return requestData.build();
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
static GeneratedAndroidWebView.WebResourceResponseData createWebResourceResponseData(
WebResourceResponse response) {
final GeneratedAndroidWebView.WebResourceResponseData.Builder responseData =
new GeneratedAndroidWebView.WebResourceResponseData.Builder()
.setStatusCode((long) response.getStatusCode());

return responseData.build();
}

/**
* Creates a Flutter api that sends messages to Dart.
*
Expand Down Expand Up @@ -110,6 +121,25 @@ public void onPageFinished(
onPageFinished(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
}

/** Passes arguments from {@link WebViewClient#onReceivedHttpError} to Dart. */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void onReceivedHttpError(
@NonNull WebViewClient webViewClient,
@NonNull WebView webView,
@NonNull WebResourceRequest request,
@NonNull WebResourceResponse response,
@NonNull Reply<Void> callback) {
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier = instanceManager.getIdentifierForStrongReference(webView);
onReceivedHttpError(
getIdentifierForClient(webViewClient),
webViewIdentifier,
createWebResourceRequestData(request),
createWebResourceResponseData(response),
callback);
}

/**
* Passes arguments from {@link WebViewClient#onReceivedError(WebView, WebResourceRequest,
* WebResourceError)} to Dart.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import android.webkit.HttpAuthHandler;
import android.webkit.WebResourceError;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -55,6 +56,14 @@ public void onPageFinished(@NonNull WebView view, @NonNull String url) {
flutterApi.onPageFinished(this, view, url, reply -> {});
}

@Override
public void onReceivedHttpError(
@NonNull WebView view,
@NonNull WebResourceRequest request,
@NonNull WebResourceResponse response) {
flutterApi.onReceivedHttpError(this, view, request, response, reply -> {});
}

@Override
public void onReceivedError(
@NonNull WebView view,
Expand Down Expand Up @@ -140,6 +149,15 @@ public void onPageFinished(@NonNull WebView view, @NonNull String url) {
flutterApi.onPageFinished(this, view, url, reply -> {});
}

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public void onReceivedHttpError(
@NonNull WebView view,
@NonNull WebResourceRequest request,
@NonNull WebResourceResponse response) {
flutterApi.onReceivedHttpError(this, view, request, response, reply -> {});
}

// This method is only called when the WebViewFeature.RECEIVE_WEB_RESOURCE_ERROR feature is
// enabled. The deprecated method is called when a device doesn't support this.
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import android.net.Uri;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
Expand Down Expand Up @@ -136,4 +137,28 @@ public void doUpdateVisitedHistory() {
.doUpdateVisitedHistory(
eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), eq(true), any());
}

@Test
public void onReceivedHttpError() {
final Uri mockUri = mock(Uri.class);
when(mockUri.toString()).thenReturn("");

final WebResourceRequest mockRequest = mock(WebResourceRequest.class);
when(mockRequest.getMethod()).thenReturn("method");
when(mockRequest.getUrl()).thenReturn(mockUri);
when(mockRequest.isForMainFrame()).thenReturn(true);
when(mockRequest.getRequestHeaders()).thenReturn(null);

final WebResourceResponse mockResponse = mock(WebResourceResponse.class);
when(mockResponse.getStatusCode()).thenReturn(404);

webViewClient.onReceivedHttpError(mockWebView, mockRequest, mockResponse);
verify(mockFlutterApi)
.onReceivedHttpError(
eq(webViewClient),
eq(mockWebView),
any(WebResourceRequest.class),
any(WebResourceResponse.class),
any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,81 @@ Future<void> main() async {
await pageFinishCompleter.future;
});

testWidgets('onHttpError', (WidgetTester tester) async {
final Completer<HttpResponseError> errorCompleter =
Completer<HttpResponseError>();

final PlatformWebViewController controller = PlatformWebViewController(
const PlatformWebViewControllerCreationParams(),
);
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
final PlatformNavigationDelegate delegate = PlatformNavigationDelegate(
const PlatformNavigationDelegateCreationParams(),
);
unawaited(delegate.setOnHttpError((HttpResponseError error) {
errorCompleter.complete(error);
}));
unawaited(controller.setPlatformNavigationDelegate(delegate));
unawaited(controller.loadRequest(
LoadRequestParams(uri: Uri.parse('$prefixUrl/favicon.ico')),
));

await tester.pumpWidget(Builder(
builder: (BuildContext context) {
return PlatformWebViewWidget(
PlatformWebViewWidgetCreationParams(controller: controller),
).build(context);
},
));

final HttpResponseError error = await errorCompleter.future;

expect(error, isNotNull);
expect(error.response?.statusCode, 404);
});

testWidgets('onHttpError is not called when no HTTP error is received',
(WidgetTester tester) async {
const String testPage = '''
<!DOCTYPE html><html>
</head>
<body>
</body>
</html>
''';

final Completer<HttpResponseError> errorCompleter =
Completer<HttpResponseError>();
final Completer<void> pageFinishCompleter = Completer<void>();

final PlatformWebViewController controller = PlatformWebViewController(
const PlatformWebViewControllerCreationParams(),
);
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));
final PlatformNavigationDelegate delegate = PlatformNavigationDelegate(
const PlatformNavigationDelegateCreationParams(),
);
unawaited(delegate.setOnHttpError((HttpResponseError error) {
errorCompleter.complete(error);
}));
unawaited(delegate.setOnPageFinished(
(_) => pageFinishCompleter.complete(),
));
unawaited(controller.setPlatformNavigationDelegate(delegate));
unawaited(controller.loadHtmlString(testPage));

await tester.pumpWidget(Builder(
builder: (BuildContext context) {
return PlatformWebViewWidget(
PlatformWebViewWidgetCreationParams(controller: controller),
).build(context);
},
));

expect(errorCompleter.future, doesNotComplete);
await pageFinishCompleter.future;
});

testWidgets('can block requests', (WidgetTester tester) async {
Completer<void> pageLoaded = Completer<void>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,11 @@ class _WebViewExampleState extends State<WebViewExample> {
..setOnPageFinished((String url) {
debugPrint('Page finished loading: $url');
})
..setOnHttpError((HttpResponseError error) {
debugPrint(
'HTTP error occured on page: ${error.response?.statusCode}',
);
})
..setOnWebResourceError((WebResourceError error) {
debugPrint('''
Page resource error:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ class AndroidWebViewProxy {
final android_webview.WebViewClient Function({
void Function(android_webview.WebView webView, String url)? onPageStarted,
void Function(android_webview.WebView webView, String url)? onPageFinished,
void Function(
android_webview.WebView webView,
android_webview.WebResourceRequest request,
android_webview.WebResourceResponse response,
)? onReceivedHttpError,
void Function(
android_webview.WebView webView,
android_webview.WebResourceRequest request,
Expand Down
Loading