Allow skipping webOnlyInitializePlatform in Flutter for Web (#40301)

diff --git a/packages/flutter_tools/lib/src/build_runner/build_script.dart b/packages/flutter_tools/lib/src/build_runner/build_script.dart
index 679cafa..48bb8f2 100644
--- a/packages/flutter_tools/lib/src/build_runner/build_script.dart
+++ b/packages/flutter_tools/lib/src/build_runner/build_script.dart
@@ -101,7 +101,11 @@
     <BuilderFactory>[
       (BuilderOptions options) {
         final bool hasPlugins = options.config['hasPlugins'] == true;
-        return FlutterWebShellBuilder(hasPlugins: hasPlugins);
+        final bool initializePlatform = options.config['initializePlatform'] == true;
+        return FlutterWebShellBuilder(
+          hasPlugins: hasPlugins,
+          initializePlatform: initializePlatform,
+        );
       }
     ],
     core.toRoot(),
@@ -360,10 +364,13 @@
 
 /// A shell builder which generates the web specific entrypoint.
 class FlutterWebShellBuilder implements Builder {
-  const FlutterWebShellBuilder({this.hasPlugins = false});
+  const FlutterWebShellBuilder({this.hasPlugins = false, this.initializePlatform = true});
 
   final bool hasPlugins;
 
+  /// Whether to call webOnlyInitializePlatform.
+  final bool initializePlatform;
+
   @override
   Future<void> build(BuildStep buildStep) async {
     final AssetId dartEntrypointId = buildStep.inputId;
@@ -383,7 +390,9 @@
 
 Future<void> main() async {
   registerPlugins(webPluginRegistry);
-  await ui.webOnlyInitializePlatform();
+  if ($initializePlatform) {
+    await ui.webOnlyInitializePlatform();
+  }
   entrypoint.main();
 }
 ''');
@@ -394,7 +403,9 @@
 import "${path.url.basename(buildStep.inputId.path)}" as entrypoint;
 
 Future<void> main() async {
-  await ui.webOnlyInitializePlatform();
+  if ($initializePlatform) {
+    await ui.webOnlyInitializePlatform();
+  }
   entrypoint.main();
 }
 ''');
diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
index 6943aeb..a5d5ee8 100644
--- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
@@ -168,6 +168,7 @@
         target: target,
         flutterProject: flutterProject,
         buildInfo: debuggingOptions.buildInfo,
+        initializePlatform: debuggingOptions.initializePlatform,
         hostname: debuggingOptions.hostname,
         port: debuggingOptions.port,
         skipDwds: device is WebServerDevice,
diff --git a/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart b/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
index 4425a72..4d428ca 100644
--- a/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
+++ b/packages/flutter_tools/lib/src/build_runner/web_compilation_delegate.dart
@@ -16,6 +16,9 @@
 import '../base/file_system.dart';
 import '../build_info.dart';
 import '../convert.dart';
+import '../platform_plugins.dart';
+import '../plugins.dart';
+import '../project.dart';
 import '../web/compile.dart';
 import 'web_fs.dart';
 
@@ -28,18 +31,23 @@
     Directory projectDirectory,
     String testOutputDir,
     BuildMode mode,
-    String projectName
+    String projectName,
+    bool initializePlatform,
   }) async {
     // Create the .dart_tool directory if it doesn't exist.
     projectDirectory
       .childDirectory('.dart_tool')
       .createSync();
+    final FlutterProject flutterProject = FlutterProject.fromDirectory(projectDirectory);
+    final bool hasWebPlugins = findPlugins(flutterProject)
+        .any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
     final BuildDaemonClient client = await buildDaemonCreator.startBuildDaemon(
       projectDirectory.path,
       release: mode == BuildMode.release,
       profile: mode == BuildMode.profile,
-      hasPlugins: false,
+      hasPlugins: hasWebPlugins,
       includeTests: true,
+      initializePlatform: initializePlatform,
     );
     client.startBuild();
     bool success = true;
diff --git a/packages/flutter_tools/lib/src/build_runner/web_fs.dart b/packages/flutter_tools/lib/src/build_runner/web_fs.dart
index 9e2dd70..e7ce547 100644
--- a/packages/flutter_tools/lib/src/build_runner/web_fs.dart
+++ b/packages/flutter_tools/lib/src/build_runner/web_fs.dart
@@ -75,6 +75,7 @@
   @required FlutterProject flutterProject,
   @required BuildInfo buildInfo,
   @required bool skipDwds,
+  @required bool initializePlatform,
   @required String hostname,
   @required String port,
 });
@@ -142,6 +143,7 @@
     @required FlutterProject flutterProject,
     @required BuildInfo buildInfo,
     @required bool skipDwds,
+    @required bool initializePlatform,
     @required String hostname,
     @required String port,
   }) async {
@@ -149,11 +151,16 @@
     if (!flutterProject.dartTool.existsSync()) {
       flutterProject.dartTool.createSync(recursive: true);
     }
-
-    final bool hasWebPlugins = findPlugins(flutterProject).any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
+    final bool hasWebPlugins = findPlugins(flutterProject)
+        .any((Plugin p) => p.platforms.containsKey(WebPlugin.kConfigKey));
     // Start the build daemon and run an initial build.
     final BuildDaemonClient client = await buildDaemonCreator
-      .startBuildDaemon(fs.currentDirectory.path, release: buildInfo.isRelease, profile: buildInfo.isProfile, hasPlugins: hasWebPlugins);
+      .startBuildDaemon(fs.currentDirectory.path,
+          release: buildInfo.isRelease,
+          profile: buildInfo.isProfile,
+          hasPlugins: hasWebPlugins,
+          initializePlatform: initializePlatform,
+      );
     client.startBuild();
     // Only provide relevant build results
     final Stream<BuildResult> filteredBuildResults = client.buildResults
@@ -340,11 +347,14 @@
   static const String _ignoredLine3 = 'have your dependencies specified fully in your pubspec.yaml';
 
   /// Start a build daemon and register the web targets.
+  ///
+  /// [initializePlatform] controls whether we should invoke [webOnlyInitializePlatform].
   Future<BuildDaemonClient> startBuildDaemon(String workingDirectory, {
     bool release = false,
     bool profile = false,
     bool hasPlugins = false,
     bool includeTests = false,
+    bool initializePlatform = true,
   }) async {
     try {
       final BuildDaemonClient client = await _connectClient(
@@ -352,6 +362,7 @@
         release: release,
         profile: profile,
         hasPlugins: hasPlugins,
+        initializePlatform: initializePlatform,
       );
       _registerBuildTargets(client, includeTests);
       return client;
@@ -384,7 +395,7 @@
 
   Future<BuildDaemonClient> _connectClient(
     String workingDirectory,
-    { bool release, bool profile, bool hasPlugins }
+    { bool release, bool profile, bool hasPlugins, bool initializePlatform }
   ) {
     final String flutterToolsPackages = fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools', '.packages');
     final String buildScript = fs.path.join(Cache.flutterRoot, 'packages', 'flutter_tools', 'lib', 'src', 'build_runner', 'build_script.dart');
@@ -406,6 +417,7 @@
         '--define', 'flutter_tools:entrypoint=profile=$profile',
         '--define', 'flutter_tools:shell=flutterWebSdk=$flutterWebSdk',
         '--define', 'flutter_tools:shell=hasPlugins=$hasPlugins',
+        '--define', 'flutter_tools:shell=initializePlatform=$initializePlatform'
       ],
       logHandler: (ServerLog serverLog) {
         switch (serverLog.level) {
diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart
index 16ade17..a443d31 100644
--- a/packages/flutter_tools/lib/src/commands/build_web.dart
+++ b/packages/flutter_tools/lib/src/commands/build_web.dart
@@ -18,6 +18,12 @@
     usesTargetOption();
     usesPubOption();
     addBuildModeFlags();
+    argParser.addFlag('web-initialize-platform',
+        defaultsTo: true,
+        negatable: true,
+        hide: true,
+        help: 'Whether to automatically invoke webOnlyInitializePlatform.',
+    );
   }
 
   @override
@@ -44,7 +50,7 @@
     final FlutterProject flutterProject = FlutterProject.current();
     final String target = argResults['target'];
     final BuildInfo buildInfo = getBuildInfo();
-    await buildWeb(flutterProject, target, buildInfo);
+    await buildWeb(flutterProject, target, buildInfo, argResults['web-initialize-platform']);
     return null;
   }
 }
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index 74fce6a..4bcb839 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -169,6 +169,12 @@
         hide: !verboseHelp,
         help: 'No longer require an authentication code to connect to the VM '
               'service (not recommended).')
+      ..addFlag('web-initialize-platform',
+        negatable: true,
+        defaultsTo: true,
+        hide: true,
+        help: 'Whether to automatically invoke webOnlyInitializePlatform.'
+      )
       ..addOption(FlutterOptions.kExtraFrontEndOptions, hide: true)
       ..addOption(FlutterOptions.kExtraGenSnapshotOptions, hide: true)
       ..addMultiOption(FlutterOptions.kEnableExperiment,
@@ -274,7 +280,10 @@
   DebuggingOptions _createDebuggingOptions() {
     final BuildInfo buildInfo = getBuildInfo();
     if (buildInfo.isRelease) {
-      return DebuggingOptions.disabled(buildInfo);
+      return DebuggingOptions.disabled(
+        buildInfo,
+        initializePlatform: argResults['web-initialize-platform'],
+      );
     } else {
       return DebuggingOptions.enabled(
         buildInfo,
@@ -289,6 +298,7 @@
         dumpSkpOnShaderCompilation: argResults['dump-skp-on-shader-compilation'],
         observatoryPort: observatoryPort,
         verboseSystemLogs: argResults['verbose-system-logs'],
+        initializePlatform: argResults['web-initialize-platform'],
         hostname: featureFlags.isWebEnabled ? argResults['web-hostname'] : '',
         port: featureFlags.isWebEnabled ? argResults['web-port'] : '',
       );
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index bcd710e..09f6cdb 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -484,11 +484,12 @@
     this.useTestFonts = false,
     this.verboseSystemLogs = false,
     this.observatoryPort,
+    this.initializePlatform = true,
     this.hostname,
     this.port,
    }) : debuggingEnabled = true;
 
-  DebuggingOptions.disabled(this.buildInfo)
+  DebuggingOptions.disabled(this.buildInfo, { this.initializePlatform = true })
     : debuggingEnabled = false,
       useTestFonts = false,
       startPaused = false,
@@ -517,6 +518,8 @@
   final bool dumpSkpOnShaderCompilation;
   final bool useTestFonts;
   final bool verboseSystemLogs;
+  /// Whether to invoke webOnlyInitializePlatform in Flutter for web.
+  final bool initializePlatform;
   final int observatoryPort;
   final String port;
   final String hostname;
diff --git a/packages/flutter_tools/lib/src/test/runner.dart b/packages/flutter_tools/lib/src/test/runner.dart
index dc83d9b..67aed19 100644
--- a/packages/flutter_tools/lib/src/test/runner.dart
+++ b/packages/flutter_tools/lib/src/test/runner.dart
@@ -70,6 +70,7 @@
       projectDirectory: flutterProject.directory,
       testOutputDir: tempBuildDir,
       projectName: flutterProject.manifest.appName,
+      initializePlatform: true,
     );
     if (!result) {
       throwToolExit('Failed to compile tests');
diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart
index f749851..e40a37d 100644
--- a/packages/flutter_tools/lib/src/web/compile.dart
+++ b/packages/flutter_tools/lib/src/web/compile.dart
@@ -18,7 +18,7 @@
 /// The [WebCompilationProxy] instance.
 WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();
 
-Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo) async {
+Future<void> buildWeb(FlutterProject flutterProject, String target, BuildInfo buildInfo, bool initializePlatform) async {
   if (!flutterProject.web.existsSync()) {
     throwToolExit('Missing index.html.');
   }
@@ -32,6 +32,7 @@
       projectDirectory: FlutterProject.current().directory,
       mode: buildInfo.mode,
       projectName: flutterProject.manifest.appName,
+      initializePlatform: initializePlatform,
     );
     if (result) {
       // Places assets adjacent to the web stuff.
@@ -84,6 +85,7 @@
     @required String projectName,
     String testOutputDir,
     BuildMode mode,
+    bool initializePlatform,
   }) async {
     throw UnimplementedError();
   }
diff --git a/packages/flutter_tools/test/general.shard/build_runner/build_script_test.dart b/packages/flutter_tools/test/general.shard/build_runner/build_script_test.dart
new file mode 100644
index 0000000..6fc186f
--- /dev/null
+++ b/packages/flutter_tools/test/general.shard/build_runner/build_script_test.dart
@@ -0,0 +1,74 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:build/build.dart';
+import 'package:flutter_tools/src/build_runner/build_script.dart';
+import 'package:mockito/mockito.dart';
+
+import '../../src/common.dart';
+
+void main() {
+  MockBuildStep mockBuildStep;
+  AssetId inputId;
+
+  setUp(() {
+    mockBuildStep = MockBuildStep();
+    inputId = AssetId('hello_world', 'lib/main.dart');
+    when(mockBuildStep.inputId).thenReturn(inputId);
+    when(mockBuildStep.readAsString(any)).thenAnswer((Invocation invocation) async {
+      return 'void main() { }';
+    });
+
+  });
+
+  test('FlutterWebShellBuilder correctly configures platform', () async {
+    const FlutterWebShellBuilder builder = FlutterWebShellBuilder(
+      hasPlugins: false,
+      initializePlatform: true,
+    );
+
+    await builder.build(mockBuildStep);
+
+    verify(mockBuildStep.writeAsString(any, argThat(contains('if (true) '
+        '{\n    await ui.webOnlyInitializePlatform')))).called(1);
+  });
+
+  test('FlutterWebShellBuilder correctly configures does not platform', () async {
+    const FlutterWebShellBuilder builder = FlutterWebShellBuilder(
+      hasPlugins: false,
+      initializePlatform: false,
+    );
+
+    await builder.build(mockBuildStep);
+
+    verify(mockBuildStep.writeAsString(any, argThat(contains('if (false) '
+        '{\n    await ui.webOnlyInitializePlatform')))).called(1);
+  });
+
+  test('FlutterWebShellBuilder correctly configures plugins', () async {
+    const FlutterWebShellBuilder builder = FlutterWebShellBuilder(
+      hasPlugins: true,
+      initializePlatform: true,
+    );
+
+    await builder.build(mockBuildStep);
+
+    verify(mockBuildStep.writeAsString(any,
+        argThat(contains('registerPlugins(webPluginRegistry)')))).called(1);
+  });
+
+  test('FlutterWebShellBuilder correctly does not configure plugins', () async {
+    const FlutterWebShellBuilder builder = FlutterWebShellBuilder(
+      hasPlugins: false,
+      initializePlatform: true,
+    );
+
+    await builder.build(mockBuildStep);
+
+    verify(mockBuildStep.writeAsString(any,
+        argThat(isNot(contains('registerPlugins(webPluginRegistry)'))))).called(1);
+  });
+}
+
+class MockBuildStep extends Mock implements BuildStep {}
diff --git a/packages/flutter_tools/test/general.shard/commands/build_web_test.dart b/packages/flutter_tools/test/general.shard/commands/build_web_test.dart
index dab520e..df62a96 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_web_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_web_test.dart
@@ -43,7 +43,8 @@
       when(mockWebCompilationProxy.initialize(
         projectName: anyNamed('projectName'),
         projectDirectory: anyNamed('projectDirectory'),
-        mode: anyNamed('mode')
+        mode: anyNamed('mode'),
+        initializePlatform: anyNamed('initializePlatform')
       )).thenAnswer((Invocation invocation) {
         final String path = fs.path.join('.dart_tool', 'build', 'flutter_web', 'foo', 'lib', 'main_web_entrypoint.dart.js');
         fs.file(path).createSync(recursive: true);
@@ -65,6 +66,7 @@
       FlutterProject.current(),
       fs.path.join('lib', 'main.dart'),
       BuildInfo.debug,
+      false,
     ), throwsA(isInstanceOf<ToolExit>()));
   }));
 
@@ -86,6 +88,7 @@
       FlutterProject.current(),
       fs.path.join('lib', 'main.dart'),
       BuildInfo.debug,
+      false,
     );
   }));
 
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
index e96f352..561d755 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_cold_test.dart
@@ -43,6 +43,7 @@
         @required FlutterProject flutterProject,
         @required BuildInfo buildInfo,
         @required bool skipDwds,
+        @required bool initializePlatform,
         @required String hostname,
         @required String port,
       }) async {
@@ -102,7 +103,6 @@
 
 }
 
-
 class MockWebDevice extends Mock implements Device {}
 class MockBuildDaemonCreator extends Mock implements BuildDaemonCreator {}
 class MockFlutterWebFs extends Mock implements WebFs {}
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
index 057ed4f..d7d8d91 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
@@ -52,6 +52,7 @@
         @required FlutterProject flutterProject,
         @required BuildInfo buildInfo,
         @required bool skipDwds,
+        @required bool initializePlatform,
         @required String hostname,
         @required String port,
       }) async {
diff --git a/packages/flutter_tools/test/general.shard/web/web_fs_test.dart b/packages/flutter_tools/test/general.shard/web/web_fs_test.dart
index 572da10..aa8de89 100644
--- a/packages/flutter_tools/test/general.shard/web/web_fs_test.dart
+++ b/packages/flutter_tools/test/general.shard/web/web_fs_test.dart
@@ -26,20 +26,23 @@
   MockHttpMultiServer mockHttpMultiServer;
   MockBuildDaemonClient mockBuildDaemonClient;
   MockOperatingSystemUtils mockOperatingSystemUtils;
+  bool lastInitializePlatform;
   dynamic lastAddress;
   int lastPort;
 
   setUp(() {
     lastAddress = null;
     lastPort = null;
+    lastInitializePlatform = null;
     mockBuildDaemonCreator =  MockBuildDaemonCreator();
     mockChromeLauncher = MockChromeLauncher();
     mockHttpMultiServer = MockHttpMultiServer();
     mockBuildDaemonClient = MockBuildDaemonClient();
     mockOperatingSystemUtils = MockOperatingSystemUtils();
     mockDwds = MockDwds();
-    when(mockBuildDaemonCreator.startBuildDaemon(any, release: anyNamed('release')))
-      .thenAnswer((Invocation _) async {
+    when(mockBuildDaemonCreator.startBuildDaemon(any, release: anyNamed('release'), initializePlatform: anyNamed('initializePlatform')))
+      .thenAnswer((Invocation invocation) async {
+        lastInitializePlatform = invocation.namedArguments[#initializePlatform];
         return mockBuildDaemonClient;
       });
     when(mockOperatingSystemUtils.findFreePort()).thenAnswer((Invocation _) async {
@@ -89,6 +92,7 @@
       target: fs.path.join('lib', 'main.dart'),
       buildInfo: BuildInfo.debug,
       flutterProject: flutterProject,
+      initializePlatform: true,
       hostname: null,
       port: null,
     );
@@ -98,6 +102,27 @@
 
     // .dart_tool directory is created.
     expect(flutterProject.dartTool.existsSync(), true);
+    expect(lastInitializePlatform, true);
+  }));
+
+  test('Can create webFs from mocked interfaces with initializePlatform', () => testbed.run(() async {
+    final FlutterProject flutterProject = FlutterProject.current();
+    await WebFs.start(
+      skipDwds: false,
+      target: fs.path.join('lib', 'main.dart'),
+      buildInfo: BuildInfo.debug,
+      flutterProject: flutterProject,
+      initializePlatform: false,
+      hostname: null,
+      port: null,
+    );
+
+    // The build daemon is told to build once.
+    verify(mockBuildDaemonClient.startBuild()).called(1);
+
+    // .dart_tool directory is created.
+    expect(flutterProject.dartTool.existsSync(), true);
+    expect(lastInitializePlatform, false);
   }));
 
   test('Uses provided port number and hostname.', () => testbed.run(() async {
@@ -107,6 +132,7 @@
       target: fs.path.join('lib', 'main.dart'),
       buildInfo: BuildInfo.debug,
       flutterProject: flutterProject,
+      initializePlatform: false,
       hostname: 'foo',
       port: '1234',
     );