blob: e47f8ed82f2a9357ebaf1474efa1c5b445edaff2 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/// JavaScript API a Flutter Web application can use to configure the Web
/// Engine.
///
/// The configuration is passed from JavaScript to the engine as part of the
/// bootstrap process, through the `FlutterEngineInitializer.initializeEngine`
/// JS method, with an (optional) object of type [JsFlutterConfiguration].
///
/// This library also supports the legacy method of setting a plain JavaScript
/// object set as the `flutterConfiguration` property of the top-level `window`
/// object, but that approach is now deprecated and will warn users.
///
/// Both methods are **disallowed** to be used at the same time.
///
/// Example:
///
/// _flutter.loader.loadEntrypoint({
/// // ...
/// onEntrypointLoaded: async function(engineInitializer) {
/// let appRunner = await engineInitializer.initializeEngine({
/// // JsFlutterConfiguration goes here...
/// canvasKitBaseUrl: "https://example.com/my-custom-canvaskit/",
/// });
/// appRunner.runApp();
/// }
/// });
///
/// Example of the **deprecated** style (this will issue a JS console warning!):
///
/// <script>
/// window.flutterConfiguration = {
/// canvasKitBaseUrl: "https://example.com/my-custom-canvaskit/"
/// };
/// </script>
///
/// Configuration properties supplied via this object override those supplied
/// using the corresponding environment variables. For example, if both the
/// `canvasKitBaseUrl` config entry and the `FLUTTER_WEB_CANVASKIT_URL`
/// environment variables are provided, the `canvasKitBaseUrl` entry is used.
@JS()
library configuration;
import 'dart:js_interop';
import 'package:meta/meta.dart';
import 'canvaskit/renderer.dart';
import 'dom.dart';
/// The Web Engine configuration for the current application.
FlutterConfiguration get configuration {
if (_debugConfiguration != null) {
return _debugConfiguration!;
}
return _configuration ??= FlutterConfiguration.legacy(_jsConfiguration);
}
FlutterConfiguration? _configuration;
FlutterConfiguration? _debugConfiguration;
/// Overrides the initial test configuration with new values coming from `newConfig`.
///
/// The initial test configuration (AKA `_jsConfiguration`) is set in the
/// `test_platform.dart` file. See: `window.flutterConfiguration` in `_testBootstrapHandler`.
///
/// The result of calling this method each time is:
///
/// [configuration] = _jsConfiguration + newConfig
///
/// Subsequent calls to this method don't *add* more to an already overridden
/// configuration; this method always starts from an original `_jsConfiguration`,
/// and adds `newConfig` to it.
///
/// If `newConfig` is null, [configuration] resets to the initial `_jsConfiguration`.
///
/// This must be called before the engine is initialized. Calling it after the
/// engine is initialized will result in some of the properties not taking
/// effect because they are consumed during initialization.
@visibleForTesting
void debugOverrideJsConfiguration(JsFlutterConfiguration? newConfig) {
if (newConfig != null) {
_debugConfiguration = configuration.withOverrides(newConfig);
} else {
_debugConfiguration = null;
}
}
/// Supplies Web Engine configuration properties.
class FlutterConfiguration {
/// Constructs an unitialized configuration object.
@visibleForTesting
FlutterConfiguration();
/// Constucts a "tainted by JS globals" configuration object.
///
/// This configuration style is deprecated. It will warn the user about the
/// new API (if used)
FlutterConfiguration.legacy(JsFlutterConfiguration? config) {
if (config != null) {
_usedLegacyConfigStyle = true;
_configuration = config;
}
// Warn the user of the deprecated behavior.
assert(() {
if (config != null) {
domWindow.console.warn('window.flutterConfiguration is now deprecated.\n'
'Use engineInitializer.initializeEngine(config) instead.\n'
'See: https://docs.flutter.dev/development/platform-integration/web/initialization');
}
if (_requestedRendererType != null) {
domWindow.console.warn('window.flutterWebRenderer is now deprecated.\n'
'Use engineInitializer.initializeEngine(config) instead.\n'
'See: https://docs.flutter.dev/development/platform-integration/web/initialization');
}
return true;
}());
}
FlutterConfiguration withOverrides(JsFlutterConfiguration? overrides) {
final JsFlutterConfiguration newJsConfig = objectConstructor.assign(
<String, Object>{}.jsify(),
_configuration.jsify(),
overrides.jsify(),
) as JsFlutterConfiguration;
final FlutterConfiguration newConfig = FlutterConfiguration();
newConfig._configuration = newJsConfig;
return newConfig;
}
bool _usedLegacyConfigStyle = false;
JsFlutterConfiguration? _configuration;
/// Sets a value for [_configuration].
///
/// This method is called by the engine initialization process, through the
/// [initEngineServices] method.
///
/// This method throws an AssertionError, if the _configuration object has
/// been set to anything non-null through the [FlutterConfiguration.legacy]
/// constructor.
void setUserConfiguration(JsFlutterConfiguration? configuration) {
if (configuration != null) {
assert(!_usedLegacyConfigStyle,
'Use engineInitializer.initializeEngine(config) only. '
'Using the (deprecated) window.flutterConfiguration and initializeEngine '
'configuration simultaneously is not supported.');
assert(_requestedRendererType == null || configuration.renderer == null,
'Use engineInitializer.initializeEngine(config) only. '
'Using the (deprecated) window.flutterWebRenderer and initializeEngine '
'configuration simultaneously is not supported.');
_configuration = configuration;
}
}
// Static constant parameters.
//
// These properties affect tree shaking and therefore cannot be supplied at
// runtime. They must be static constants for the compiler to remove dead code
// effectively.
/// Auto detect which rendering backend to use.
///
/// Using flutter tools option "--web-render=auto" or not specifying one
/// would set the value to true. Otherwise, it would be false.
static const bool flutterWebAutoDetect =
bool.fromEnvironment('FLUTTER_WEB_AUTO_DETECT', defaultValue: true);
static const bool flutterWebUseSkwasm =
bool.fromEnvironment('FLUTTER_WEB_USE_SKWASM');
/// Enable the Skia-based rendering backend.
///
/// Using flutter tools option "--web-render=canvaskit" would set the value to
/// true.
///
/// Using flutter tools option "--web-render=html" would set the value to false.
static const bool useSkia =
bool.fromEnvironment('FLUTTER_WEB_USE_SKIA');
// Runtime parameters.
//
// These parameters can be supplied either as environment variables, or at
// runtime. Runtime-supplied values take precedence over environment
// variables.
/// The absolute base URL of the location of the `assets` directory of the app.
///
/// This value is useful when Flutter web assets are deployed to a separate
/// domain (or subdirectory) from which the index.html is served, for example:
///
/// * Application: https://www.my-app.com/
/// * Flutter Assets: https://cdn.example.com/my-app/build-hash/assets/
///
/// The `assetBase` value would be set to:
///
/// * `'https://cdn.example.com/my-app/build-hash/'`
///
/// It is also useful in the case that a Flutter web application is embedded
/// into another web app, in a way that the `<base>` tag of the index.html
/// cannot be set (because it'd break the host app), for example:
///
/// * Application: https://www.my-app.com/
/// * Flutter Assets: https://www.my-app.com/static/companion/flutter/assets/
///
/// The `assetBase` would be set to:
///
/// * `'/static/companion/flutter/'`
///
/// Do not confuse this configuration value with [canvasKitBaseUrl].
String? get assetBase => _configuration?.assetBase;
/// The base URL to use when downloading the CanvasKit script and associated
/// wasm.
///
/// The expected directory structure nested under this URL is as follows:
///
/// /canvaskit.js - the build of CanvasKit JS API bindings
/// /canvaskit.wasm - the build of CanvasKit WASM module
///
/// The base URL can be overridden using the `FLUTTER_WEB_CANVASKIT_URL`
/// environment variable or using the configuration API for JavaScript.
///
/// When specifying using the environment variable set it in the Flutter tool
/// using the `--dart-define` option. The value must end with a `/`.
///
/// Example:
///
/// ```
/// flutter run \
/// -d chrome \
/// --web-renderer=canvaskit \
/// --dart-define=FLUTTER_WEB_CANVASKIT_URL=https://example.com/custom-canvaskit-build/
/// ```
String get canvasKitBaseUrl => _configuration?.canvasKitBaseUrl ?? _defaultCanvasKitBaseUrl;
static const String _defaultCanvasKitBaseUrl = String.fromEnvironment(
'FLUTTER_WEB_CANVASKIT_URL',
defaultValue: 'canvaskit/',
);
/// The variant of CanvasKit to download.
///
/// Available values are:
///
/// * `auto` - the default value. The engine will automatically detect the
/// best variant to use based on the browser.
///
/// * `full` - the full variant of CanvasKit that can be used in any browser.
///
/// * `chromium` - the lite variant of CanvasKit that can be used in
/// Chromium-based browsers.
CanvasKitVariant get canvasKitVariant {
final String variant = _configuration?.canvasKitVariant ?? 'auto';
return CanvasKitVariant.values.byName(variant);
}
/// If set to true, forces CPU-only rendering in CanvasKit (i.e. the engine
/// won't use WebGL).
///
/// This is mainly used for testing or for apps that want to ensure they
/// run on devices which don't support WebGL.
bool get canvasKitForceCpuOnly => _configuration?.canvasKitForceCpuOnly ?? _defaultCanvasKitForceCpuOnly;
static const bool _defaultCanvasKitForceCpuOnly = bool.fromEnvironment(
'FLUTTER_WEB_CANVASKIT_FORCE_CPU_ONLY',
);
/// Set this flag to `true` to cause the engine to visualize the semantics tree
/// on the screen for debugging.
///
/// This only works in profile and release modes. Debug mode does not support
/// passing compile-time constants.
///
/// Example:
///
/// ```
/// flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true
/// ```
bool get debugShowSemanticsNodes => _configuration?.debugShowSemanticsNodes ?? _defaultDebugShowSemanticsNodes;
static const bool _defaultDebugShowSemanticsNodes = bool.fromEnvironment(
'FLUTTER_WEB_DEBUG_SHOW_SEMANTICS',
);
/// Returns the [hostElement] in which the Flutter Application is supposed
/// 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
String? get nonce => _configuration?.nonce;
/// Returns the [requestedRendererType] to be used with the current Flutter
/// application, normally 'canvaskit' or 'auto'.
///
/// This value may come from the JS configuration, but also a specific JS value:
/// `window.flutterWebRenderer`.
///
/// This is used by the Renderer class to decide how to initialize the engine.
String? get requestedRendererType => _configuration?.renderer ?? _requestedRendererType;
/// Whether to use color emojis or not.
///
/// The font used to render color emojis is large (~24MB). This configuration
/// gives developers the ability to decide for their app.
bool get useColorEmoji => _configuration?.useColorEmoji ?? false;
}
@JS('window.flutterConfiguration')
external JsFlutterConfiguration? get _jsConfiguration;
/// The JS bindings for the object that's set as `window.flutterConfiguration`.
@JS()
@anonymous
@staticInterop
class JsFlutterConfiguration {
external factory JsFlutterConfiguration();
}
extension JsFlutterConfigurationExtension on JsFlutterConfiguration {
@JS('assetBase')
external JSString? get _assetBase;
String? get assetBase => _assetBase?.toDart;
@JS('canvasKitBaseUrl')
external JSString? get _canvasKitBaseUrl;
String? get canvasKitBaseUrl => _canvasKitBaseUrl?.toDart;
@JS('canvasKitVariant')
external JSString? get _canvasKitVariant;
String? get canvasKitVariant => _canvasKitVariant?.toDart;
@JS('canvasKitForceCpuOnly')
external JSBoolean? get _canvasKitForceCpuOnly;
bool? get canvasKitForceCpuOnly => _canvasKitForceCpuOnly?.toDart;
@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;
@JS('renderer')
external JSString? get _renderer;
String? get renderer => _renderer?.toDart;
@JS('useColorEmoji')
external JSBoolean? get _useColorEmoji;
bool? get useColorEmoji => _useColorEmoji?.toDart;
}
/// A JavaScript entrypoint that allows developer to set rendering backend
/// at runtime before launching the application.
@JS('window.flutterWebRenderer')
external JSString? get __requestedRendererType;
String? get _requestedRendererType => __requestedRendererType?.toDart;