@@ -37,11 +37,15 @@ import 'window.dart';
3737/// - [semanticsHostElement] , hosts the ARIA-annotated semantics tree.
3838class FlutterViewEmbedder {
3939 FlutterViewEmbedder () {
40- reset ();
4140 assert (() {
4241 _setupHotRestart ();
4342 return true ;
4443 }());
44+ reset ();
45+ assert (() {
46+ _registerHotRestartCleanUp ();
47+ return true ;
48+ }());
4549 }
4650
4751 // The tag name for the root view of the flutter app (glass-pane)
@@ -83,7 +87,7 @@ class FlutterViewEmbedder {
8387 /// This element is created and inserted in the HTML DOM once. It is never
8488 /// removed or moved.
8589 ///
86- /// We render semantics inside the glasspane for proper focus and event
90+ /// Render semantics inside the glasspane for proper focus and event
8791 /// handling. If semantics is behind the glasspane, the phone will disable
8892 /// focusing by touch, only by tabbing around the UI. If semantics is in
8993 /// front of glasspane, then DOM event won't bubble up to the glasspane so
@@ -99,11 +103,15 @@ class FlutterViewEmbedder {
99103 html.Element ? _sceneElement;
100104
101105 /// This is state persistent across hot restarts that indicates what
102- /// to clear. We delay removal of old visible state to make the
106+ /// to clear. Delay removal of old visible state to make the
103107 /// transition appear smooth.
104108 static const String _staleHotRestartStore = '__flutter_state' ;
105109 List <html.Element ?>? _staleHotRestartState;
106110
111+ /// Creates a container for DOM elements that need to be cleaned up between
112+ /// hot restarts.
113+ ///
114+ /// If a contains already exists, reuses the existing one.
107115 void _setupHotRestart () {
108116 // This persists across hot restarts to clear stale DOM.
109117 _staleHotRestartState = getJsProperty< List <html.Element ?>? > (html.window, _staleHotRestartStore);
@@ -112,7 +120,12 @@ class FlutterViewEmbedder {
112120 setJsProperty (
113121 html.window, _staleHotRestartStore, _staleHotRestartState);
114122 }
123+ }
115124
125+ /// Registers DOM elements that need to be cleaned up before hot restarting.
126+ ///
127+ /// [_setupHotRestart] must have been called prior to calling this method.
128+ void _registerHotRestartCleanUp () {
116129 registerHotRestartListener (() {
117130 _resizeSubscription? .cancel ();
118131 _localeSubscription? .cancel ();
@@ -133,11 +146,11 @@ class FlutterViewEmbedder {
133146 }
134147 }
135148
136- /// We don't want to unnecessarily move DOM nodes around. If a DOM node is
149+ /// Don't unnecessarily move DOM nodes around. If a DOM node is
137150 /// already in the right place, skip DOM mutation. This is both faster and
138151 /// more correct, because moving DOM nodes loses internal state, such as
139152 /// text selection.
140- void renderScene (html.Element ? sceneElement) {
153+ void addSceneToSceneHost (html.Element ? sceneElement) {
141154 if (sceneElement != _sceneElement) {
142155 _sceneElement? .remove ();
143156 _sceneElement = sceneElement;
@@ -203,15 +216,15 @@ class FlutterViewEmbedder {
203216 setElementStyle (bodyElement, 'padding' , '0' );
204217 setElementStyle (bodyElement, 'margin' , '0' );
205218
206- // TODO(yjbanov): fix this when we support KVM I/O. Currently we scroll
219+ // TODO(yjbanov): fix this when KVM I/O support is added . Currently scroll
207220 // using drag, and text selection interferes.
208221 setElementStyle (bodyElement, 'user-select' , 'none' );
209222 setElementStyle (bodyElement, '-webkit-user-select' , 'none' );
210223 setElementStyle (bodyElement, '-ms-user-select' , 'none' );
211224 setElementStyle (bodyElement, '-moz-user-select' , 'none' );
212225
213226 // This is required to prevent the browser from doing any native touch
214- // handling. If we don't do this , the browser doesn't report 'pointermove'
227+ // handling. If this is not done , the browser doesn't report 'pointermove'
215228 // events properly.
216229 setElementStyle (bodyElement, 'touch-action' , 'none' );
217230
@@ -227,7 +240,7 @@ class FlutterViewEmbedder {
227240 for (final html.Element viewportMeta
228241 in html.document.head! .querySelectorAll ('meta[name="viewport"]' )) {
229242 if (assertionsEnabled) {
230- // Filter out the meta tag that we ourselves placed on the page. This is
243+ // Filter out the meta tag that the engine placed on the page. This is
231244 // to avoid UI flicker during hot restart. Hot restart will clean up the
232245 // old meta tag synchronously with the first post-restart frame.
233246 if (! viewportMeta.hasAttribute ('flt-viewport' )) {
@@ -265,7 +278,8 @@ class FlutterViewEmbedder {
265278 ..bottom = '0'
266279 ..left = '0' ;
267280
268- // This must be appended to the body, so we can create a host node properly.
281+ // This must be appended to the body, so the engine can create a host node
282+ // properly.
269283 bodyElement.append (glassPaneElement);
270284
271285 // Create a [HostNode] under the glass pane element, and attach everything
@@ -277,6 +291,14 @@ class FlutterViewEmbedder {
277291 _sceneHostElement = html.document.createElement ('flt-scene-host' )
278292 ..style.pointerEvents = 'none' ;
279293
294+ /// CanvasKit uses a static scene element that never gets replaced, so it's
295+ /// added eagerly during initialization here and never touched, unless the
296+ /// system is reset due to hot restart or in a test.
297+ if (useCanvasKit) {
298+ skiaSceneHost = html.Element .tag ('flt-scene' );
299+ addSceneToSceneHost (skiaSceneHost);
300+ }
301+
280302 final html.Element semanticsHostElement =
281303 html.document.createElement ('flt-semantics-host' );
282304 semanticsHostElement.style
@@ -290,25 +312,31 @@ class FlutterViewEmbedder {
290312 .prepareAccessibilityPlaceholder ();
291313
292314 glassPaneElementHostNode.nodes.addAll (< html.Node > [
293- semanticsHostElement,
294315 _accessibilityPlaceholder,
295316 _sceneHostElement! ,
317+
318+ // The semantic host goes last because hit-test order-wise it must be
319+ // first. If semantics goes under the scene host, platform views will
320+ // obscure semantic elements.
321+ //
322+ // You may be wondering: wouldn't semantics obscure platform views and
323+ // make then not accessible? At least with some careful planning, that
324+ // should not be the case. The semantics tree makes all of its non-leaf
325+ // elements transparent. This way, if a platform view appears among other
326+ // interactive Flutter widgets, as long as those widgets do not intersect
327+ // with the platform view, the platform view will be reachable.
328+ semanticsHostElement,
296329 ]);
297330
298331 // When debugging semantics, make the scene semi-transparent so that the
299- // semantics tree is visible .
332+ // semantics tree is more prominent .
300333 if (configuration.debugShowSemanticsNodes) {
301334 _sceneHostElement! .style.opacity = '0.3' ;
302335 }
303336
304337 PointerBinding .initInstance (glassPaneElement);
305338 KeyboardBinding .initInstance (glassPaneElement);
306339
307- // Hide the DOM nodes used to render the scene from accessibility, because
308- // the accessibility tree is built from the SemanticsNode tree as a parallel
309- // DOM tree.
310- _sceneHostElement! .setAttribute ('aria-hidden' , 'true' );
311-
312340 if (html.window.visualViewport == null && isWebKit) {
313341 // Older Safari versions sometimes give us bogus innerWidth/innerHeight
314342 // values when the page loads. When it changes the values to correct ones
@@ -321,10 +349,11 @@ class FlutterViewEmbedder {
321349 //
322350 // VisualViewport API is not enabled in Firefox as well. On the other hand
323351 // Firefox returns correct values for innerHeight, innerWidth.
324- // Firefox also triggers html.window.onResize therefore we don't need this
325- // timer to be set up for Firefox.
352+ // Firefox also triggers html.window.onResize therefore this timer does
353+ // not need to be set up for Firefox.
326354 final int initialInnerWidth = html.window.innerWidth! ;
327- // Counts how many times we checked screen size. We check up to 5 times.
355+ // Counts how many times screen size was checked. It is checked up to 5
356+ // times.
328357 int checkCount = 0 ;
329358 Timer .periodic (const Duration (milliseconds: 100 ), (Timer t) {
330359 checkCount += 1 ;
@@ -361,7 +390,7 @@ class FlutterViewEmbedder {
361390 }
362391
363392 /// The framework specifies semantics in physical pixels, but CSS uses
364- /// logical pixels. To compensate, we inject an inverse scale at the root
393+ /// logical pixels. To compensate, an inverse scale is injected at the root
365394 /// level.
366395 void updateSemanticsScreenProperties () {
367396 _semanticsHostElement! .style.transform =
0 commit comments