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

[web] Remove non-ShadowDom mode #39915

Merged
merged 10 commits into from
Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1902,7 +1902,7 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart + ../../..
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/font_change_util.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/fonts.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/host_node.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/global_styles.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart + ../../../flutter/LICENSE
ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart + ../../../flutter/LICENSE
Expand Down Expand Up @@ -4489,7 +4489,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/font_change_util.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/fonts.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/host_node.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/global_styles.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/bitmap_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export 'engine/engine_canvas.dart';
export 'engine/font_change_util.dart';
export 'engine/fonts.dart';
export 'engine/frame_reference.dart';
export 'engine/host_node.dart';
export 'engine/global_styles.dart';
export 'engine/html/backdrop_filter.dart';
export 'engine/html/bitmap_canvas.dart';
export 'engine/html/canvas.dart';
Expand Down
46 changes: 29 additions & 17 deletions lib/web_ui/lib/src/engine/embedder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

import 'dart:async';

import 'package:ui/src/engine/safe_browser_api.dart';
import 'package:ui/ui.dart' as ui;

import '../engine.dart' show buildMode, renderer, window;
import 'browser_detection.dart';
import 'configuration.dart';
import 'dom.dart';
import 'host_node.dart';
import 'global_styles.dart';
import 'keyboard_binding.dart';
import 'platform_dispatcher.dart';
import 'pointer_binding.dart';
Expand Down Expand Up @@ -127,9 +128,9 @@ class FlutterViewEmbedder {
DomElement get glassPaneElement => _glassPaneElement;
late DomElement _glassPaneElement;

/// The [HostNode] of the [glassPaneElement], which contains the whole Flutter app.
HostNode get glassPaneShadow => _glassPaneShadow;
late HostNode _glassPaneShadow;
/// The shadow root of the [glassPaneElement], which contains the whole Flutter app.
DomShadowRoot get glassPaneShadow => _glassPaneShadow;
late DomShadowRoot _glassPaneShadow;

DomElement get textEditingHostNode => _textEditingHostNode;
late DomElement _textEditingHostNode;
Expand Down Expand Up @@ -171,15 +172,29 @@ class FlutterViewEmbedder {
_embeddingStrategy.attachGlassPane(flutterViewElement);
flutterViewElement.appendChild(glassPaneElement);

if (getJsProperty<Object?>(glassPaneElement, 'attachShadow') == null) {
throw UnsupportedError('ShadowDOM is not supported in this browser.');
}

// Create a [HostNode] under the glass pane element, and attach everything
// there, instead of directly underneath the glass panel.
//
// TODO(dit): clean HostNode, https://github.com/flutter/flutter/issues/116204
final HostNode glassPaneElementHostNode = HostNode.create(
glassPaneElement,
defaultCssFont,
final DomShadowRoot shadowRoot = glassPaneElement.attachShadow(<String, dynamic>{
'mode': 'open',
// This needs to stay false to prevent issues like this:
// - https://github.com/flutter/flutter/issues/85759
'delegatesFocus': false,
});
_glassPaneShadow = shadowRoot;

final DomHTMLStyleElement shadowRootStyleElement = createDomHTMLStyleElement();
shadowRootStyleElement.id = 'flt-internals-stylesheet';
// The shadowRootStyleElement must be appended to the DOM, or its `sheet` will be null later.
shadowRoot.appendChild(shadowRootStyleElement);
applyGlobalCssRulesToSheet(
shadowRootStyleElement,
hasAutofillOverlay: browserHasAutofillOverlay(),
defaultCssFont: defaultCssFont,
);
_glassPaneShadow = glassPaneElementHostNode;

_textEditingHostNode =
createTextEditingHostNode(flutterViewElement, defaultCssFont);
Expand All @@ -202,10 +217,8 @@ class FlutterViewEmbedder {
.instance.semanticsHelper
.prepareAccessibilityPlaceholder();

glassPaneElementHostNode.appendAll(<DomNode>[
accessibilityPlaceholder,
_sceneHostElement!,
]);
shadowRoot.append(accessibilityPlaceholder);
shadowRoot.append(_sceneHostElement!);

// The semantic host goes last because hit-test order-wise it must be
// first. If semantics goes under the scene host, platform views will
Expand Down Expand Up @@ -354,8 +367,7 @@ class FlutterViewEmbedder {
_embeddingStrategy.attachResourcesHost(resourcesHost,
nextTo: flutterViewElement);
} else {
glassPaneShadow.node
.insertBefore(resourcesHost, glassPaneShadow.node.firstChild);
glassPaneShadow.insertBefore(resourcesHost, glassPaneShadow.firstChild);
}
_resourcesHost = resourcesHost;
}
Expand Down Expand Up @@ -420,7 +432,7 @@ DomElement createTextEditingHostNode(DomElement root, String defaultFont) {
styleElement.id = 'flt-text-editing-stylesheet';
root.appendChild(styleElement);
applyGlobalCssRulesToSheet(
styleElement.sheet! as DomCSSStyleSheet,
styleElement,
hasAutofillOverlay: browserHasAutofillOverlay(),
cssSelectorPrefix: FlutterViewEmbedder.flutterViewTagName,
defaultCssFont: defaultFont,
Expand Down
151 changes: 151 additions & 0 deletions lib/web_ui/lib/src/engine/global_styles.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// 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.

import 'browser_detection.dart';
import 'dom.dart';
import 'text_editing/text_editing.dart';

// Applies the required global CSS to an incoming [DomCSSStyleSheet] `sheet`.
void applyGlobalCssRulesToSheet(
DomHTMLStyleElement styleElement, {
required bool hasAutofillOverlay,
String cssSelectorPrefix = '',
required String defaultCssFont,
}) {
// TODO(web): use more efficient CSS selectors; descendant selectors are slow.
// More info: https://csswizardry.com/2011/09/writing-efficient-css-selectors

assert(styleElement.sheet != null);
final DomCSSStyleSheet sheet = styleElement.sheet! as DomCSSStyleSheet;

// These are intentionally outrageous font parameters to make sure that the
// apps fully specify their text styles.
//
// Fixes #115216 by ensuring that our parameters only affect the flt-scene-host children.
sheet.insertRule('''
$cssSelectorPrefix flt-scene-host {
color: red;
font: $defaultCssFont;
}
''', sheet.cssRules.length);

// By default on iOS, Safari would highlight the element that's being tapped
// on using gray background. This CSS rule disables that.
if (isSafari) {
sheet.insertRule('''
$cssSelectorPrefix * {
-webkit-tap-highlight-color: transparent;
}
''', sheet.cssRules.length);
}

if (isFirefox) {
// For firefox set line-height, otherwise text at same font-size will
// measure differently in ruler.
//
// - See: https://github.com/flutter/flutter/issues/44803
sheet.insertRule('''
$cssSelectorPrefix flt-paragraph,
$cssSelectorPrefix flt-span {
line-height: 100%;
}
''', sheet.cssRules.length);
}

// This undoes browser's default painting and layout attributes of range
// input, which is used in semantics.
sheet.insertRule('''
$cssSelectorPrefix flt-semantics input[type=range] {
appearance: none;
-webkit-appearance: none;
width: 100%;
position: absolute;
border: none;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
''', sheet.cssRules.length);

if (isSafari) {
sheet.insertRule('''
$cssSelectorPrefix flt-semantics input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
}
''', sheet.cssRules.length);
}

// The invisible semantic text field may have a visible cursor and selection
// highlight. The following 2 CSS rules force everything to be transparent.
sheet.insertRule('''
$cssSelectorPrefix input::selection {
background-color: transparent;
}
''', sheet.cssRules.length);
sheet.insertRule('''
$cssSelectorPrefix textarea::selection {
background-color: transparent;
}
''', sheet.cssRules.length);

sheet.insertRule('''
$cssSelectorPrefix flt-semantics input,
$cssSelectorPrefix flt-semantics textarea,
$cssSelectorPrefix flt-semantics [contentEditable="true"] {
caret-color: transparent;
}
''', sheet.cssRules.length);

// Hide placeholder text
sheet.insertRule('''
$cssSelectorPrefix .flt-text-editing::placeholder {
opacity: 0;
}
''', sheet.cssRules.length);

// This CSS makes the autofill overlay transparent in order to prevent it
// from overlaying on top of Flutter-rendered text inputs.
// See: https://github.com/flutter/flutter/issues/118337.
if (browserHasAutofillOverlay()) {
sheet.insertRule('''
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill,
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:hover,
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:focus,
$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:active {
opacity: 0 !important;
}
''', sheet.cssRules.length);
}

// Removes password reveal icon for text inputs in Edge browsers.
// Non-Edge browsers will crash trying to parse -ms-reveal CSS selector,
// so we guard it behind an isEdge check.
// Fixes: https://github.com/flutter/flutter/issues/83695
if (isEdge) {
// We try-catch this, because in testing, we fake Edge via the UserAgent,
// so the below will throw an exception (because only real Edge understands
// the ::-ms-reveal pseudo-selector).
try {
sheet.insertRule('''
$cssSelectorPrefix input::-ms-reveal {
display: none;
}
''', sheet.cssRules.length);
} on DomException catch (e) {
// Browsers that don't understand ::-ms-reveal throw a DOMException
// of type SyntaxError.
domWindow.console.warn(e);
// Add a fake rule if our code failed because we're under testing
assert(() {
sheet.insertRule('''
$cssSelectorPrefix input.fallback-for-fakey-browser-in-ci {
display: none;
}
''', sheet.cssRules.length);
return true;
}());
}
}
}
Loading