Skip to content

Commit 2fbc99f

Browse files
authored
[web] - Move text editing nodes outside of shadowDOM, reland (flutter#40968)
[web] - Move text editing nodes outside of shadowDOM, reland
1 parent 8d57e6f commit 2fbc99f

File tree

13 files changed

+335
-308
lines changed

13 files changed

+335
-308
lines changed

lib/web_ui/lib/src/engine/embedder.dart

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import 'view_embedder/embedding_strategy/embedding_strategy.dart';
2424
/// Manages several top-level elements that host Flutter-generated content,
2525
/// including:
2626
///
27-
/// - [glassPaneElement], the root element of a Flutter view.
27+
/// - [flutterViewElement], the root element of a Flutter view.
28+
/// - [glassPaneElement], the glass pane element that hosts the shadowDOM.
2829
/// - [glassPaneShadow], the shadow root used to isolate Flutter-rendered
2930
/// content from the surrounding page content, including from the platform
3031
/// views.
@@ -62,7 +63,10 @@ class FlutterViewEmbedder {
6263
/// Abstracts all the DOM manipulations required to embed a Flutter app in an user-supplied `hostElement`.
6364
final EmbeddingStrategy _embeddingStrategy;
6465

65-
// The tag name for the root view of the flutter app (glass-pane)
66+
// The tag name for the Flutter View, which hosts the app.
67+
static const String flutterViewTagName = 'flutter-view';
68+
69+
// The tag name for the glass-pane.
6670
static const String glassPaneTagName = 'flt-glass-pane';
6771

6872
/// The element that contains the [sceneElement].
@@ -117,13 +121,19 @@ class FlutterViewEmbedder {
117121
/// which captures semantics input events. The semantics DOM tree must be a
118122
/// child of the glass pane element so that events bubble up to the glass pane
119123
/// if they are not handled by semantics.
124+
DomElement get flutterViewElement => _flutterViewElement;
125+
late DomElement _flutterViewElement;
126+
120127
DomElement get glassPaneElement => _glassPaneElement;
121128
late DomElement _glassPaneElement;
122129

123130
/// The [HostNode] of the [glassPaneElement], which contains the whole Flutter app.
124131
HostNode get glassPaneShadow => _glassPaneShadow;
125132
late HostNode _glassPaneShadow;
126133

134+
DomElement get textEditingHostNode => _textEditingHostNode;
135+
late DomElement _textEditingHostNode;
136+
127137
static const String defaultFontStyle = 'normal';
128138
static const String defaultFontWeight = 'normal';
129139
static const double defaultFontSize = 14;
@@ -149,14 +159,17 @@ class FlutterViewEmbedder {
149159
);
150160

151161
// Create and inject the [_glassPaneElement].
162+
_flutterViewElement = domDocument.createElement(flutterViewTagName);
152163
_glassPaneElement = domDocument.createElement(glassPaneTagName);
153164

165+
154166
// This must be attached to the DOM now, so the engine can create a host
155167
// node (ShadowDOM or a fallback) next.
156168
//
157169
// The embeddingStrategy will take care of cleaning up the glassPane on hot
158170
// restart.
159-
_embeddingStrategy.attachGlassPane(glassPaneElement);
171+
_embeddingStrategy.attachGlassPane(flutterViewElement);
172+
flutterViewElement.appendChild(glassPaneElement);
160173

161174
// Create a [HostNode] under the glass pane element, and attach everything
162175
// there, instead of directly underneath the glass panel.
@@ -168,6 +181,9 @@ class FlutterViewEmbedder {
168181
);
169182
_glassPaneShadow = glassPaneElementHostNode;
170183

184+
_textEditingHostNode =
185+
createTextEditingHostNode(flutterViewElement, defaultCssFont);
186+
171187
// Don't allow the scene to receive pointer events.
172188
_sceneHostElement = domDocument.createElement('flt-scene-host')
173189
..style.pointerEvents = 'none';
@@ -189,20 +205,20 @@ class FlutterViewEmbedder {
189205
glassPaneElementHostNode.appendAll(<DomNode>[
190206
accessibilityPlaceholder,
191207
_sceneHostElement!,
192-
193-
// The semantic host goes last because hit-test order-wise it must be
194-
// first. If semantics goes under the scene host, platform views will
195-
// obscure semantic elements.
196-
//
197-
// You may be wondering: wouldn't semantics obscure platform views and
198-
// make then not accessible? At least with some careful planning, that
199-
// should not be the case. The semantics tree makes all of its non-leaf
200-
// elements transparent. This way, if a platform view appears among other
201-
// interactive Flutter widgets, as long as those widgets do not intersect
202-
// with the platform view, the platform view will be reachable.
203-
semanticsHostElement,
204208
]);
205209

210+
// The semantic host goes last because hit-test order-wise it must be
211+
// first. If semantics goes under the scene host, platform views will
212+
// obscure semantic elements.
213+
//
214+
// You may be wondering: wouldn't semantics obscure platform views and
215+
// make then not accessible? At least with some careful planning, that
216+
// should not be the case. The semantics tree makes all of its non-leaf
217+
// elements transparent. This way, if a platform view appears among other
218+
// interactive Flutter widgets, as long as those widgets do not intersect
219+
// with the platform view, the platform view will be reachable.
220+
flutterViewElement.appendChild(semanticsHostElement);
221+
206222
// When debugging semantics, make the scene semi-transparent so that the
207223
// semantics tree is more prominent.
208224
if (configuration.debugShowSemanticsNodes) {
@@ -211,7 +227,7 @@ class FlutterViewEmbedder {
211227

212228
KeyboardBinding.initInstance();
213229
PointerBinding.initInstance(
214-
glassPaneElement,
230+
flutterViewElement,
215231
KeyboardBinding.instance!.converter,
216232
);
217233

@@ -336,7 +352,7 @@ class FlutterViewEmbedder {
336352
if (isWebKit) {
337353
// The resourcesHost *must* be a sibling of the glassPaneElement.
338354
_embeddingStrategy.attachResourcesHost(resourcesHost,
339-
nextTo: glassPaneElement);
355+
nextTo: flutterViewElement);
340356
} else {
341357
glassPaneShadow.node
342358
.insertBefore(resourcesHost, glassPaneShadow.node.firstChild);
@@ -393,3 +409,24 @@ FlutterViewEmbedder? _flutterViewEmbedder;
393409
FlutterViewEmbedder ensureFlutterViewEmbedderInitialized() =>
394410
_flutterViewEmbedder ??=
395411
FlutterViewEmbedder(hostElement: configuration.hostElement);
412+
413+
/// Creates a node to host text editing elements and applies a stylesheet
414+
/// to Flutter nodes that exist outside of the shadowDOM.
415+
DomElement createTextEditingHostNode(DomElement root, String defaultFont) {
416+
final DomElement domElement =
417+
domDocument.createElement('flt-text-editing-host');
418+
final DomHTMLStyleElement styleElement = createDomHTMLStyleElement();
419+
420+
styleElement.id = 'flt-text-editing-stylesheet';
421+
root.appendChild(styleElement);
422+
applyGlobalCssRulesToSheet(
423+
styleElement.sheet! as DomCSSStyleSheet,
424+
hasAutofillOverlay: browserHasAutofillOverlay(),
425+
cssSelectorPrefix: FlutterViewEmbedder.flutterViewTagName,
426+
defaultCssFont: defaultFont,
427+
);
428+
429+
root.appendChild(domElement);
430+
431+
return domElement;
432+
}

lib/web_ui/lib/src/engine/host_node.dart

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,8 @@ class ShadowDomHostNode implements HostNode {
110110
/// This also calls [applyGlobalCssRulesToSheet], with the [defaultFont]
111111
/// to be used as the default font definition.
112112
ShadowDomHostNode(DomElement root, String defaultFont)
113-
: assert(
114-
root.isConnected ?? true,
115-
'The `root` of a ShadowDomHostNode must be connected to the Document object or a ShadowRoot.'
116-
) {
113+
: assert(root.isConnected ?? true,
114+
'The `root` of a ShadowDomHostNode must be connected to the Document object or a ShadowRoot.') {
117115
_shadow = root.attachShadow(<String, dynamic>{
118116
'mode': 'open',
119117
// This needs to stay false to prevent issues like this:
@@ -181,7 +179,7 @@ class ElementHostNode implements HostNode {
181179
applyGlobalCssRulesToSheet(
182180
styleElement.sheet! as DomCSSStyleSheet,
183181
hasAutofillOverlay: browserHasAutofillOverlay(),
184-
cssSelectorPrefix: FlutterViewEmbedder.glassPaneTagName,
182+
cssSelectorPrefix: FlutterViewEmbedder.flutterViewTagName,
185183
defaultCssFont: defaultFont,
186184
);
187185

lib/web_ui/lib/src/engine/mouse_cursor.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ class MouseCursor {
6767

6868
void activateSystemCursor(String? kind) {
6969
setElementStyle(
70-
flutterViewEmbedder.glassPaneElement,
70+
flutterViewEmbedder.flutterViewElement,
7171
'cursor',
7272
_mapKindToCssValue(kind),
7373
);

lib/web_ui/lib/src/engine/platform_views/content_manager.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,9 @@ class PlatformViewManager {
128128
}
129129

130130
_ensureContentCorrectlySized(content, viewType);
131+
wrapper.append(content);
131132

132-
return wrapper..append(content);
133+
return wrapper;
133134
});
134135
}
135136

0 commit comments

Comments
 (0)