Skip to content

Commit 3e09393

Browse files
authored
Fixes handleNavigationMessage in order (flutter#26596)
1 parent 38ec57c commit 3e09393

File tree

2 files changed

+96
-25
lines changed

2 files changed

+96
-25
lines changed

lib/web_ui/lib/src/engine/window.dart

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
part of engine;
66

7+
typedef _HandleMessageCallBack = Future<bool> Function();
8+
79
/// When set to true, all platform messages will be printed to the console.
810
const bool /*!*/ _debugPrintPlatformMessages = false;
911

@@ -112,32 +114,50 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow {
112114
_customUrlStrategy = null;
113115
}
114116

115-
Future<bool> handleNavigationMessage(ByteData? data) async {
116-
final MethodCall decoded = JSONMethodCodec().decodeMethodCall(data);
117-
final Map<String, dynamic>? arguments = decoded.arguments;
118-
switch (decoded.method) {
119-
case 'selectMultiEntryHistory':
120-
await _useMultiEntryBrowserHistory();
121-
return true;
122-
case 'selectSingleEntryHistory':
123-
await _useSingleEntryBrowserHistory();
124-
return true;
125-
// the following cases assert that arguments are not null
126-
case 'routeUpdated': // deprecated
127-
assert(arguments != null);
128-
await _useSingleEntryBrowserHistory();
129-
browserHistory.setRouteName(arguments!['routeName']);
130-
return true;
131-
case 'routeInformationUpdated':
132-
assert(arguments != null);
133-
browserHistory.setRouteName(
134-
arguments!['location'],
135-
state: arguments['state'],
136-
replace: arguments['replace'] ?? false,
137-
);
138-
return true;
117+
Future<void> _endOfTheLine = Future<void>.value(null);
118+
119+
Future<bool> _waitInTheLine(_HandleMessageCallBack callback) async {
120+
final Future<void> currentPosition = _endOfTheLine;
121+
final Completer<void> completer = Completer<void>();
122+
_endOfTheLine = completer.future;
123+
await currentPosition;
124+
bool result = false;
125+
try {
126+
result = await callback();
127+
} finally {
128+
completer.complete();
139129
}
140-
return false;
130+
return result;
131+
}
132+
133+
Future<bool> handleNavigationMessage(ByteData? data) async {
134+
return _waitInTheLine(() async {
135+
final MethodCall decoded = JSONMethodCodec().decodeMethodCall(data);
136+
final Map<String, dynamic>? arguments = decoded.arguments;
137+
switch (decoded.method) {
138+
case 'selectMultiEntryHistory':
139+
await _useMultiEntryBrowserHistory();
140+
return true;
141+
case 'selectSingleEntryHistory':
142+
await _useSingleEntryBrowserHistory();
143+
return true;
144+
// the following cases assert that arguments are not null
145+
case 'routeUpdated': // deprecated
146+
assert(arguments != null);
147+
await _useSingleEntryBrowserHistory();
148+
browserHistory.setRouteName(arguments!['routeName']);
149+
return true;
150+
case 'routeInformationUpdated':
151+
assert(arguments != null);
152+
browserHistory.setRouteName(
153+
arguments!['location'],
154+
state: arguments['state'],
155+
replace: arguments['replace'] ?? false,
156+
);
157+
return true;
158+
}
159+
return false;
160+
});
141161
}
142162

143163
@override

lib/web_ui/test/window_test.dart

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,57 @@ void testMain() {
133133
}, throwsAssertionError);
134134
});
135135

136+
test('handleNavigationMessage execute request in order.', () async {
137+
// Start with multi entries.
138+
await window.debugInitializeHistory(TestUrlStrategy.fromEntry(
139+
TestHistoryEntry('initial state', null, '/initial'),
140+
), useSingle: false);
141+
expect(window.browserHistory, isA<MultiEntriesBrowserHistory>());
142+
final List<String> executionOrder = <String>[];
143+
window.handleNavigationMessage(
144+
JSONMethodCodec().encodeMethodCall(MethodCall(
145+
'selectSingleEntryHistory',
146+
null,
147+
))
148+
).then<void>((bool data) {
149+
executionOrder.add('1');
150+
});
151+
window.handleNavigationMessage(
152+
JSONMethodCodec().encodeMethodCall(MethodCall(
153+
'selectMultiEntryHistory',
154+
null,
155+
))
156+
).then<void>((bool data) {
157+
executionOrder.add('2');
158+
});
159+
window.handleNavigationMessage(
160+
JSONMethodCodec().encodeMethodCall(MethodCall(
161+
'selectSingleEntryHistory',
162+
null,
163+
))
164+
).then<void>((bool data) {
165+
executionOrder.add('3');
166+
});
167+
await window.handleNavigationMessage(
168+
JSONMethodCodec().encodeMethodCall(MethodCall(
169+
'routeInformationUpdated',
170+
<String, dynamic>{
171+
'location': '/baz',
172+
'state': null,
173+
}, // boom
174+
))
175+
).then<void>((bool data) {
176+
executionOrder.add('4');
177+
});
178+
// The routeInformationUpdated should finish after the browser history
179+
// has been set to single entry.
180+
expect(executionOrder.length, 4);
181+
expect(executionOrder[0], '1');
182+
expect(executionOrder[1], '2');
183+
expect(executionOrder[2], '3');
184+
expect(executionOrder[3], '4');
185+
});
186+
136187
test('should not throw when using nav1 and nav2 together',
137188
() async {
138189
await window.debugInitializeHistory(TestUrlStrategy.fromEntry(

0 commit comments

Comments
 (0)