diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index d3f68a9b18537..e7c70e51f9a06 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -146,7 +146,7 @@ void _updateAccessibilityFeatures(int values) { if (newFeatures == window._accessibilityFeatures) return; window._accessibilityFeatures = newFeatures; - _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFlagsChangedZone); + _invoke(window.onAccessibilityFeaturesChanged, window._onAccessibilityFeaturesChangedZone); } @pragma('vm:entry-point') @@ -276,22 +276,7 @@ void _invoke1(void callback(A a), Zone zone, A arg) { } } -/// Invokes [callback] inside the given [zone] passing it [arg1] and [arg2]. -// ignore: unused_element -void _invoke2(void callback(A1 a1, A2 a2), Zone zone, A1 arg1, A2 arg2) { - if (callback == null) - return; - - assert(zone != null); - - if (identical(zone, Zone.current)) { - callback(arg1, arg2); - } else { - zone.runBinaryGuarded(callback, arg1, arg2); - } -} - -/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2] and [arg3]. +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { if (callback == null) return; diff --git a/lib/ui/window.dart b/lib/ui/window.dart index d52bfea1a6c3c..4c4b9fe9d94ee 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -1098,10 +1098,10 @@ class Window { /// callback was set. VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; VoidCallback _onAccessibilityFeaturesChanged; - Zone _onAccessibilityFlagsChangedZone; + Zone _onAccessibilityFeaturesChangedZone; set onAccessibilityFeaturesChanged(VoidCallback callback) { _onAccessibilityFeaturesChanged = callback; - _onAccessibilityFlagsChangedZone = Zone.current; + _onAccessibilityFeaturesChangedZone = Zone.current; } /// Change the retained semantics data about this window. diff --git a/lib/web_ui/lib/src/engine.dart b/lib/web_ui/lib/src/engine.dart index 04e688ff090e7..f735c78ea5612 100644 --- a/lib/web_ui/lib/src/engine.dart +++ b/lib/web_ui/lib/src/engine.dart @@ -181,17 +181,17 @@ void webOnlyInitializeEngine() { // microsecond precision, and only then convert to `int`. final int highResTimeMicroseconds = (1000 * highResTime).toInt(); - if (ui.window.onBeginFrame != null) { - ui.window - .onBeginFrame(Duration(microseconds: highResTimeMicroseconds)); + if (window._onBeginFrame != null) { + window + .invokeOnBeginFrame(Duration(microseconds: highResTimeMicroseconds)); } - if (ui.window.onDrawFrame != null) { + if (window._onDrawFrame != null) { // TODO(yjbanov): technically Flutter flushes microtasks between // onBeginFrame and onDrawFrame. We don't, which hasn't // been an issue yet, but eventually we'll have to // implement it properly. - ui.window.onDrawFrame(); + window.invokeOnDrawFrame(); } }); } diff --git a/lib/web_ui/lib/src/engine/dom_renderer.dart b/lib/web_ui/lib/src/engine/dom_renderer.dart index a5d630dd59de7..5990ad2f49d7a 100644 --- a/lib/web_ui/lib/src/engine/dom_renderer.dart +++ b/lib/web_ui/lib/src/engine/dom_renderer.dart @@ -467,8 +467,8 @@ flt-glass-pane * { /// Called immediately after browser window metrics change. void _metricsDidChange(html.Event event) { window._computePhysicalSize(); - if (ui.window.onMetricsChanged != null) { - ui.window.onMetricsChanged(); + if (window._onMetricsChanged != null) { + window.invokeOnMetricsChanged(); } } diff --git a/lib/web_ui/lib/src/engine/history.dart b/lib/web_ui/lib/src/engine/history.dart index 0c46004dc0117..ad7558f7ecfc5 100644 --- a/lib/web_ui/lib/src/engine/history.dart +++ b/lib/web_ui/lib/src/engine/history.dart @@ -94,8 +94,8 @@ class BrowserHistory { _setupFlutterEntry(_locationStrategy); // 2. Send a 'popRoute' platform message so the app can handle it accordingly. - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall(_popRouteMethodCall), (_) {}, @@ -113,8 +113,8 @@ class BrowserHistory { _userProvidedRouteName = null; // Send a 'pushRoute' platform message so the app handles it accordingly. - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/navigation', const JSONMethodCodec().encodeMethodCall( MethodCall('pushRoute', newRouteName), diff --git a/lib/web_ui/lib/src/engine/keyboard.dart b/lib/web_ui/lib/src/engine/keyboard.dart index d42b44fc6f829..4183cd3054256 100644 --- a/lib/web_ui/lib/src/engine/keyboard.dart +++ b/lib/web_ui/lib/src/engine/keyboard.dart @@ -68,7 +68,7 @@ class Keyboard { static const JSONMessageCodec _messageCodec = JSONMessageCodec(); void _handleHtmlEvent(html.KeyboardEvent event) { - if (ui.window.onPlatformMessage == null) { + if (window._onPlatformMessage == null) { return; } @@ -88,7 +88,7 @@ class Keyboard { 'metaState': _getMetaState(event), }; - ui.window.onPlatformMessage('flutter/keyevent', + window.invokeOnPlatformMessage('flutter/keyevent', _messageCodec.encodeMessage(eventData), _noopCallback); } diff --git a/lib/web_ui/lib/src/engine/pointer_binding.dart b/lib/web_ui/lib/src/engine/pointer_binding.dart index 6b6726f3b6ce7..d88e1d4074d84 100644 --- a/lib/web_ui/lib/src/engine/pointer_binding.dart +++ b/lib/web_ui/lib/src/engine/pointer_binding.dart @@ -125,9 +125,8 @@ class PointerBinding { void _onPointerData(Iterable data) { final ui.PointerDataPacket packet = ui.PointerDataPacket(data: data.toList()); - final ui.PointerDataPacketCallback callback = ui.window.onPointerDataPacket; - if (callback != null) { - callback(packet); + if (window._onPointerDataPacket != null) { + window.invokeOnPointerDataPacket(packet); } } } diff --git a/lib/web_ui/lib/src/engine/semantics/incrementable.dart b/lib/web_ui/lib/src/engine/semantics/incrementable.dart index e61baf76188d5..5ae975aa4fd1f 100644 --- a/lib/web_ui/lib/src/engine/semantics/incrementable.dart +++ b/lib/web_ui/lib/src/engine/semantics/incrementable.dart @@ -53,11 +53,11 @@ class Incrementable extends RoleManager { final int newInputValue = int.parse(_element.value); if (newInputValue > _currentSurrogateValue) { _currentSurrogateValue += 1; - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.increase, null); } else if (newInputValue < _currentSurrogateValue) { _currentSurrogateValue -= 1; - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.decrease, null); } }); diff --git a/lib/web_ui/lib/src/engine/semantics/scrollable.dart b/lib/web_ui/lib/src/engine/semantics/scrollable.dart index 0f847d631098e..0c818314a26f1 100644 --- a/lib/web_ui/lib/src/engine/semantics/scrollable.dart +++ b/lib/web_ui/lib/src/engine/semantics/scrollable.dart @@ -53,20 +53,20 @@ class Scrollable extends RoleManager { final int semanticsId = semanticsObject.id; if (doScrollForward) { if (semanticsObject.isVerticalScrollContainer) { - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollUp, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollLeft, null); } } else { if (semanticsObject.isVerticalScrollContainer) { - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollDown, null); } else { assert(semanticsObject.isHorizontalScrollContainer); - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsId, ui.SemanticsAction.scrollRight, null); } } diff --git a/lib/web_ui/lib/src/engine/semantics/semantics.dart b/lib/web_ui/lib/src/engine/semantics/semantics.dart index 0554a1a48350a..a37fb7e19bae9 100644 --- a/lib/web_ui/lib/src/engine/semantics/semantics.dart +++ b/lib/web_ui/lib/src/engine/semantics/semantics.dart @@ -1236,8 +1236,8 @@ class EngineSemanticsOwner { _gestureModeClock?.datetime = null; } - if (ui.window.onSemanticsEnabledChanged != null) { - ui.window.onSemanticsEnabledChanged(); + if (window._onSemanticsEnabledChanged != null) { + window.invokeOnSemanticsEnabledChanged(); } } diff --git a/lib/web_ui/lib/src/engine/semantics/tappable.dart b/lib/web_ui/lib/src/engine/semantics/tappable.dart index 25b94819d4882..e60c9881918e5 100644 --- a/lib/web_ui/lib/src/engine/semantics/tappable.dart +++ b/lib/web_ui/lib/src/engine/semantics/tappable.dart @@ -39,7 +39,7 @@ class Tappable extends RoleManager { GestureMode.browserGestures) { return; } - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); }; element.addEventListener('click', _clickListener); diff --git a/lib/web_ui/lib/src/engine/semantics/text_field.dart b/lib/web_ui/lib/src/engine/semantics/text_field.dart index 4091ece5854db..5018d8d2d5aa2 100644 --- a/lib/web_ui/lib/src/engine/semantics/text_field.dart +++ b/lib/web_ui/lib/src/engine/semantics/text_field.dart @@ -149,8 +149,8 @@ class TextField extends RoleManager { } textEditing.useCustomEditableElement(textEditingElement); - ui.window - .onSemanticsAction(semanticsObject.id, ui.SemanticsAction.tap, null); + window + .invokeOnSemanticsAction(semanticsObject.id, ui.SemanticsAction.tap, null); }); } @@ -187,7 +187,7 @@ class TextField extends RoleManager { if (offsetX * offsetX + offsetY * offsetY < kTouchSlop) { // Recognize it as a tap that requires a keyboard. - ui.window.onSemanticsAction( + window.invokeOnSemanticsAction( semanticsObject.id, ui.SemanticsAction.tap, null); } } else { diff --git a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart index b13b0a168e604..040e11f3036b3 100644 --- a/lib/web_ui/lib/src/engine/text_editing/text_editing.dart +++ b/lib/web_ui/lib/src/engine/text_editing/text_editing.dart @@ -823,8 +823,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.updateEditingState' message to the framework. void updateEditingState(int clientId, EditingState editingState) { - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall('TextInputClient.updateEditingState', [ @@ -839,8 +839,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.performAction' message to the framework. void performAction(int clientId, String inputAction) { - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( @@ -855,8 +855,8 @@ class TextEditingChannel { /// Sends the 'TextInputClient.onConnectionClosed' message to the framework. void onConnectionClosed(int clientId) { - if (ui.window.onPlatformMessage != null) { - ui.window.onPlatformMessage( + if (window._onPlatformMessage != null) { + window.invokeOnPlatformMessage( 'flutter/textinput', const JSONMethodCodec().encodeMethodCall( MethodCall( diff --git a/lib/web_ui/lib/src/engine/util.dart b/lib/web_ui/lib/src/engine/util.dart index 54a03397de936..4ca7c5c540eb9 100644 --- a/lib/web_ui/lib/src/engine/util.dart +++ b/lib/web_ui/lib/src/engine/util.dart @@ -444,3 +444,14 @@ void applyWebkitClipFix(html.Element containerElement) { containerElement.style.zIndex = '0'; } } + +final ByteData _fontChangeMessage = JSONMessageCodec().encodeMessage({'type': 'fontsChange'}); + +FutureOr sendFontChangeMessage() async { + if (window._onPlatformMessage != null) + window.invokeOnPlatformMessage( + 'flutter/system', + _fontChangeMessage, + (_) {}, + ); +} diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index 8bfb18039b5fb..03695abc096b3 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -110,19 +110,248 @@ class EngineWindow extends ui.Window { _browserHistory.locationStrategy = strategy; } + @override + ui.VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; + ui.VoidCallback _onTextScaleFactorChanged; + Zone _onTextScaleFactorChangedZone; + @override + set onTextScaleFactorChanged(ui.VoidCallback callback) { + _onTextScaleFactorChanged = callback; + _onTextScaleFactorChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnTextScaleFactorChanged() { + _invoke(_onTextScaleFactorChanged, _onTextScaleFactorChangedZone); + } + + @override + ui.VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; + ui.VoidCallback _onPlatformBrightnessChanged; + Zone _onPlatformBrightnessChangedZone; + @override + set onPlatformBrightnessChanged(ui.VoidCallback callback) { + _onPlatformBrightnessChanged = callback; + _onPlatformBrightnessChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformBrightnessChanged() { + _invoke(_onPlatformBrightnessChanged, _onPlatformBrightnessChangedZone); + } + + @override + ui.VoidCallback get onMetricsChanged => _onMetricsChanged; + ui.VoidCallback _onMetricsChanged; + Zone _onMetricsChangedZone; + @override + set onMetricsChanged(ui.VoidCallback callback) { + _onMetricsChanged = callback; + _onMetricsChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnMetricsChanged() { + _invoke(_onMetricsChanged, _onMetricsChangedZone); + } + + @override + ui.VoidCallback get onLocaleChanged => _onLocaleChanged; + ui.VoidCallback _onLocaleChanged; + Zone _onLocaleChangedZone; + @override + set onLocaleChanged(ui.VoidCallback callback) { + _onLocaleChanged = callback; + _onLocaleChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnLocaleChanged() { + _invoke(_onLocaleChanged, _onLocaleChangedZone); + } + + @override + ui.FrameCallback get onBeginFrame => _onBeginFrame; + ui.FrameCallback _onBeginFrame; + Zone _onBeginFrameZone; + @override + set onBeginFrame(ui.FrameCallback callback) { + _onBeginFrame = callback; + _onBeginFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnBeginFrame(Duration duration) { + _invoke1(_onBeginFrame, _onBeginFrameZone, duration); + } + + @override + ui.TimingsCallback get onReportTimings => _onReportTimings; + ui.TimingsCallback _onReportTimings; + Zone _onReportTimingsZone; + @override + set onReportTimings(ui.TimingsCallback callback) { + _onReportTimings = callback; + _onReportTimingsZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnReportTimings(List timings) { + _invoke1>(_onReportTimings, _onReportTimingsZone, timings); + } + + @override + ui.VoidCallback get onDrawFrame => _onDrawFrame; + ui.VoidCallback _onDrawFrame; + Zone _onDrawFrameZone; + @override + set onDrawFrame(ui.VoidCallback callback) { + _onDrawFrame = callback; + _onDrawFrameZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnDrawFrame() { + _invoke(_onDrawFrame, _onDrawFrameZone); + } + + @override + ui.PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; + ui.PointerDataPacketCallback _onPointerDataPacket; + Zone _onPointerDataPacketZone; + @override + set onPointerDataPacket(ui.PointerDataPacketCallback callback) { + _onPointerDataPacket = callback; + _onPointerDataPacketZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPointerDataPacket(ui.PointerDataPacket packet) { + _invoke1(_onPointerDataPacket, _onPointerDataPacketZone, packet); + } + + @override + ui.VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; + ui.VoidCallback _onSemanticsEnabledChanged; + Zone _onSemanticsEnabledChangedZone; + @override + set onSemanticsEnabledChanged(ui.VoidCallback callback) { + _onSemanticsEnabledChanged = callback; + _onSemanticsEnabledChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsEnabledChanged() { + _invoke(_onSemanticsEnabledChanged, _onSemanticsEnabledChangedZone); + } + + @override + ui.SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; + ui.SemanticsActionCallback _onSemanticsAction; + Zone _onSemanticsActionZone; + @override + set onSemanticsAction(ui.SemanticsActionCallback callback) { + _onSemanticsAction = callback; + _onSemanticsActionZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnSemanticsAction(int id, ui.SemanticsAction action, ByteData args) { + _invoke3(_onSemanticsAction, + _onSemanticsActionZone, id, action, args); + } + + @override + ui.VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged; + ui.VoidCallback _onAccessibilityFeaturesChanged; + Zone _onAccessibilityFeaturesChangedZone; + @override + set onAccessibilityFeaturesChanged(ui.VoidCallback callback) { + _onAccessibilityFeaturesChanged = callback; + _onAccessibilityFeaturesChangedZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnAccessibilityFeaturesChanged() { + _invoke(_onAccessibilityFeaturesChanged, _onAccessibilityFeaturesChangedZone); + } + + @override + ui.PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; + ui.PlatformMessageCallback _onPlatformMessage; + Zone _onPlatformMessageZone; + @override + set onPlatformMessage(ui.PlatformMessageCallback callback) { + _onPlatformMessage = callback; + _onPlatformMessageZone = Zone.current; + } + + /// Engine code should use this method instead of the callback directly. + /// Otherwise zones won't work properly. + void invokeOnPlatformMessage(String name, ByteData data, ui.PlatformMessageResponseCallback callback) { + _invoke3( + _onPlatformMessage, + _onPlatformMessageZone, + name, + data, + callback, + ); + } + @override void sendPlatformMessage( String name, ByteData data, ui.PlatformMessageResponseCallback callback, + ) { + _sendPlatformMessage(name, data, _zonedPlatformMessageResponseCallback(callback)); + } + + /// Wraps the given [callback] in another callback that ensures that the + /// original callback is called in the zone it was registered in. + static ui.PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(ui.PlatformMessageResponseCallback callback) { + if (callback == null) + return null; + + // Store the zone in which the callback is being registered. + final Zone registrationZone = Zone.current; + + return (ByteData data) { + registrationZone.runUnaryGuarded(callback, data); + }; + } + + void _sendPlatformMessage( + String name, + ByteData data, + ui.PlatformMessageResponseCallback callback, ) { // In widget tests we want to bypass processing of platform messages. if (assertionsEnabled && ui.debugEmulateFlutterTesterEnvironment) { return; } + if (_debugPrintPlatformMessages) { print('Sent platform message on channel: "$name"'); } + + if (assertionsEnabled && name == 'flutter/debug-echo') { + // Echoes back the data unchanged. Used for testing purpopses. + _replyToPlatformMessage(callback, data); + return; + } + switch (name) { case 'flutter/assets': assert(ui.webOnlyAssetManager != null); @@ -272,7 +501,9 @@ class EngineWindow extends ui.Window { _platformBrightness = newPlatformBrightness; if (previousPlatformBrightness != _platformBrightness && - onPlatformBrightnessChanged != null) onPlatformBrightnessChanged(); + onPlatformBrightnessChanged != null) { + invokeOnPlatformBrightnessChanged(); + } } /// Reference to css media query that indicates the user theme preference on the web. @@ -336,6 +567,50 @@ bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) { return false; } +/// Invokes [callback] inside the given [zone]. +void _invoke(void callback(), Zone zone) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(); + } else { + zone.runGuarded(callback); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg]. +void _invoke1(void callback(A a), Zone zone, A arg) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg); + } else { + zone.runUnaryGuarded(callback, arg); + } +} + +/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3]. +void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1, A2 arg2, A3 arg3) { + if (callback == null) + return; + + assert(zone != null); + + if (identical(zone, Zone.current)) { + callback(arg1, arg2, arg3); + } else { + zone.runGuarded(() { + callback(arg1, arg2, arg3); + }); + } +} + /// The window singleton. /// /// `dart:ui` window delegates to this value. However, this value has a wider diff --git a/lib/web_ui/lib/src/ui/text.dart b/lib/web_ui/lib/src/ui/text.dart index aeba957a8da7b..8a1ae89fab5a6 100644 --- a/lib/web_ui/lib/src/ui/text.dart +++ b/lib/web_ui/lib/src/ui/text.dart @@ -1590,22 +1590,11 @@ abstract class ParagraphBuilder { Future loadFontFromList(Uint8List list, {String fontFamily}) { if (engine.experimentalUseSkia) { return engine.skiaFontCollection.loadFontFromList(list, fontFamily: fontFamily).then( - (_) => _sendFontChangeMessage() + (_) => engine.sendFontChangeMessage() ); } else { return _fontCollection.loadFontFromList(list, fontFamily: fontFamily).then( - (_) => _sendFontChangeMessage() + (_) => engine.sendFontChangeMessage() ); } } - -final ByteData _fontChangeMessage = engine.JSONMessageCodec().encodeMessage({'type': 'fontsChange'}); - -FutureOr _sendFontChangeMessage() async { - if (window.onPlatformMessage != null) - window.onPlatformMessage( - 'flutter/system', - _fontChangeMessage, - (_) {}, - ); -} diff --git a/lib/web_ui/lib/src/ui/window.dart b/lib/web_ui/lib/src/ui/window.dart index f19432c4e0832..0586af5dd1891 100644 --- a/lib/web_ui/lib/src/ui/window.dart +++ b/lib/web_ui/lib/src/ui/window.dart @@ -621,11 +621,8 @@ abstract class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged; - VoidCallback _onTextScaleFactorChanged; - set onTextScaleFactorChanged(VoidCallback callback) { - _onTextScaleFactorChanged = callback; - } + VoidCallback get onTextScaleFactorChanged; + set onTextScaleFactorChanged(VoidCallback callback); /// The setting indicating the current brightness mode of the host platform. Brightness get platformBrightness; @@ -639,11 +636,8 @@ abstract class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged; - VoidCallback _onPlatformBrightnessChanged; - set onPlatformBrightnessChanged(VoidCallback callback) { - _onPlatformBrightnessChanged = callback; - } + VoidCallback get onPlatformBrightnessChanged; + set onPlatformBrightnessChanged(VoidCallback callback); /// A callback that is invoked whenever the [devicePixelRatio], /// [physicalSize], [padding], or [viewInsets] values change, for example @@ -661,11 +655,8 @@ abstract class Window { /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// register for notifications when this is called. /// * [MediaQuery.of], a simpler mechanism for the same. - VoidCallback get onMetricsChanged => _onMetricsChanged; - VoidCallback _onMetricsChanged; - set onMetricsChanged(VoidCallback callback) { - _onMetricsChanged = callback; - } + VoidCallback get onMetricsChanged; + set onMetricsChanged(VoidCallback callback); static const _enUS = const Locale('en', 'US'); @@ -713,11 +704,8 @@ abstract class Window { /// /// * [WidgetsBindingObserver], for a mechanism at the widgets layer to /// observe when this callback is invoked. - VoidCallback get onLocaleChanged => _onLocaleChanged; - VoidCallback _onLocaleChanged; - set onLocaleChanged(VoidCallback callback) { - _onLocaleChanged = callback; - } + VoidCallback get onLocaleChanged; + set onLocaleChanged(VoidCallback callback); /// Requests that, at the next appropriate opportunity, the [onBeginFrame] /// and [onDrawFrame] callbacks be invoked. @@ -753,11 +741,8 @@ abstract class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - FrameCallback get onBeginFrame => _onBeginFrame; - FrameCallback _onBeginFrame; - set onBeginFrame(FrameCallback callback) { - _onBeginFrame = callback; - } + FrameCallback get onBeginFrame; + set onBeginFrame(FrameCallback callback); /// A callback that is invoked to report the [FrameTiming] of recently /// rasterized frames. @@ -775,12 +760,8 @@ abstract class Window { /// decrease the overhead (as this is available in the release mode). The /// timing of any frame will be sent within about 1 second even if there are /// no later frames to batch. - TimingsCallback get onReportTimings => _onReportTimings; - TimingsCallback _onReportTimings; - Zone _onReportTimingsZone; // ignore: unused_field - set onReportTimings(TimingsCallback callback) { - _onReportTimings = callback; - } + TimingsCallback get onReportTimings; + set onReportTimings(TimingsCallback callback); /// A callback that is invoked for each frame after [onBeginFrame] has /// completed and after the microtask queue has been drained. This can be @@ -796,11 +777,8 @@ abstract class Window { /// scheduling of frames. /// * [RendererBinding], the Flutter framework class which manages layout and /// painting. - VoidCallback get onDrawFrame => _onDrawFrame; - VoidCallback _onDrawFrame; - set onDrawFrame(VoidCallback callback) { - _onDrawFrame = callback; - } + VoidCallback get onDrawFrame; + set onDrawFrame(VoidCallback callback); /// A callback that is invoked when pointer data is available. /// @@ -811,11 +789,8 @@ abstract class Window { /// /// * [GestureBinding], the Flutter framework class which manages pointer /// events. - PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket; - PointerDataPacketCallback _onPointerDataPacket; - set onPointerDataPacket(PointerDataPacketCallback callback) { - _onPointerDataPacket = callback; - } + PointerDataPacketCallback get onPointerDataPacket; + set onPointerDataPacket(PointerDataPacketCallback callback); /// The route or path that the embedder requested when the application was /// launched. @@ -864,11 +839,8 @@ abstract class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged; - VoidCallback _onSemanticsEnabledChanged; - set onSemanticsEnabledChanged(VoidCallback callback) { - _onSemanticsEnabledChanged = callback; - } + VoidCallback get onSemanticsEnabledChanged; + set onSemanticsEnabledChanged(VoidCallback callback); /// A callback that is invoked whenever the user requests an action to be /// performed. @@ -878,22 +850,15 @@ abstract class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - SemanticsActionCallback get onSemanticsAction => _onSemanticsAction; - SemanticsActionCallback _onSemanticsAction; - set onSemanticsAction(SemanticsActionCallback callback) { - _onSemanticsAction = callback; - } + SemanticsActionCallback get onSemanticsAction; + set onSemanticsAction(SemanticsActionCallback callback); /// A callback that is invoked when the value of [accessibilityFlags] changes. /// /// The framework invokes this callback in the same zone in which the /// callback was set. - VoidCallback get onAccessibilityFeaturesChanged => - _onAccessibilityFeaturesChanged; - VoidCallback _onAccessibilityFeaturesChanged; - set onAccessibilityFeaturesChanged(VoidCallback callback) { - _onAccessibilityFeaturesChanged = callback; - } + VoidCallback get onAccessibilityFeaturesChanged; + set onAccessibilityFeaturesChanged(VoidCallback callback); /// Called whenever this window receives a message from a platform-specific /// plugin. @@ -908,11 +873,8 @@ abstract class Window { /// /// The framework invokes this callback in the same zone in which the /// callback was set. - PlatformMessageCallback get onPlatformMessage => _onPlatformMessage; - PlatformMessageCallback _onPlatformMessage; - set onPlatformMessage(PlatformMessageCallback callback) { - _onPlatformMessage = callback; - } + PlatformMessageCallback get onPlatformMessage; + set onPlatformMessage(PlatformMessageCallback callback); /// Change the retained semantics data about this window. /// diff --git a/lib/web_ui/test/engine/window_test.dart b/lib/web_ui/test/engine/window_test.dart new file mode 100644 index 0000000000000..4dd297627285e --- /dev/null +++ b/lib/web_ui/test/engine/window_test.dart @@ -0,0 +1,225 @@ +// 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. + +// @dart = 2.6 +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:test/test.dart'; +import 'package:ui/ui.dart' as ui; +import 'package:ui/src/engine.dart'; + +void main() { + test('onTextScaleFactorChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onTextScaleFactorChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onTextScaleFactorChanged, same(callback)); + }); + + window.invokeOnTextScaleFactorChanged(); + }); + + test('onPlatformBrightnessChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onPlatformBrightnessChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onPlatformBrightnessChanged, same(callback)); + }); + + window.invokeOnPlatformBrightnessChanged(); + }); + + test('onMetricsChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onMetricsChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onMetricsChanged, same(callback)); + }); + + window.invokeOnMetricsChanged(); + }); + + test('onLocaleChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onLocaleChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onLocaleChanged, same(callback)); + }); + + window.invokeOnLocaleChanged(); + }); + + test('onBeginFrame preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.FrameCallback callback = (_) { + expect(Zone.current, innerZone); + }; + window.onBeginFrame = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onBeginFrame, same(callback)); + }); + + window.invokeOnBeginFrame(null); + }); + + test('onReportTimings preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.TimingsCallback callback = (_) { + expect(Zone.current, innerZone); + }; + window.onReportTimings = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onReportTimings, same(callback)); + }); + + window.invokeOnReportTimings(null); + }); + + test('onDrawFrame preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onDrawFrame = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onDrawFrame, same(callback)); + }); + + window.invokeOnDrawFrame(); + }); + + test('onPointerDataPacket preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.PointerDataPacketCallback callback = (_) { + expect(Zone.current, innerZone); + }; + window.onPointerDataPacket = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onPointerDataPacket, same(callback)); + }); + + window.invokeOnPointerDataPacket(null); + }); + + test('onSemanticsEnabledChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onSemanticsEnabledChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onSemanticsEnabledChanged, same(callback)); + }); + + window.invokeOnSemanticsEnabledChanged(); + }); + + test('onSemanticsAction preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.SemanticsActionCallback callback = (_, __, ___) { + expect(Zone.current, innerZone); + }; + window.onSemanticsAction = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onSemanticsAction, same(callback)); + }); + + window.invokeOnSemanticsAction(null, null, null); + }); + + test('onAccessibilityFeaturesChanged preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.VoidCallback callback = () { + expect(Zone.current, innerZone); + }; + window.onAccessibilityFeaturesChanged = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onAccessibilityFeaturesChanged, same(callback)); + }); + + window.invokeOnAccessibilityFeaturesChanged(); + }); + + test('onPlatformMessage preserves the zone', () { + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ui.PlatformMessageCallback callback = (_, __, ___) { + expect(Zone.current, innerZone); + }; + window.onPlatformMessage = callback; + + // Test that the getter returns the exact same callback, e.g. it doesn't wrap it. + expect(window.onPlatformMessage, same(callback)); + }); + + window.invokeOnPlatformMessage(null, null, null); + }); + + test('sendPlatformMessage preserves the zone', () async { + final Completer completer = Completer(); + final Zone innerZone = Zone.current.fork(); + + innerZone.runGuarded(() { + final ByteData inputData = ByteData(4); + inputData.setUint32(0, 42); + window.sendPlatformMessage( + 'flutter/debug-echo', + inputData, + (outputData) { + expect(Zone.current, innerZone); + completer.complete(); + }, + ); + }); + + await completer.future; + }); +}