Skip to content

Commit

Permalink
[web] Remove non-ShadowDom mode (#39915)
Browse files Browse the repository at this point in the history
If we still want to do this, here's a quick PR :)

Fixes flutter/flutter#116204
  • Loading branch information
mdebbar committed Apr 19, 2023
1 parent 1394832 commit d626f16
Show file tree
Hide file tree
Showing 10 changed files with 361 additions and 626 deletions.
4 changes: 2 additions & 2 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -1908,7 +1908,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 @@ -4505,7 +4505,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 @@ -65,7 +65,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;
}());
}
}
}

0 comments on commit d626f16

Please sign in to comment.