Skip to content

Commit d3c96c6

Browse files
authored
Wait until all scripts are loaded in the page before running main for the DDC library bundle format (#162707)
flutter/flutter#162567 - Uses the `bootstrapScript` field in `loadConfig` to run a script after all scripts have loaded. - This script just calls a callback that is set up beforehand and calls main. - Modifies the callback that calls `dartDevEmbedder.runMain` to wait until both DWDS called main and all scripts have loaded. - Unskips hot reload tests now that the race condition should no longer exist. ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing.
1 parent 44785da commit d3c96c6

10 files changed

+52
-36
lines changed

packages/flutter_tools/lib/src/isolated/devfs_web.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,13 +1087,18 @@ class WebDevFS implements DevFS {
10871087
generateLoadingIndicator: enableDwds,
10881088
),
10891089
);
1090+
const String onLoadEndBootstrap = 'on_load_end_bootstrap.js';
1091+
if (ddcModuleSystem) {
1092+
webAssetServer.writeFile(onLoadEndBootstrap, generateDDCLibraryBundleOnLoadEndBootstrap());
1093+
}
10901094
webAssetServer.writeFile(
10911095
'main_module.bootstrap.js',
10921096
ddcModuleSystem
10931097
? generateDDCLibraryBundleMainModule(
10941098
entrypoint: entrypoint,
10951099
nullAssertions: nullAssertions,
10961100
nativeNullAssertions: nativeNullAssertions,
1101+
onLoadEndBootstrap: onLoadEndBootstrap,
10971102
)
10981103
: generateMainModule(
10991104
entrypoint: entrypoint,

packages/flutter_tools/lib/src/web/bootstrap.dart

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -505,10 +505,13 @@ String generateDDCMainModule({
505505
''';
506506
}
507507

508+
const String _onLoadEndCallback = r'$onLoadEndCallback';
509+
508510
String generateDDCLibraryBundleMainModule({
509511
required String entrypoint,
510512
required bool nullAssertions,
511513
required bool nativeNullAssertions,
514+
required String onLoadEndBootstrap,
512515
}) {
513516
// The typo below in "EXTENTION" is load-bearing, package:build depends on it.
514517
return '''
@@ -519,21 +522,48 @@ String generateDDCLibraryBundleMainModule({
519522
520523
dartDevEmbedder.debugger.registerDevtoolsFormatter();
521524
522-
let child = {};
523-
child.main = function() {
524-
let sdkOptions = {
525-
nonNullAsserts: $nullAssertions,
526-
nativeNonNullAsserts: $nativeNullAssertions,
527-
};
528-
dartDevEmbedder.runMain(appName, sdkOptions);
525+
// Set up a final script that lets us know when all scripts have been loaded.
526+
let onLoadEndSrc = '$onLoadEndBootstrap';
527+
window.\$dartLoader.loadConfig.bootstrapScript = {
528+
src: onLoadEndSrc,
529+
id: onLoadEndSrc,
530+
};
531+
window.\$dartLoader.loadConfig.tryLoadBootstrapScript = true;
532+
let dwdsCalledMain = false;
533+
let dartSrcsLoaded = false;
534+
let runMainWhenBoth = function() {
535+
// Only run once both all the scripts are loaded and DWDS triggers main.
536+
if (dwdsCalledMain && dartSrcsLoaded) {
537+
let sdkOptions = {
538+
nonNullAsserts: $nullAssertions,
539+
nativeNonNullAsserts: $nativeNullAssertions,
540+
};
541+
dartDevEmbedder.runMain(appName, sdkOptions);
542+
}
543+
}
544+
// DWDS expects the main function to be lowercase.
545+
// TODO(srujzs): DWDS should be more robust to not have to require that.
546+
dwdsmain = function() {
547+
dwdsCalledMain = true;
548+
runMainWhenBoth();
549+
}
550+
// Should be called by $onLoadEndBootstrap once all the scripts have been
551+
// loaded.
552+
window.$_onLoadEndCallback = function() {
553+
dartSrcsLoaded = true;
554+
runMainWhenBoth();
529555
}
530556
531557
/* MAIN_EXTENSION_MARKER */
532-
child.main();
558+
dwdsmain();
533559
})();
534560
''';
535561
}
536562

563+
String generateDDCLibraryBundleOnLoadEndBootstrap() {
564+
return '''window.$_onLoadEndCallback();''';
565+
}
566+
537567
/// Generate a synthetic main module which captures the application's main
538568
/// method.
539569
///

packages/flutter_tools/test/general.shard/web/bootstrap_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,7 @@ void main() {
273273
entrypoint: 'main.js',
274274
nullAssertions: false,
275275
nativeNullAssertions: false,
276+
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
276277
);
277278
// bootstrap main module has correct defined module.
278279
expect(result, contains('let appName = "org-dartlang-app:/main.js";'));
@@ -284,6 +285,7 @@ void main() {
284285
entrypoint: 'main.js',
285286
nullAssertions: true,
286287
nativeNullAssertions: true,
288+
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
287289
);
288290

289291
expect(result, contains('nonNullAsserts: true'));
@@ -295,6 +297,7 @@ void main() {
295297
entrypoint: 'main.js',
296298
nullAssertions: false,
297299
nativeNullAssertions: false,
300+
onLoadEndBootstrap: 'on_load_end_bootstrap.js',
298301
);
299302

300303
expect(result, contains('nonNullAsserts: false'));

packages/flutter_tools/test/integration.shard/test_data/hot_reload_test_common.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ import '../test_driver.dart';
1212
import '../test_utils.dart';
1313
import 'hot_reload_project.dart';
1414

15-
void testAll({
16-
bool chrome = false,
17-
List<String> additionalCommandArgs = const <String>[],
18-
Object? skip = false,
19-
}) {
15+
void testAll({bool chrome = false, List<String> additionalCommandArgs = const <String>[]}) {
2016
group('chrome: $chrome'
2117
'${additionalCommandArgs.isEmpty ? '' : ' with args: $additionalCommandArgs'}', () {
2218
late Directory tempDir;
@@ -235,7 +231,7 @@ void testAll({
235231
// isolates, so this test will wait forever.
236232
skip: chrome,
237233
);
238-
}, skip: skip);
234+
});
239235
}
240236

241237
bool _isHotReloadCompletionEvent(Map<String, Object?>? event) {

packages/flutter_tools/test/integration.shard/test_data/hot_reload_with_asset_test_common.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,7 @@ import '../test_driver.dart';
1111
import '../test_utils.dart';
1212
import 'hot_reload_with_asset.dart';
1313

14-
void testAll({
15-
bool chrome = false,
16-
List<String> additionalCommandArgs = const <String>[],
17-
Object? skip = false,
18-
}) {
14+
void testAll({bool chrome = false, List<String> additionalCommandArgs = const <String>[]}) {
1915
group('chrome: $chrome'
2016
'${additionalCommandArgs.isEmpty ? '' : ' with args: $additionalCommandArgs'}', () {
2117
late Directory tempDir;
@@ -86,5 +82,5 @@ void testAll({
8682
await flutter.hotRestart();
8783
await onSecondLoad.future;
8884
});
89-
}, skip: skip);
85+
});
9086
}

packages/flutter_tools/test/integration.shard/test_data/stateless_stateful_hot_reload_test_common.dart

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,7 @@ import '../test_utils.dart';
1313

1414
// This test verifies that we can hot reload a stateless widget into a
1515
// stateful one and back.
16-
void testAll({
17-
bool chrome = false,
18-
List<String> additionalCommandArgs = const <String>[],
19-
Object? skip = false,
20-
}) {
16+
void testAll({bool chrome = false, List<String> additionalCommandArgs = const <String>[]}) {
2117
group('chrome: $chrome'
2218
'${additionalCommandArgs.isEmpty ? '' : ' with args: $additionalCommandArgs'}', () {
2319
late Directory tempDir;
@@ -60,5 +56,5 @@ void testAll({
6056
expect(logs, contains('STATEFUL'));
6157
await subscription.cancel();
6258
});
63-
}, skip: skip);
59+
});
6460
}

packages/flutter_tools/test/web.shard/hot_reload_web_errors_test.dart

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
@Tags(<String>['flutter-test-driver'])
66
library;
77

8-
import 'dart:io';
9-
108
import '../integration.shard/test_data/hot_reload_errors_common.dart';
119
import '../src/common.dart';
1210

@@ -19,7 +17,5 @@ void main() {
1917
// TODO(srujzs): Remove this custom message once we have the delta inspector emitting the same
2018
// string as the VM.
2119
constClassFieldRemovalErrorMessage: 'Const class cannot remove fields',
22-
// https://github.com/flutter/flutter/issues/162567
23-
skip: Platform.isWindows,
2420
);
2521
}

packages/flutter_tools/test/web.shard/hot_reload_web_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,5 @@ void main() {
1414
additionalCommandArgs: <String>[
1515
'--extra-front-end-options=--dartdevc-canary,--dartdevc-module-format=ddc',
1616
],
17-
// https://github.com/flutter/flutter/issues/162567
18-
skip: true,
1917
);
2018
}

packages/flutter_tools/test/web.shard/hot_reload_with_asset_web_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,5 @@ void main() {
1414
additionalCommandArgs: <String>[
1515
'--extra-front-end-options=--dartdevc-canary,--dartdevc-module-format=ddc',
1616
],
17-
// https://github.com/flutter/flutter/issues/162567
18-
skip: true,
1917
);
2018
}

packages/flutter_tools/test/web.shard/stateless_stateful_hot_reload_web_test.dart

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,5 @@ void main() {
1414
additionalCommandArgs: <String>[
1515
'--extra-front-end-options=--dartdevc-canary,--dartdevc-module-format=ddc',
1616
],
17-
// https://github.com/flutter/flutter/issues/162567
18-
skip: true,
1917
);
2018
}

0 commit comments

Comments
 (0)