66part of engine;
77
88/// Set this flag to `true` to cause the engine to visualize the semantics tree
9- /// on the screen.
9+ /// on the screen for debugging .
1010///
11- /// This is useful for debugging.
12- const bool _debugShowSemanticsNodes = false ;
11+ /// This only works in profile and release modes. Debug mode does not support
12+ /// passing compile-time constants.
13+ ///
14+ /// Example:
15+ ///
16+ /// ```
17+ /// flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true
18+ /// ```
19+ const bool _debugShowSemanticsNodes = bool .fromEnvironment (
20+ 'FLUTTER_WEB_DEBUG_SHOW_SEMANTICS' ,
21+ defaultValue: false ,
22+ );
1323
1424/// Contains updates for the semantics tree.
1525///
@@ -233,27 +243,29 @@ class SemanticsObject {
233243 /// Creates a semantics tree node with the given [id] and [owner] .
234244 SemanticsObject (this .id, this .owner) {
235245 // DOM nodes created for semantics objects are positioned absolutely using
236- // transforms. We use a transparent color instead of "visibility:hidden" or
237- // "display:none" so that a screen reader does not ignore these elements.
246+ // transforms.
238247 element.style.position = 'absolute' ;
239248
240249 // The root node has some properties that other nodes do not.
241- if (id == 0 ) {
250+ if (id == 0 && ! _debugShowSemanticsNodes ) {
242251 // Make all semantics transparent. We use `filter` instead of `opacity`
243252 // attribute because `filter` is stronger. `opacity` does not apply to
244253 // some elements, particularly on iOS, such as the slider thumb and track.
254+ //
255+ // We use transparency instead of "visibility:hidden" or "display:none"
256+ // so that a screen reader does not ignore these elements.
245257 element.style.filter = 'opacity(0%)' ;
246258
247259 // Make text explicitly transparent to signal to the browser that no
248260 // rasterization needs to be done.
249261 element.style.color = 'rgba(0,0,0,0)' ;
250262 }
251263
264+ // Make semantic elements visible for debugging by outlining them using a
265+ // green border. We do not use `border` attribute because it affects layout
266+ // (`outline` does not).
252267 if (_debugShowSemanticsNodes) {
253- element.style
254- ..filter = 'opacity(90%)'
255- ..outline = '1px solid green'
256- ..color = 'purple' ;
268+ element.style.outline = '1px solid green' ;
257269 }
258270 }
259271
@@ -853,9 +865,9 @@ class SemanticsObject {
853865 hasIdentityTransform &&
854866 verticalContainerAdjustment == 0.0 &&
855867 horizontalContainerAdjustment == 0.0 ) {
856- _resetElementOffsets (element);
868+ _clearSemanticElementTransform (element);
857869 if (containerElement != null ) {
858- _resetElementOffsets (containerElement);
870+ _clearSemanticElementTransform (containerElement);
859871 }
860872 return ;
861873 }
@@ -879,81 +891,48 @@ class SemanticsObject {
879891 effectiveTransformIsIdentity = false ;
880892 }
881893
882- if (! effectiveTransformIsIdentity || isMacOrIOS) {
883- if (effectiveTransformIsIdentity) {
884- effectiveTransform = Matrix4 .identity ();
885- }
886- if (isDesktop) {
887- element.style
888- ..transformOrigin = '0 0 0'
889- ..transform = (effectiveTransformIsIdentity ? 'translate(0px 0px 0px)'
890- : matrix4ToCssTransform (effectiveTransform));
891- } else {
892- // Mobile screen readers observed to have errors while calculating the
893- // semantics focus borders if css `transform` properties are used.
894- // See: https://github.com/flutter/flutter/issues/68225
895- // Therefore we are calculating a bounding rectangle for the
896- // effective transform and use that rectangle to set TLWH css style
897- // properties.
898- // Note: Identity matrix is not using this code path.
899- final ui.Rect rect =
900- computeBoundingRectangleFromMatrix (effectiveTransform, _rect! );
901- element.style
902- ..top = '${rect .top }px'
903- ..left = '${rect .left }px'
904- ..width = '${rect .width }px'
905- ..height = '${rect .height }px' ;
906- }
894+ if (! effectiveTransformIsIdentity) {
895+ element.style
896+ ..transformOrigin = '0 0 0'
897+ ..transform = matrix4ToCssTransform (effectiveTransform);
907898 } else {
908- _resetElementOffsets (element);
909- // TODO: https://github.com/flutter/flutter/issues/73347
899+ _clearSemanticElementTransform (element);
910900 }
911901
912902 if (containerElement != null ) {
913903 if (! hasZeroRectOffset ||
914- isMacOrIOS ||
915904 verticalContainerAdjustment != 0.0 ||
916905 horizontalContainerAdjustment != 0.0 ) {
917906 final double translateX = - _rect! .left + horizontalContainerAdjustment;
918907 final double translateY = - _rect! .top + verticalContainerAdjustment;
919- if (isDesktop) {
920- containerElement.style
921- ..transformOrigin = '0 0 0'
922- ..transform = 'translate(${translateX }px, ${translateY }px)' ;
923- } else {
924- containerElement.style
925- ..top = '${translateY }px'
926- ..left = '${translateX }px' ;
927- }
908+ containerElement.style
909+ ..top = '${translateY }px'
910+ ..left = '${translateX }px' ;
928911 } else {
929- _resetElementOffsets (containerElement);
912+ _clearSemanticElementTransform (containerElement);
930913 }
931914 }
932915 }
933916
934- // On Mac OS and iOS, VoiceOver requires left=0 top=0 value to correctly
935- // handle order. See https://github.com/flutter/flutter/issues/73347.
936- static void _resetElementOffsets (html.Element element) {
917+ /// Clears the transform on a semantic element as if an identity transform is
918+ /// applied.
919+ ///
920+ /// On macOS and iOS, VoiceOver requires `left=0; top=0` value to correctly
921+ /// handle traversal order.
922+ ///
923+ /// See https://github.com/flutter/flutter/issues/73347.
924+ static void _clearSemanticElementTransform (html.Element element) {
925+ element.style
926+ ..removeProperty ('transform-origin' )
927+ ..removeProperty ('transform' );
937928 if (isMacOrIOS) {
938- if (isDesktop) {
939- element.style
940- ..transformOrigin = '0 0 0'
941- ..transform = 'translate(0px, 0px)' ;
942- } else {
943- element.style
944- ..top = '0px'
945- ..left = '0px' ;
946- }
929+ element.style
930+ ..top = '0px'
931+ ..left = '0px' ;
947932 } else {
948- if (isDesktop) {
949- element.style
950- ..removeProperty ('transform-origin' )
951- ..removeProperty ('transform' );
952- } else {
953- element.style
954- ..removeProperty ('top' )
955- ..removeProperty ('left' );
956- }
933+ element.style
934+ ..removeProperty ('top' )
935+ ..removeProperty ('left' );
957936 }
958937 }
959938
@@ -1493,7 +1472,10 @@ class EngineSemanticsOwner {
14931472 /// Updates the semantics tree from data in the [uiUpdate] .
14941473 void updateSemantics (ui.SemanticsUpdate uiUpdate) {
14951474 if (! _semanticsEnabled) {
1496- return ;
1475+ // If we're receiving a semantics update from the framework, it means the
1476+ // developer enabled it programmatically, so we enable it in the engine
1477+ // too.
1478+ semanticsEnabled = true ;
14971479 }
14981480
14991481 final SemanticsUpdate update = uiUpdate as SemanticsUpdate ;
@@ -1505,19 +1487,7 @@ class EngineSemanticsOwner {
15051487 if (_rootSemanticsElement == null ) {
15061488 final SemanticsObject root = _semanticsTree[0 ]! ;
15071489 _rootSemanticsElement = root.element;
1508- // We render semantics inside the glasspane for proper focus and event
1509- // handling. If semantics is behind the glasspane, the phone will disable
1510- // focusing by touch, only by tabbing around the UI. If semantics is in
1511- // front of glasspane, then DOM event won't bubble up to the glasspane so
1512- // it can forward events to the framework.
1513- //
1514- // We insert the semantics root before the scene host. For all widgets
1515- // in the scene, except for platform widgets, the scene host will pass the
1516- // pointer events through to the semantics tree. However, for platform
1517- // views, the pointer events will not pass through, and will be handled
1518- // by the platform view.
1519- domRenderer.glassPaneElement!
1520- .insertBefore (_rootSemanticsElement! , domRenderer.sceneHostElement);
1490+ domRenderer.semanticsHostElement! .append (root.element);
15211491 }
15221492
15231493 _finalizeTree ();
0 commit comments