[web] JSConfig: Add multiViewEnabled value. (#47939)

This change:

* Adds a boolean to `multiViewEnabled`.
* Removes unused `canvasKitMaximumSurfaces` value.

Part of: https://github.com/flutter/flutter/issues/137377

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
diff --git a/lib/web_ui/lib/src/engine/configuration.dart b/lib/web_ui/lib/src/engine/configuration.dart
index d875686..e47f8ed 100644
--- a/lib/web_ui/lib/src/engine/configuration.dart
+++ b/lib/web_ui/lib/src/engine/configuration.dart
@@ -267,17 +267,6 @@
     'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY',
   );
 
-  /// This is deprecated. The CanvasKit renderer will only ever create one
-  /// WebGL context, obviating the problem this configuration was meant to
-  /// solve originally.
-  @Deprecated('Setting canvasKitMaximumSurfaces has no effect')
-  int get canvasKitMaximumSurfaces =>
-      _configuration?.canvasKitMaximumSurfaces?.toInt() ?? _defaultCanvasKitMaximumSurfaces;
-  static const int _defaultCanvasKitMaximumSurfaces = int.fromEnvironment(
-    'FLUTTER_WEB_MAXIMUM_SURFACES',
-    defaultValue: 8,
-  );
-
   /// Set this flag to `true` to cause the engine to visualize the semantics tree
   /// on the screen for debugging.
   ///
@@ -298,6 +287,16 @@
   /// to render, or `null` if the user hasn't specified anything.
   DomElement? get hostElement => _configuration?.hostElement;
 
+  /// Sets Flutter Web in "multi-view" mode.
+  ///
+  /// Multi-view mode allows apps to:
+  ///
+  ///  * Start without a `hostElement`.
+  ///  * Add/remove views (`hostElements`) from JS while the application is running.
+  ///  * ...
+  ///  * PROFIT?
+  bool get multiViewEnabled => _configuration?.multiViewEnabled ?? false;
+
   /// Returns a `nonce` to allowlist the inline styles that Flutter web needs.
   ///
   /// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/nonce
@@ -347,16 +346,16 @@
   external JSBoolean? get _canvasKitForceCpuOnly;
   bool? get canvasKitForceCpuOnly => _canvasKitForceCpuOnly?.toDart;
 
-  @JS('canvasKitMaximumSurfaces')
-  external JSNumber? get _canvasKitMaximumSurfaces;
-  double? get canvasKitMaximumSurfaces => _canvasKitMaximumSurfaces?.toDartDouble;
-
   @JS('debugShowSemanticsNodes')
   external JSBoolean? get _debugShowSemanticsNodes;
   bool? get debugShowSemanticsNodes => _debugShowSemanticsNodes?.toDart;
 
   external DomElement? get hostElement;
 
+  @JS('multiViewEnabled')
+  external JSBoolean? get _multiViewEnabled;
+  bool? get multiViewEnabled => _multiViewEnabled?.toDart;
+
   @JS('nonce')
   external JSString? get _nonce;
   String? get nonce => _nonce?.toDart;
diff --git a/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart b/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart
index b7869d9..e708471 100644
--- a/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart
+++ b/lib/web_ui/test/canvaskit/initialization/stores_config_test.dart
@@ -15,12 +15,23 @@
   group('initializeEngineServices', () {
     test('stores user configuration', () async {
       final JsFlutterConfiguration config = JsFlutterConfiguration();
-      js_util.setProperty(config, 'canvasKitMaximumSurfaces', 32.0);
+      // `canvasKitBaseUrl` is required for the test to actually run.
       js_util.setProperty(config, 'canvasKitBaseUrl', '/canvaskit/');
+      // A property under test, that we'll try to read later.
+      js_util.setProperty(config, 'nonce', 'some_nonce');
+      // A non-existing property to verify our js-interop doesn't crash.
+      js_util.setProperty(config, 'canvasKitMaximumSurfaces', 32.0);
+
+      // Remove window.flutterConfiguration (if it's there)
       js_util.setProperty(domWindow, 'flutterConfiguration', null);
+
+      // TODO(web): Replace the above nullification by the following assertion
+      // when wasm and JS tests initialize their config the same way:
+      // assert(js_util.getProperty<Object?>(domWindow, 'flutterConfiguration') == null);
+
       await initializeEngineServices(jsConfiguration: config);
 
-      expect(configuration.canvasKitMaximumSurfaces, 32);
+      expect(configuration.nonce, 'some_nonce');
     });
   });
 }
diff --git a/lib/web_ui/test/engine/configuration_test.dart b/lib/web_ui/test/engine/configuration_test.dart
index 7dc5aae..60eca6c 100644
--- a/lib/web_ui/test/engine/configuration_test.dart
+++ b/lib/web_ui/test/engine/configuration_test.dart
@@ -22,16 +22,16 @@
     test('initializes with null', () async {
       final FlutterConfiguration config = FlutterConfiguration.legacy(null);
 
-      expect(config.canvasKitMaximumSurfaces, 8); // _defaultCanvasKitMaximumSurfaces
+      expect(config.canvasKitBaseUrl, 'canvaskit/'); // _defaultCanvasKitBaseUrl
     });
 
     test('legacy constructor initializes with a Js Object', () async {
       final FlutterConfiguration config = FlutterConfiguration.legacy(
         js_util.jsify(<String, Object?>{
-          'canvasKitMaximumSurfaces': 16,
+          'canvasKitBaseUrl': 'some_other_url/',
         }) as JsFlutterConfiguration);
 
-      expect(config.canvasKitMaximumSurfaces, 16);
+      expect(config.canvasKitBaseUrl, 'some_other_url/');
     });
   });
 
@@ -39,13 +39,13 @@
     test('throws assertion error if already initialized from JS', () async {
       final FlutterConfiguration config = FlutterConfiguration.legacy(
         js_util.jsify(<String, Object?>{
-          'canvasKitMaximumSurfaces': 12,
+          'canvasKitBaseUrl': 'some_other_url/',
         }) as JsFlutterConfiguration);
 
       expect(() {
         config.setUserConfiguration(
           js_util.jsify(<String, Object?>{
-            'canvasKitMaximumSurfaces': 16,
+            'canvasKitBaseUrl': 'yet_another_url/',
           }) as JsFlutterConfiguration);
       }, throwsAssertionError);
     });
@@ -55,70 +55,98 @@
 
       config.setUserConfiguration(
         js_util.jsify(<String, Object?>{
-          'canvasKitMaximumSurfaces': 16,
+          'canvasKitBaseUrl': 'one_more_url/',
         }) as JsFlutterConfiguration);
 
-      expect(config.canvasKitMaximumSurfaces, 16);
+      expect(config.canvasKitBaseUrl, 'one_more_url/');
+    });
+
+    test('can receive non-existing properties without crashing', () async {
+      final FlutterConfiguration config = FlutterConfiguration.legacy(null);
+
+      expect(() {
+        config.setUserConfiguration(
+          js_util.jsify(<String, Object?>{
+            'canvasKitMaximumSurfaces': 32.0,
+          }) as JsFlutterConfiguration);
+      }, returnsNormally);
     });
   });
 
-  group('CanvasKit config', () {
-    test('default canvasKitVariant', () {
-      final FlutterConfiguration config = FlutterConfiguration();
-
-      expect(config.canvasKitVariant, CanvasKitVariant.auto);
-    });
-
-    test('default canvasKitVariant when it is undefined', () {
-      final FlutterConfiguration config = FlutterConfiguration();
-      config.setUserConfiguration(
-        // With an empty map, the canvasKitVariant is undefined in JS.
+  group('Default configuration values', () {
+    late FlutterConfiguration defaultConfig;
+    setUp(() {
+      defaultConfig = FlutterConfiguration();
+      defaultConfig.setUserConfiguration(
         js_util.jsify(<String, Object?>{}) as JsFlutterConfiguration,
       );
-
-      expect(config.canvasKitVariant, CanvasKitVariant.auto);
     });
 
-    test('validates canvasKitVariant', () {
-      final FlutterConfiguration config = FlutterConfiguration();
-
-      config.setUserConfiguration(
-        js_util.jsify(<String, Object?>{'canvasKitVariant': 'foo'}) as JsFlutterConfiguration,
-      );
-      expect(() => config.canvasKitVariant, throwsArgumentError);
-
-      config.setUserConfiguration(
-        js_util.jsify(<String, Object?>{'canvasKitVariant': 'auto'}) as JsFlutterConfiguration,
-      );
-      expect(config.canvasKitVariant, CanvasKitVariant.auto);
-
-      config.setUserConfiguration(
-        js_util.jsify(<String, Object?>{'canvasKitVariant': 'full'}) as JsFlutterConfiguration,
-      );
-      expect(config.canvasKitVariant, CanvasKitVariant.full);
-
-      config.setUserConfiguration(
-        js_util.jsify(<String, Object?>{'canvasKitVariant': 'chromium'}) as JsFlutterConfiguration,
-      );
-      expect(config.canvasKitVariant, CanvasKitVariant.chromium);
+    test('canvasKitVariant', () {
+      expect(defaultConfig.canvasKitVariant, CanvasKitVariant.auto);
     });
+
+    test('useColorEmoji', () {
+      expect(defaultConfig.useColorEmoji, isFalse);
+    });
+
+    test('multiViewEnabled', () {
+      expect(defaultConfig.multiViewEnabled, isFalse);
+    });
+
   });
 
-  group('useColorEmoji', () {
-    test('defaults to false', () {
-      final FlutterConfiguration config = FlutterConfiguration();
-      config.setUserConfiguration(
-        js_util.jsify(<String, Object?>{}) as JsFlutterConfiguration,
-      );
-      expect(config.useColorEmoji, isFalse);
+  group('setUserConfiguration (values)', () {
+    group('canvasKitVariant', () {
+      test('value undefined - defaults to "auto"', () {
+        final FlutterConfiguration config = FlutterConfiguration();
+        config.setUserConfiguration(
+          // With an empty map, the canvasKitVariant is undefined in JS.
+          js_util.jsify(<String, Object?>{}) as JsFlutterConfiguration,
+        );
+
+        expect(config.canvasKitVariant, CanvasKitVariant.auto);
+      });
+
+      test('value - converts to CanvasKitVariant enum (or throw)', () {
+        final FlutterConfiguration config = FlutterConfiguration();
+
+        config.setUserConfiguration(
+          js_util.jsify(<String, Object?>{'canvasKitVariant': 'foo'}) as JsFlutterConfiguration,
+        );
+        expect(() => config.canvasKitVariant, throwsArgumentError);
+
+        config.setUserConfiguration(
+          js_util.jsify(<String, Object?>{'canvasKitVariant': 'auto'}) as JsFlutterConfiguration,
+        );
+        expect(config.canvasKitVariant, CanvasKitVariant.auto);
+
+        config.setUserConfiguration(
+          js_util.jsify(<String, Object?>{'canvasKitVariant': 'full'}) as JsFlutterConfiguration,
+        );
+        expect(config.canvasKitVariant, CanvasKitVariant.full);
+
+        config.setUserConfiguration(
+          js_util.jsify(<String, Object?>{'canvasKitVariant': 'chromium'}) as JsFlutterConfiguration,
+        );
+        expect(config.canvasKitVariant, CanvasKitVariant.chromium);
+      });
     });
 
-    test('can be set to true', () {
+    test('useColorEmoji', () {
       final FlutterConfiguration config = FlutterConfiguration();
       config.setUserConfiguration(
         js_util.jsify(<String, Object?>{'useColorEmoji': true}) as JsFlutterConfiguration,
       );
       expect(config.useColorEmoji, isTrue);
     });
+
+    test('multiViewEnabled', () {
+      final FlutterConfiguration config = FlutterConfiguration();
+      config.setUserConfiguration(
+        js_util.jsify(<String, Object?>{'multiViewEnabled': true}) as JsFlutterConfiguration,
+      );
+      expect(config.multiViewEnabled, isTrue);
+    });
   });
 }