Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit d626f16

Browse files
authored
[web] Remove non-ShadowDom mode (#39915)
If we still want to do this, here's a quick PR :) Fixes flutter/flutter#116204
1 parent 1394832 commit d626f16

File tree

10 files changed

+361
-626
lines changed

10 files changed

+361
-626
lines changed

ci/licenses_golden/licenses_flutter

+2-2
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart + ../../..
19081908
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/font_change_util.dart + ../../../flutter/LICENSE
19091909
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/fonts.dart + ../../../flutter/LICENSE
19101910
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart + ../../../flutter/LICENSE
1911-
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/host_node.dart + ../../../flutter/LICENSE
1911+
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/global_styles.dart + ../../../flutter/LICENSE
19121912
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart + ../../../flutter/LICENSE
19131913
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart + ../../../flutter/LICENSE
19141914
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart + ../../../flutter/LICENSE
@@ -4505,7 +4505,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart
45054505
FILE: ../../../flutter/lib/web_ui/lib/src/engine/font_change_util.dart
45064506
FILE: ../../../flutter/lib/web_ui/lib/src/engine/fonts.dart
45074507
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart
4508-
FILE: ../../../flutter/lib/web_ui/lib/src/engine/host_node.dart
4508+
FILE: ../../../flutter/lib/web_ui/lib/src/engine/global_styles.dart
45094509
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
45104510
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart
45114511
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart

lib/web_ui/lib/src/engine.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export 'engine/engine_canvas.dart';
6565
export 'engine/font_change_util.dart';
6666
export 'engine/fonts.dart';
6767
export 'engine/frame_reference.dart';
68-
export 'engine/host_node.dart';
68+
export 'engine/global_styles.dart';
6969
export 'engine/html/backdrop_filter.dart';
7070
export 'engine/html/bitmap_canvas.dart';
7171
export 'engine/html/canvas.dart';

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

+29-17
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
import 'dart:async';
66

7+
import 'package:ui/src/engine/safe_browser_api.dart';
78
import 'package:ui/ui.dart' as ui;
89

910
import '../engine.dart' show buildMode, renderer, window;
1011
import 'browser_detection.dart';
1112
import 'configuration.dart';
1213
import 'dom.dart';
13-
import 'host_node.dart';
14+
import 'global_styles.dart';
1415
import 'keyboard_binding.dart';
1516
import 'platform_dispatcher.dart';
1617
import 'pointer_binding.dart';
@@ -127,9 +128,9 @@ class FlutterViewEmbedder {
127128
DomElement get glassPaneElement => _glassPaneElement;
128129
late DomElement _glassPaneElement;
129130

130-
/// The [HostNode] of the [glassPaneElement], which contains the whole Flutter app.
131-
HostNode get glassPaneShadow => _glassPaneShadow;
132-
late HostNode _glassPaneShadow;
131+
/// The shadow root of the [glassPaneElement], which contains the whole Flutter app.
132+
DomShadowRoot get glassPaneShadow => _glassPaneShadow;
133+
late DomShadowRoot _glassPaneShadow;
133134

134135
DomElement get textEditingHostNode => _textEditingHostNode;
135136
late DomElement _textEditingHostNode;
@@ -171,15 +172,29 @@ class FlutterViewEmbedder {
171172
_embeddingStrategy.attachGlassPane(flutterViewElement);
172173
flutterViewElement.appendChild(glassPaneElement);
173174

175+
if (getJsProperty<Object?>(glassPaneElement, 'attachShadow') == null) {
176+
throw UnsupportedError('ShadowDOM is not supported in this browser.');
177+
}
178+
174179
// Create a [HostNode] under the glass pane element, and attach everything
175180
// there, instead of directly underneath the glass panel.
176-
//
177-
// TODO(dit): clean HostNode, https://github.com/flutter/flutter/issues/116204
178-
final HostNode glassPaneElementHostNode = HostNode.create(
179-
glassPaneElement,
180-
defaultCssFont,
181+
final DomShadowRoot shadowRoot = glassPaneElement.attachShadow(<String, dynamic>{
182+
'mode': 'open',
183+
// This needs to stay false to prevent issues like this:
184+
// - https://github.com/flutter/flutter/issues/85759
185+
'delegatesFocus': false,
186+
});
187+
_glassPaneShadow = shadowRoot;
188+
189+
final DomHTMLStyleElement shadowRootStyleElement = createDomHTMLStyleElement();
190+
shadowRootStyleElement.id = 'flt-internals-stylesheet';
191+
// The shadowRootStyleElement must be appended to the DOM, or its `sheet` will be null later.
192+
shadowRoot.appendChild(shadowRootStyleElement);
193+
applyGlobalCssRulesToSheet(
194+
shadowRootStyleElement,
195+
hasAutofillOverlay: browserHasAutofillOverlay(),
196+
defaultCssFont: defaultCssFont,
181197
);
182-
_glassPaneShadow = glassPaneElementHostNode;
183198

184199
_textEditingHostNode =
185200
createTextEditingHostNode(flutterViewElement, defaultCssFont);
@@ -202,10 +217,8 @@ class FlutterViewEmbedder {
202217
.instance.semanticsHelper
203218
.prepareAccessibilityPlaceholder();
204219

205-
glassPaneElementHostNode.appendAll(<DomNode>[
206-
accessibilityPlaceholder,
207-
_sceneHostElement!,
208-
]);
220+
shadowRoot.append(accessibilityPlaceholder);
221+
shadowRoot.append(_sceneHostElement!);
209222

210223
// The semantic host goes last because hit-test order-wise it must be
211224
// first. If semantics goes under the scene host, platform views will
@@ -354,8 +367,7 @@ class FlutterViewEmbedder {
354367
_embeddingStrategy.attachResourcesHost(resourcesHost,
355368
nextTo: flutterViewElement);
356369
} else {
357-
glassPaneShadow.node
358-
.insertBefore(resourcesHost, glassPaneShadow.node.firstChild);
370+
glassPaneShadow.insertBefore(resourcesHost, glassPaneShadow.firstChild);
359371
}
360372
_resourcesHost = resourcesHost;
361373
}
@@ -420,7 +432,7 @@ DomElement createTextEditingHostNode(DomElement root, String defaultFont) {
420432
styleElement.id = 'flt-text-editing-stylesheet';
421433
root.appendChild(styleElement);
422434
applyGlobalCssRulesToSheet(
423-
styleElement.sheet! as DomCSSStyleSheet,
435+
styleElement,
424436
hasAutofillOverlay: browserHasAutofillOverlay(),
425437
cssSelectorPrefix: FlutterViewEmbedder.flutterViewTagName,
426438
defaultCssFont: defaultFont,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
// Copyright 2013 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 'browser_detection.dart';
6+
import 'dom.dart';
7+
import 'text_editing/text_editing.dart';
8+
9+
// Applies the required global CSS to an incoming [DomCSSStyleSheet] `sheet`.
10+
void applyGlobalCssRulesToSheet(
11+
DomHTMLStyleElement styleElement, {
12+
required bool hasAutofillOverlay,
13+
String cssSelectorPrefix = '',
14+
required String defaultCssFont,
15+
}) {
16+
// TODO(web): use more efficient CSS selectors; descendant selectors are slow.
17+
// More info: https://csswizardry.com/2011/09/writing-efficient-css-selectors
18+
19+
assert(styleElement.sheet != null);
20+
final DomCSSStyleSheet sheet = styleElement.sheet! as DomCSSStyleSheet;
21+
22+
// These are intentionally outrageous font parameters to make sure that the
23+
// apps fully specify their text styles.
24+
//
25+
// Fixes #115216 by ensuring that our parameters only affect the flt-scene-host children.
26+
sheet.insertRule('''
27+
$cssSelectorPrefix flt-scene-host {
28+
color: red;
29+
font: $defaultCssFont;
30+
}
31+
''', sheet.cssRules.length);
32+
33+
// By default on iOS, Safari would highlight the element that's being tapped
34+
// on using gray background. This CSS rule disables that.
35+
if (isSafari) {
36+
sheet.insertRule('''
37+
$cssSelectorPrefix * {
38+
-webkit-tap-highlight-color: transparent;
39+
}
40+
''', sheet.cssRules.length);
41+
}
42+
43+
if (isFirefox) {
44+
// For firefox set line-height, otherwise text at same font-size will
45+
// measure differently in ruler.
46+
//
47+
// - See: https://github.com/flutter/flutter/issues/44803
48+
sheet.insertRule('''
49+
$cssSelectorPrefix flt-paragraph,
50+
$cssSelectorPrefix flt-span {
51+
line-height: 100%;
52+
}
53+
''', sheet.cssRules.length);
54+
}
55+
56+
// This undoes browser's default painting and layout attributes of range
57+
// input, which is used in semantics.
58+
sheet.insertRule('''
59+
$cssSelectorPrefix flt-semantics input[type=range] {
60+
appearance: none;
61+
-webkit-appearance: none;
62+
width: 100%;
63+
position: absolute;
64+
border: none;
65+
top: 0;
66+
right: 0;
67+
bottom: 0;
68+
left: 0;
69+
}
70+
''', sheet.cssRules.length);
71+
72+
if (isSafari) {
73+
sheet.insertRule('''
74+
$cssSelectorPrefix flt-semantics input[type=range]::-webkit-slider-thumb {
75+
-webkit-appearance: none;
76+
}
77+
''', sheet.cssRules.length);
78+
}
79+
80+
// The invisible semantic text field may have a visible cursor and selection
81+
// highlight. The following 2 CSS rules force everything to be transparent.
82+
sheet.insertRule('''
83+
$cssSelectorPrefix input::selection {
84+
background-color: transparent;
85+
}
86+
''', sheet.cssRules.length);
87+
sheet.insertRule('''
88+
$cssSelectorPrefix textarea::selection {
89+
background-color: transparent;
90+
}
91+
''', sheet.cssRules.length);
92+
93+
sheet.insertRule('''
94+
$cssSelectorPrefix flt-semantics input,
95+
$cssSelectorPrefix flt-semantics textarea,
96+
$cssSelectorPrefix flt-semantics [contentEditable="true"] {
97+
caret-color: transparent;
98+
}
99+
''', sheet.cssRules.length);
100+
101+
// Hide placeholder text
102+
sheet.insertRule('''
103+
$cssSelectorPrefix .flt-text-editing::placeholder {
104+
opacity: 0;
105+
}
106+
''', sheet.cssRules.length);
107+
108+
// This CSS makes the autofill overlay transparent in order to prevent it
109+
// from overlaying on top of Flutter-rendered text inputs.
110+
// See: https://github.com/flutter/flutter/issues/118337.
111+
if (browserHasAutofillOverlay()) {
112+
sheet.insertRule('''
113+
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill,
114+
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:hover,
115+
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:focus,
116+
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:active {
117+
opacity: 0 !important;
118+
}
119+
''', sheet.cssRules.length);
120+
}
121+
122+
// Removes password reveal icon for text inputs in Edge browsers.
123+
// Non-Edge browsers will crash trying to parse -ms-reveal CSS selector,
124+
// so we guard it behind an isEdge check.
125+
// Fixes: https://github.com/flutter/flutter/issues/83695
126+
if (isEdge) {
127+
// We try-catch this, because in testing, we fake Edge via the UserAgent,
128+
// so the below will throw an exception (because only real Edge understands
129+
// the ::-ms-reveal pseudo-selector).
130+
try {
131+
sheet.insertRule('''
132+
$cssSelectorPrefix input::-ms-reveal {
133+
display: none;
134+
}
135+
''', sheet.cssRules.length);
136+
} on DomException catch (e) {
137+
// Browsers that don't understand ::-ms-reveal throw a DOMException
138+
// of type SyntaxError.
139+
domWindow.console.warn(e);
140+
// Add a fake rule if our code failed because we're under testing
141+
assert(() {
142+
sheet.insertRule('''
143+
$cssSelectorPrefix input.fallback-for-fakey-browser-in-ci {
144+
display: none;
145+
}
146+
''', sheet.cssRules.length);
147+
return true;
148+
}());
149+
}
150+
}
151+
}

0 commit comments

Comments
 (0)