Unverified Commit 315ebafe authored by chunhtai's avatar chunhtai Committed by GitHub

PlatformRouteInformationProvider does not push new entry if query par… (#130457)

…ameter is semanticsally the same

The URI compare does not taking into account that query parameter may or may not be encoded, or the parameters' order can be different. However, they are all semantically the same. 

This pr makes PlatformRouteInformationProvider to take those into account when deciding whether it should push/replace the browser history entry.
parent 5870159e
......@@ -5,6 +5,7 @@
import 'dart:async';
import 'dart:collection';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
......@@ -1466,12 +1467,18 @@ class PlatformRouteInformationProvider extends RouteInformationProvider with Wid
required RouteInformation initialRouteInformation,
}) : _value = initialRouteInformation;
static bool _equals(Uri a, Uri b) {
return a.path == b.path
&& a.fragment == b.fragment
&& const DeepCollectionEquality.unordered().equals(a.queryParametersAll, b.queryParametersAll);
}
@override
void routerReportsNewRouteInformation(RouteInformation routeInformation, {RouteInformationReportingType type = RouteInformationReportingType.none}) {
final bool replace =
type == RouteInformationReportingType.neglect ||
(type == RouteInformationReportingType.none &&
_valueInEngine.uri == routeInformation.uri);
_equals(_valueInEngine.uri, routeInformation.uri));
SystemNavigator.selectMultiEntryHistory();
SystemNavigator.routeInformationUpdated(
uri: routeInformation.uri,
......
......@@ -867,6 +867,76 @@ testWidgets('ChildBackButtonDispatcher take priority recursively', (WidgetTester
]);
});
testWidgets('PlatformRouteInformationProvider does not push new entry if query parameters are semantically the same', (WidgetTester tester) async {
final List<MethodCall> log = <MethodCall>[];
TestDefaultBinaryMessengerBinding
.instance.defaultBinaryMessenger
.setMockMethodCallHandler(
SystemChannels.navigation,
(MethodCall methodCall) async {
log.add(methodCall);
return null;
}
);
final RouteInformation initial = RouteInformation(
uri: Uri.parse('initial?a=ws/abcd'),
);
final RouteInformationProvider provider = PlatformRouteInformationProvider(
initialRouteInformation: initial
);
// Make sure engine is updated with initial route
provider.routerReportsNewRouteInformation(initial);
log.clear();
provider.routerReportsNewRouteInformation(
RouteInformation(
uri: Uri(
path: 'initial',
queryParameters: <String, String>{'a': 'ws/abcd'}, // This will be escaped.
),
),
);
expect(provider.value.uri.toString(), 'initial?a=ws%2Fabcd');
// should use `replace: true`
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'initial?a=ws%2Fabcd', 'state': null, 'replace': true }),
]);
log.clear();
provider.routerReportsNewRouteInformation(
RouteInformation(uri: Uri.parse('initial?a=1&b=2')),
);
log.clear();
// Change query parameters order
provider.routerReportsNewRouteInformation(
RouteInformation(uri: Uri.parse('initial?b=2&a=1')),
);
// should use `replace: true`
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'initial?b=2&a=1', 'state': null, 'replace': true }),
]);
log.clear();
provider.routerReportsNewRouteInformation(
RouteInformation(uri: Uri.parse('initial?a=1&a=2')),
);
log.clear();
// Change query parameters order for same key
provider.routerReportsNewRouteInformation(
RouteInformation(uri: Uri.parse('initial?a=2&a=1')),
);
// should use `replace: true`
expect(log, <Object>[
isMethodCall('selectMultiEntryHistory', arguments: null),
isMethodCall('routeInformationUpdated', arguments: <String, dynamic>{ 'uri': 'initial?a=2&a=1', 'state': null, 'replace': true }),
]);
log.clear();
});
testWidgets('RootBackButtonDispatcher works', (WidgetTester tester) async {
final BackButtonDispatcher outerDispatcher = RootBackButtonDispatcher();
final RouteInformationProvider provider = PlatformRouteInformationProvider(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment