Skip to content

[webview_flutter_android] [webview_flutter_wkwebview] Adds support for PlatformNavigationDelegate.onUrlChange #3653

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 13 commits into from
Apr 12, 2023
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,6 @@
## NEXT
## 3.5.0

* Adds support for `PlatformNavigationDelegate.onUrlChange`.
* Bumps androidx.webkit:webkit from 1.6.0 to 1.6.1.
* Fixes common typos in tests and documentation.

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.webkit.WebResourceErrorCompat;
import io.flutter.plugin.common.BinaryMessenger;
Expand Down Expand Up @@ -202,6 +203,21 @@ public void urlLoading(
urlLoading(getIdentifierForClient(webViewClient), webViewIdentifier, urlArg, callback);
}

/** Passes arguments from {@link WebViewClient#doUpdateVisitedHistory} to Dart. */
public void doUpdateVisitedHistory(
@NonNull WebViewClient webViewClient,
@NonNull WebView webView,
@NonNull String url,
boolean isReload,
@NonNull Reply<Void> callback) {
webViewFlutterApi.create(webView, reply -> {});

final Long webViewIdentifier =
Objects.requireNonNull(instanceManager.getIdentifierForStrongReference(webView));
doUpdateVisitedHistory(
getIdentifierForClient(webViewClient), webViewIdentifier, url, isReload, callback);
}

private long getIdentifierForClient(WebViewClient webViewClient) {
final Long identifier = instanceManager.getIdentifierForStrongReference(webViewClient);
if (identifier == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
return returnValueForShouldOverrideUrlLoading;
}

@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
flutterApi.doUpdateVisitedHistory(this, view, url, isReload, reply -> {});
}

@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
// Deliberately empty. Occasionally the webview will mark events as having failed to be
Expand Down Expand Up @@ -155,7 +160,12 @@ public boolean shouldOverrideUrlLoading(WebView view, String url) {
}

@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
flutterApi.doUpdateVisitedHistory(this, view, url, isReload, reply -> {});
}

@Override
public void onUnhandledKeyEvent(@NonNull WebView view, @NonNull KeyEvent event) {
// Deliberately empty. Occasionally the webview will mark events as having failed to be
// handled even though they were handled. We don't want to propagate those as they're not
// truly lost.
Expand All @@ -175,7 +185,8 @@ public static class WebViewClientCreator {
* @param flutterApi handles sending messages to Dart
* @return the created {@link WebViewClient}
*/
public WebViewClient createWebViewClient(WebViewClientFlutterApiImpl flutterApi) {
@NonNull
public WebViewClient createWebViewClient(@NonNull WebViewClientFlutterApiImpl flutterApi) {
// WebViewClientCompat is used to get
// shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
// invoked by the webview on older Android devices, without it pages that use iframes will
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import android.webkit.WebResourceRequest;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCompatImpl;
import io.flutter.plugins.webviewflutter.WebViewClientHostApiImpl.WebViewClientCreator;
import java.util.HashMap;
Expand Down Expand Up @@ -111,8 +112,10 @@ public void setReturnValueForShouldOverrideUrlLoading() {
new WebViewClientHostApiImpl(
instanceManager,
new WebViewClientCreator() {
@NonNull
@Override
public WebViewClient createWebViewClient(WebViewClientFlutterApiImpl flutterApi) {
public WebViewClient createWebViewClient(
@NonNull WebViewClientFlutterApiImpl flutterApi) {
return mockWebViewClient;
}
},
Expand All @@ -123,4 +126,12 @@ public WebViewClient createWebViewClient(WebViewClientFlutterApiImpl flutterApi)

verify(mockWebViewClient).setReturnValueForShouldOverrideUrlLoading(false);
}

@Test
public void doUpdateVisitedHistory() {
webViewClient.doUpdateVisitedHistory(mockWebView, "https://www.google.com", true);
verify(mockFlutterApi)
.doUpdateVisitedHistory(
eq(webViewClient), eq(mockWebView), eq("https://www.google.com"), eq(true), any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,81 @@ Future<void> main() async {
final String? currentUrl = await controller.currentUrl();
expect(currentUrl, secondaryUrl);
});

testWidgets('can receive url changes', (WidgetTester tester) async {
final Completer<void> pageLoaded = Completer<void>();

final PlatformNavigationDelegate navigationDelegate =
PlatformNavigationDelegate(
const PlatformNavigationDelegateCreationParams(),
)..setOnPageFinished((_) => pageLoaded.complete());

final PlatformWebViewController controller = PlatformWebViewController(
const PlatformWebViewControllerCreationParams(),
)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setPlatformNavigationDelegate(navigationDelegate)
..loadRequest(LoadRequestParams(uri: Uri.parse(blankPageEncoded)));

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

await pageLoaded.future;
await navigationDelegate.setOnPageFinished((_) {});

final Completer<String> urlChangeCompleter = Completer<String>();
await navigationDelegate.setOnUrlChange((UrlChange change) {
urlChangeCompleter.complete(change.url);
});

await controller.runJavaScript('location.href = "$primaryUrl"');

await expectLater(urlChangeCompleter.future, completion(primaryUrl));
});

testWidgets('can receive updates to history state',
(WidgetTester tester) async {
final Completer<void> pageLoaded = Completer<void>();

final PlatformNavigationDelegate navigationDelegate =
PlatformNavigationDelegate(
const PlatformNavigationDelegateCreationParams(),
)..setOnPageFinished((_) => pageLoaded.complete());

final PlatformWebViewController controller = PlatformWebViewController(
const PlatformWebViewControllerCreationParams(),
)
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setPlatformNavigationDelegate(navigationDelegate)
..loadRequest(LoadRequestParams(uri: Uri.parse(primaryUrl)));

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

await pageLoaded.future;
await navigationDelegate.setOnPageFinished((_) {});

final Completer<String> urlChangeCompleter = Completer<String>();
await navigationDelegate.setOnUrlChange((UrlChange change) {
urlChangeCompleter.complete(change.url);
});

await controller.runJavaScript(
'window.history.pushState({}, "", "secondary.txt");',
);

await expectLater(urlChangeCompleter.future, completion(secondaryUrl));
});
});

testWidgets('target _blank opens in same window',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ Page resource error:
}
debugPrint('allowing navigation to ${request.url}');
return NavigationDecision.navigate;
})
..setOnUrlChange((UrlChange change) {
debugPrint('url change to ${change.url}');
}),
)
..addJavaScriptChannel(JavaScriptChannelParams(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
webview_flutter_platform_interface: ^2.0.0
webview_flutter_platform_interface: ^2.1.0

dev_dependencies:
espresso: ^0.2.0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ class AndroidWebViewProxy {
android_webview.WebResourceRequest request,
)? requestLoading,
void Function(android_webview.WebView webView, String url)? urlLoading,
void Function(android_webview.WebView webView, String url, bool isReload)?
doUpdateVisitedHistory,
}) createAndroidWebViewClient;

/// Constructs a [android_webview.FlutterAssetManager].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,7 @@ class WebViewClient extends JavaObject {
@Deprecated('Only called on Android version < 23.') this.onReceivedError,
this.requestLoading,
this.urlLoading,
this.doUpdateVisitedHistory,
@visibleForTesting super.binaryMessenger,
@visibleForTesting super.instanceManager,
}) : super.detached() {
Expand All @@ -696,6 +697,7 @@ class WebViewClient extends JavaObject {
@Deprecated('Only called on Android version < 23.') this.onReceivedError,
this.requestLoading,
this.urlLoading,
this.doUpdateVisitedHistory,
super.binaryMessenger,
super.instanceManager,
}) : super.detached();
Expand Down Expand Up @@ -840,6 +842,10 @@ class WebViewClient extends JavaObject {
/// indicates whether the [WebView] loaded the URL.
final void Function(WebView webView, String url)? urlLoading;

/// Notify the host application to update its visited links database.
final void Function(WebView webView, String url, bool isReload)?
doUpdateVisitedHistory;

/// Sets the required synchronous return value for the Java method,
/// `WebViewClient.shouldOverrideUrlLoading(...)`.
///
Expand Down Expand Up @@ -867,6 +873,7 @@ class WebViewClient extends JavaObject {
onReceivedError: onReceivedError,
requestLoading: requestLoading,
urlLoading: urlLoading,
doUpdateVisitedHistory: doUpdateVisitedHistory,
binaryMessenger: _api.binaryMessenger,
instanceManager: _api.instanceManager,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Autogenerated from Pigeon (v9.0.5), do not edit directly.
// Autogenerated from Pigeon (v9.2.3), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import

Expand Down Expand Up @@ -1514,6 +1514,9 @@ abstract class WebViewClientFlutterApi {

void urlLoading(int instanceId, int webViewInstanceId, String url);

void doUpdateVisitedHistory(
int instanceId, int webViewInstanceId, String url, bool isReload);

static void setup(WebViewClientFlutterApi? api,
{BinaryMessenger? binaryMessenger}) {
{
Expand Down Expand Up @@ -1682,6 +1685,36 @@ abstract class WebViewClientFlutterApi {
});
}
}
{
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.WebViewClientFlutterApi.doUpdateVisitedHistory',
codec,
binaryMessenger: binaryMessenger);
if (api == null) {
channel.setMessageHandler(null);
} else {
channel.setMessageHandler((Object? message) async {
assert(message != null,
'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.doUpdateVisitedHistory was null.');
final List<Object?> args = (message as List<Object?>?)!;
final int? arg_instanceId = (args[0] as int?);
assert(arg_instanceId != null,
'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.doUpdateVisitedHistory was null, expected non-null int.');
final int? arg_webViewInstanceId = (args[1] as int?);
assert(arg_webViewInstanceId != null,
'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.doUpdateVisitedHistory was null, expected non-null int.');
final String? arg_url = (args[2] as String?);
assert(arg_url != null,
'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.doUpdateVisitedHistory was null, expected non-null String.');
final bool? arg_isReload = (args[3] as bool?);
assert(arg_isReload != null,
'Argument for dev.flutter.pigeon.WebViewClientFlutterApi.doUpdateVisitedHistory was null, expected non-null bool.');
api.doUpdateVisitedHistory(
arg_instanceId!, arg_webViewInstanceId!, arg_url!, arg_isReload!);
return;
});
}
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -754,6 +754,30 @@ class WebViewClientFlutterApiImpl extends WebViewClientFlutterApi {
instance.urlLoading!(webViewInstance!, url);
}
}

@override
void doUpdateVisitedHistory(
int instanceId,
int webViewInstanceId,
String url,
bool isReload,
) {
final WebViewClient? instance = instanceManager
.getInstanceWithWeakReference(instanceId) as WebViewClient?;
final WebView? webViewInstance = instanceManager
.getInstanceWithWeakReference(webViewInstanceId) as WebView?;
assert(
instance != null,
'InstanceManager does not contain an WebViewClient with instanceId: $instanceId',
);
assert(
webViewInstance != null,
'InstanceManager does not contain an WebView with instanceId: $webViewInstanceId',
);
if (instance!.doUpdateVisitedHistory != null) {
instance.doUpdateVisitedHistory!(webViewInstance!, url, isReload);
}
}
}

/// Host api implementation for [DownloadListener].
Expand Down
Loading