"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter/lib/src/foundation/binding.dart" (13 Nov 2020, 22134 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 import 'dart:async';
    6 import 'dart:convert' show json;
    7 import 'dart:developer' as developer;
    8 import 'dart:io' show exit;
    9 import 'dart:ui' as ui show  Window, window, Brightness;
   10 // Before adding any more dart:ui imports, please read the README.
   11 
   12 import 'package:meta/meta.dart';
   13 
   14 import 'assertions.dart';
   15 import 'basic_types.dart';
   16 import 'constants.dart';
   17 import 'debug.dart';
   18 import 'object.dart';
   19 import 'platform.dart';
   20 import 'print.dart';
   21 
   22 /// Signature for service extensions.
   23 ///
   24 /// The returned map must not contain the keys "type" or "method", as
   25 /// they will be replaced before the value is sent to the client. The
   26 /// "type" key will be set to the string `_extensionType` to indicate
   27 /// that this is a return value from a service extension, and the
   28 /// "method" key will be set to the full name of the method.
   29 typedef ServiceExtensionCallback = Future<Map<String, dynamic>> Function(Map<String, String> parameters);
   30 
   31 /// Base class for mixins that provide singleton services (also known as
   32 /// "bindings").
   33 ///
   34 /// To use this class in an `on` clause of a mixin, inherit from it and implement
   35 /// [initInstances()]. The mixin is guaranteed to only be constructed once in
   36 /// the lifetime of the app (more precisely, it will assert if constructed twice
   37 /// in checked mode).
   38 ///
   39 /// The top-most layer used to write the application will have a concrete class
   40 /// that inherits from [BindingBase] and uses all the various [BindingBase]
   41 /// mixins (such as [ServicesBinding]). For example, the Widgets library in
   42 /// Flutter introduces a binding called [WidgetsFlutterBinding]. The relevant
   43 /// library defines how to create the binding. It could be implied (for example,
   44 /// [WidgetsFlutterBinding] is automatically started from [runApp]), or the
   45 /// application might be required to explicitly call the constructor.
   46 abstract class BindingBase {
   47   /// Default abstract constructor for bindings.
   48   ///
   49   /// First calls [initInstances] to have bindings initialize their
   50   /// instance pointers and other state, then calls
   51   /// [initServiceExtensions] to have bindings initialize their
   52   /// observatory service extensions, if any.
   53   BindingBase() {
   54     developer.Timeline.startSync('Framework initialization');
   55 
   56     assert(!_debugInitialized);
   57     initInstances();
   58     assert(_debugInitialized);
   59 
   60     assert(!_debugServiceExtensionsRegistered);
   61     initServiceExtensions();
   62     assert(_debugServiceExtensionsRegistered);
   63 
   64     developer.postEvent('Flutter.FrameworkInitialization', <String, String>{});
   65 
   66     developer.Timeline.finishSync();
   67   }
   68 
   69   static bool _debugInitialized = false;
   70   static bool _debugServiceExtensionsRegistered = false;
   71 
   72   /// The window to which this binding is bound.
   73   ///
   74   /// A number of additional bindings are defined as extensions of [BindingBase],
   75   /// e.g., [ServicesBinding], [RendererBinding], and [WidgetsBinding]. Each of
   76   /// these bindings define behaviors that interact with a [ui.Window], e.g.,
   77   /// [ServicesBinding] registers a [ui.Window.onPlatformMessage] handler, and
   78   /// [RendererBinding] registers [ui.Window.onMetricsChanged],
   79   /// [ui.Window.onTextScaleFactorChanged], [ui.Window.onSemanticsEnabledChanged],
   80   /// and [ui.Window.onSemanticsAction] handlers.
   81   ///
   82   /// Each of these other bindings could individually access a [Window] statically,
   83   /// but that would preclude the ability to test these behaviors with a fake
   84   /// window for verification purposes.  Therefore, [BindingBase] exposes this
   85   /// [Window] for use by other bindings.  A subclass of [BindingBase], such as
   86   /// [TestWidgetsFlutterBinding], can override this accessor to return a
   87   /// different [Window] implementation, such as a [TestWindow].
   88   ui.Window get window => ui.window;
   89 
   90   /// The initialization method. Subclasses override this method to hook into
   91   /// the platform and otherwise configure their services. Subclasses must call
   92   /// "super.initInstances()".
   93   ///
   94   /// By convention, if the service is to be provided as a singleton, it should
   95   /// be exposed as `MixinClassName.instance`, a static getter that returns
   96   /// `MixinClassName._instance`, a static field that is set by
   97   /// `initInstances()`.
   98   @protected
   99   @mustCallSuper
  100   void initInstances() {
  101     assert(!_debugInitialized);
  102     assert(() {
  103       _debugInitialized = true;
  104       return true;
  105     }());
  106   }
  107 
  108   /// Called when the binding is initialized, to register service
  109   /// extensions.
  110   ///
  111   /// Bindings that want to expose service extensions should overload
  112   /// this method to register them using calls to
  113   /// [registerSignalServiceExtension],
  114   /// [registerBoolServiceExtension],
  115   /// [registerNumericServiceExtension], and
  116   /// [registerServiceExtension] (in increasing order of complexity).
  117   ///
  118   /// Implementations of this method must call their superclass
  119   /// implementation.
  120   ///
  121   /// {@macro flutter.foundation.bindingBase.registerServiceExtension}
  122   ///
  123   /// See also:
  124   ///
  125   ///  * <https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#rpcs-requests-and-responses>
  126   @protected
  127   @mustCallSuper
  128   void initServiceExtensions() {
  129     assert(!_debugServiceExtensionsRegistered);
  130 
  131     assert(() {
  132       registerSignalServiceExtension(
  133         name: 'reassemble',
  134         callback: reassembleApplication,
  135       );
  136       return true;
  137     }());
  138 
  139     if (!kReleaseMode && !kIsWeb) {
  140       registerSignalServiceExtension(
  141         name: 'exit',
  142         callback: _exitApplication,
  143       );
  144     }
  145 
  146     assert(() {
  147       const String platformOverrideExtensionName = 'platformOverride';
  148       registerServiceExtension(
  149         name: platformOverrideExtensionName,
  150         callback: (Map<String, String> parameters) async {
  151           if (parameters.containsKey('value')) {
  152             switch (parameters['value']) {
  153               case 'android':
  154                 debugDefaultTargetPlatformOverride = TargetPlatform.android;
  155                 break;
  156               case 'fuchsia':
  157                 debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia;
  158                 break;
  159               case 'iOS':
  160                 debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
  161                 break;
  162               case 'linux':
  163                 debugDefaultTargetPlatformOverride = TargetPlatform.linux;
  164                 break;
  165               case 'macOS':
  166                 debugDefaultTargetPlatformOverride = TargetPlatform.macOS;
  167                 break;
  168               case 'windows':
  169                 debugDefaultTargetPlatformOverride = TargetPlatform.windows;
  170                 break;
  171               case 'default':
  172               default:
  173                 debugDefaultTargetPlatformOverride = null;
  174             }
  175             _postExtensionStateChangedEvent(
  176               platformOverrideExtensionName,
  177               defaultTargetPlatform.toString().substring('$TargetPlatform.'.length),
  178             );
  179             await reassembleApplication();
  180           }
  181           return <String, dynamic>{
  182             'value': defaultTargetPlatform
  183                      .toString()
  184                      .substring('$TargetPlatform.'.length),
  185           };
  186         },
  187       );
  188 
  189       const String brightnessOverrideExtensionName = 'brightnessOverride';
  190       registerServiceExtension(
  191         name: brightnessOverrideExtensionName,
  192         callback: (Map<String, String> parameters) async {
  193           if (parameters.containsKey('value')) {
  194             switch (parameters['value']) {
  195               case 'Brightness.light':
  196                 debugBrightnessOverride = ui.Brightness.light;
  197                 break;
  198               case 'Brightness.dark':
  199                 debugBrightnessOverride = ui.Brightness.dark;
  200                 break;
  201               default:
  202                 debugBrightnessOverride = null;
  203             }
  204             _postExtensionStateChangedEvent(
  205               brightnessOverrideExtensionName,
  206               (debugBrightnessOverride ?? window.platformBrightness).toString(),
  207             );
  208             await reassembleApplication();
  209           }
  210           return <String, dynamic>{
  211             'value': (debugBrightnessOverride ?? window.platformBrightness).toString(),
  212           };
  213         },
  214       );
  215       return true;
  216     }());
  217     assert(() {
  218       _debugServiceExtensionsRegistered = true;
  219       return true;
  220     }());
  221   }
  222 
  223   /// Whether [lockEvents] is currently locking events.
  224   ///
  225   /// Binding subclasses that fire events should check this first, and if it is
  226   /// set, queue events instead of firing them.
  227   ///
  228   /// Events should be flushed when [unlocked] is called.
  229   @protected
  230   bool get locked => _lockCount > 0;
  231   int _lockCount = 0;
  232 
  233   /// Locks the dispatching of asynchronous events and callbacks until the
  234   /// callback's future completes.
  235   ///
  236   /// This causes input lag and should therefore be avoided when possible. It is
  237   /// primarily intended for use during non-user-interactive time such as to
  238   /// allow [reassembleApplication] to block input while it walks the tree
  239   /// (which it partially does asynchronously).
  240   ///
  241   /// The [Future] returned by the `callback` argument is returned by [lockEvents].
  242   @protected
  243   Future<void> lockEvents(Future<void> callback()) {
  244     developer.Timeline.startSync('Lock events');
  245 
  246     assert(callback != null);
  247     _lockCount += 1;
  248     final Future<void> future = callback();
  249     assert(future != null, 'The lockEvents() callback returned null; it should return a Future<void> that completes when the lock is to expire.');
  250     future.whenComplete(() {
  251       _lockCount -= 1;
  252       if (!locked) {
  253         developer.Timeline.finishSync();
  254         unlocked();
  255       }
  256     });
  257     return future;
  258   }
  259 
  260   /// Called by [lockEvents] when events get unlocked.
  261   ///
  262   /// This should flush any events that were queued while [locked] was true.
  263   @protected
  264   @mustCallSuper
  265   void unlocked() {
  266     assert(!locked);
  267   }
  268 
  269   /// Cause the entire application to redraw, e.g. after a hot reload.
  270   ///
  271   /// This is used by development tools when the application code has changed,
  272   /// to cause the application to pick up any changed code. It can be triggered
  273   /// manually by sending the `ext.flutter.reassemble` service extension signal.
  274   ///
  275   /// This method is very computationally expensive and should not be used in
  276   /// production code. There is never a valid reason to cause the entire
  277   /// application to repaint in production. All aspects of the Flutter framework
  278   /// know how to redraw when necessary. It is only necessary in development
  279   /// when the code is literally changed on the fly (e.g. in hot reload) or when
  280   /// debug flags are being toggled.
  281   ///
  282   /// While this method runs, events are locked (e.g. pointer events are not
  283   /// dispatched).
  284   ///
  285   /// Subclasses (binding classes) should override [performReassemble] to react
  286   /// to this method being called. This method itself should not be overridden.
  287   Future<void> reassembleApplication() {
  288     return lockEvents(performReassemble);
  289   }
  290 
  291   /// This method is called by [reassembleApplication] to actually cause the
  292   /// application to reassemble, e.g. after a hot reload.
  293   ///
  294   /// Bindings are expected to use this method to re-register anything that uses
  295   /// closures, so that they do not keep pointing to old code, and to flush any
  296   /// caches of previously computed values, in case the new code would compute
  297   /// them differently. For example, the rendering layer triggers the entire
  298   /// application to repaint when this is called.
  299   ///
  300   /// Do not call this method directly. Instead, use [reassembleApplication].
  301   @mustCallSuper
  302   @protected
  303   Future<void> performReassemble() {
  304     FlutterError.resetErrorCount();
  305     return Future<void>.value();
  306   }
  307 
  308   /// Registers a service extension method with the given name (full
  309   /// name "ext.flutter.name"), which takes no arguments and returns
  310   /// no value.
  311   ///
  312   /// Calls the `callback` callback when the service extension is called.
  313   ///
  314   /// {@macro flutter.foundation.bindingBase.registerServiceExtension}
  315   @protected
  316   void registerSignalServiceExtension({
  317     required String name,
  318     required AsyncCallback callback,
  319   }) {
  320     assert(name != null);
  321     assert(callback != null);
  322     registerServiceExtension(
  323       name: name,
  324       callback: (Map<String, String> parameters) async {
  325         await callback();
  326         return <String, dynamic>{};
  327       },
  328     );
  329   }
  330 
  331   /// Registers a service extension method with the given name (full
  332   /// name "ext.flutter.name"), which takes a single argument
  333   /// "enabled" which can have the value "true" or the value "false"
  334   /// or can be omitted to read the current value. (Any value other
  335   /// than "true" is considered equivalent to "false". Other arguments
  336   /// are ignored.)
  337   ///
  338   /// Calls the `getter` callback to obtain the value when
  339   /// responding to the service extension method being called.
  340   ///
  341   /// Calls the `setter` callback with the new value when the
  342   /// service extension method is called with a new value.
  343   ///
  344   /// {@macro flutter.foundation.bindingBase.registerServiceExtension}
  345   @protected
  346   void registerBoolServiceExtension({
  347     required String name,
  348     required AsyncValueGetter<bool> getter,
  349     required AsyncValueSetter<bool> setter,
  350   }) {
  351     assert(name != null);
  352     assert(getter != null);
  353     assert(setter != null);
  354     registerServiceExtension(
  355       name: name,
  356       callback: (Map<String, String> parameters) async {
  357         if (parameters.containsKey('enabled')) {
  358           await setter(parameters['enabled'] == 'true');
  359           _postExtensionStateChangedEvent(name, await getter() ? 'true' : 'false');
  360         }
  361         return <String, dynamic>{'enabled': await getter() ? 'true' : 'false'};
  362       },
  363     );
  364   }
  365 
  366   /// Registers a service extension method with the given name (full
  367   /// name "ext.flutter.name"), which takes a single argument with the
  368   /// same name as the method which, if present, must have a value
  369   /// that can be parsed by [double.parse], and can be omitted to read
  370   /// the current value. (Other arguments are ignored.)
  371   ///
  372   /// Calls the `getter` callback to obtain the value when
  373   /// responding to the service extension method being called.
  374   ///
  375   /// Calls the `setter` callback with the new value when the
  376   /// service extension method is called with a new value.
  377   ///
  378   /// {@macro flutter.foundation.bindingBase.registerServiceExtension}
  379   @protected
  380   void registerNumericServiceExtension({
  381     required String name,
  382     required AsyncValueGetter<double> getter,
  383     required AsyncValueSetter<double> setter,
  384   }) {
  385     assert(name != null);
  386     assert(getter != null);
  387     assert(setter != null);
  388     registerServiceExtension(
  389       name: name,
  390       callback: (Map<String, String> parameters) async {
  391         if (parameters.containsKey(name)) {
  392           await setter(double.parse(parameters[name]!));
  393           _postExtensionStateChangedEvent(name, (await getter()).toString());
  394         }
  395         return <String, dynamic>{name: (await getter()).toString()};
  396       },
  397     );
  398   }
  399 
  400   /// Sends an event when a service extension's state is changed.
  401   ///
  402   /// Clients should listen for this event to stay aware of the current service
  403   /// extension state. Any service extension that manages a state should call
  404   /// this method on state change.
  405   ///
  406   /// `value` reflects the newly updated service extension value.
  407   ///
  408   /// This will be called automatically for service extensions registered via
  409   /// [registerBoolServiceExtension], [registerNumericServiceExtension], or
  410   /// [registerStringServiceExtension].
  411   void _postExtensionStateChangedEvent(String name, dynamic value) {
  412     postEvent(
  413       'Flutter.ServiceExtensionStateChanged',
  414       <String, dynamic>{
  415         'extension': 'ext.flutter.$name',
  416         'value': value,
  417       },
  418     );
  419   }
  420 
  421   /// All events dispatched by a [BindingBase] use this method instead of
  422   /// calling [developer.postEvent] directly so that tests for [BindingBase]
  423   /// can track which events were dispatched by overriding this method.
  424   @protected
  425   void postEvent(String eventKind, Map<String, dynamic> eventData) {
  426     developer.postEvent(eventKind, eventData);
  427   }
  428 
  429   /// Registers a service extension method with the given name (full name
  430   /// "ext.flutter.name"), which optionally takes a single argument with the
  431   /// name "value". If the argument is omitted, the value is to be read,
  432   /// otherwise it is to be set. Returns the current value.
  433   ///
  434   /// Calls the `getter` callback to obtain the value when
  435   /// responding to the service extension method being called.
  436   ///
  437   /// Calls the `setter` callback with the new value when the
  438   /// service extension method is called with a new value.
  439   ///
  440   /// {@macro flutter.foundation.bindingBase.registerServiceExtension}
  441   @protected
  442   void registerStringServiceExtension({
  443     required String name,
  444     required AsyncValueGetter<String> getter,
  445     required AsyncValueSetter<String> setter,
  446   }) {
  447     assert(name != null);
  448     assert(getter != null);
  449     assert(setter != null);
  450     registerServiceExtension(
  451       name: name,
  452       callback: (Map<String, String> parameters) async {
  453         if (parameters.containsKey('value')) {
  454           await setter(parameters['value']!);
  455           _postExtensionStateChangedEvent(name, await getter());
  456         }
  457         return <String, dynamic>{'value': await getter()};
  458       },
  459     );
  460   }
  461 
  462   /// Registers a service extension method with the given name (full name
  463   /// "ext.flutter.name").
  464   ///
  465   /// The given callback is called when the extension method is called. The
  466   /// callback must return a [Future] that either eventually completes to a
  467   /// return value in the form of a name/value map where the values can all be
  468   /// converted to JSON using `json.encode()` (see [JsonEncoder]), or fails. In
  469   /// case of failure, the failure is reported to the remote caller and is
  470   /// dumped to the logs.
  471   ///
  472   /// The returned map will be mutated.
  473   ///
  474   /// {@template flutter.foundation.bindingBase.registerServiceExtension}
  475   /// A registered service extension can only be activated if the vm-service
  476   /// is included in the build, which only happens in debug and profile mode.
  477   /// Although a service extension cannot be used in release mode its code may
  478   /// still be included in the Dart snapshot and blow up binary size if it is
  479   /// not wrapped in a guard that allows the tree shaker to remove it (see
  480   /// sample code below).
  481   ///
  482   /// {@tool snippet}
  483   /// The following code registers a service extension that is only included in
  484   /// debug builds.
  485   ///
  486   /// ```dart
  487   /// void myRegistrationFunction() {
  488   ///   assert(() {
  489   ///     // Register your service extension here.
  490   ///     return true;
  491   ///   }());
  492   /// }
  493   /// ```
  494   /// {@end-tool}
  495   ///
  496   /// {@tool snippet}
  497   /// A service extension registered with the following code snippet is
  498   /// available in debug and profile mode.
  499   ///
  500   /// ```dart
  501   /// void myRegistrationFunction() {
  502   ///   // kReleaseMode is defined in the 'flutter/foundation.dart' package.
  503   ///   if (!kReleaseMode) {
  504   ///     // Register your service extension here.
  505   ///   }
  506   /// }
  507   /// ```
  508   /// {@end-tool}
  509   ///
  510   /// Both guards ensure that Dart's tree shaker can remove the code for the
  511   /// service extension in release builds.
  512   /// {@endtemplate}
  513   @protected
  514   void registerServiceExtension({
  515     required String name,
  516     required ServiceExtensionCallback callback,
  517   }) {
  518     assert(name != null);
  519     assert(callback != null);
  520     final String methodName = 'ext.flutter.$name';
  521     developer.registerExtension(methodName, (String method, Map<String, String> parameters) async {
  522       assert(method == methodName);
  523       assert(() {
  524         if (debugInstrumentationEnabled)
  525           debugPrint('service extension method received: $method($parameters)');
  526         return true;
  527       }());
  528 
  529       // VM service extensions are handled as "out of band" messages by the VM,
  530       // which means they are handled at various times, generally ASAP.
  531       // Notably, this includes being handled in the middle of microtask loops.
  532       // While this makes sense for some service extensions (e.g. "dump current
  533       // stack trace", which explicitly doesn't want to wait for a loop to
  534       // complete), Flutter extensions need not be handled with such high
  535       // priority. Further, handling them with such high priority exposes us to
  536       // the possibility that they're handled in the middle of a frame, which
  537       // breaks many assertions. As such, we ensure they we run the callbacks
  538       // on the outer event loop here.
  539       await debugInstrumentAction<void>('Wait for outer event loop', () {
  540         return Future<void>.delayed(Duration.zero);
  541       });
  542 
  543       dynamic caughtException;
  544       StackTrace? caughtStack;
  545       late Map<String, dynamic> result;
  546       try {
  547         result = await callback(parameters);
  548       } catch (exception, stack) {
  549         caughtException = exception;
  550         caughtStack = stack;
  551       }
  552       if (caughtException == null) {
  553         result['type'] = '_extensionType';
  554         result['method'] = method;
  555         return developer.ServiceExtensionResponse.result(json.encode(result));
  556       } else {
  557         FlutterError.reportError(FlutterErrorDetails(
  558           exception: caughtException,
  559           stack: caughtStack,
  560           context: ErrorDescription('during a service extension callback for "$method"'),
  561         ));
  562         return developer.ServiceExtensionResponse.error(
  563           developer.ServiceExtensionResponse.extensionError,
  564           json.encode(<String, String>{
  565             'exception': caughtException.toString(),
  566             'stack': caughtStack.toString(),
  567             'method': method,
  568           }),
  569         );
  570       }
  571     });
  572   }
  573 
  574   @override
  575   String toString() => '<${objectRuntimeType(this, 'BindingBase')}>';
  576 }
  577 
  578 /// Terminate the Flutter application.
  579 Future<void> _exitApplication() async {
  580   exit(0);
  581 }