diff --git a/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart b/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart index 5be8ed3225dad..3cdff41224b97 100644 --- a/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart +++ b/lib/web_ui/lib/src/engine/view_embedder/style_manager.dart @@ -78,27 +78,59 @@ void applyGlobalCssRulesToSheet( 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; - - // Fixes #115216 by ensuring that our parameters only affect the flt-scene-host children. - sheet.insertRule(''' - $cssSelectorPrefix ${DomManager.sceneHostTagName} { - font: $defaultCssFont; - } - ''', sheet.cssRules.length); + styleElement.appendText( + // Fixes #115216 by ensuring that our parameters only affect the flt-scene-host children. + '$cssSelectorPrefix ${DomManager.sceneHostTagName} {' + ' font: $defaultCssFont;' + '}' + + // This undoes browser's default painting and layout attributes of range + // input, which is used in semantics. + '$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;' + '}' + + // The invisible semantic text field may have a visible cursor and selection + // highlight. The following 2 CSS rules force everything to be transparent. + '$cssSelectorPrefix input::selection {' + ' background-color: transparent;' + '}' + '$cssSelectorPrefix textarea::selection {' + ' background-color: transparent;' + '}' + + '$cssSelectorPrefix flt-semantics input,' + '$cssSelectorPrefix flt-semantics textarea,' + '$cssSelectorPrefix flt-semantics [contentEditable="true"] {' + ' caret-color: transparent;' + '}' + + // Hide placeholder text + '$cssSelectorPrefix .flt-text-editing::placeholder {' + ' opacity: 0;' + '}', + ); // 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); + styleElement.appendText( + '$cssSelectorPrefix * {' + ' -webkit-tap-highlight-color: transparent;' + '}' + + '$cssSelectorPrefix flt-semantics input[type=range]::-webkit-slider-thumb {' + ' -webkit-appearance: none;' + '}' + ); } if (isFirefox) { @@ -106,78 +138,26 @@ void applyGlobalCssRulesToSheet( // 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); + styleElement.appendText( + '$cssSelectorPrefix flt-paragraph,' + '$cssSelectorPrefix flt-span {' + ' line-height: 100%;' + '}' + ); } - // 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); + styleElement.appendText( + '$cssSelectorPrefix .transparentTextEditing:-webkit-autofill,' + '$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:hover,' + '$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:focus,' + '$cssSelectorPrefix .transparentTextEditing:-webkit-autofill:active {' + ' opacity: 0 !important;' + '}' + ); } // Removes password reveal icon for text inputs in Edge browsers. @@ -189,22 +169,22 @@ void applyGlobalCssRulesToSheet( // 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); + styleElement.appendText( + '$cssSelectorPrefix input::-ms-reveal {' + ' display: none;' + '}' + ); } 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); + styleElement.appendText( + '$cssSelectorPrefix input.fallback-for-fakey-browser-in-ci {' + ' display: none;' + '}' + ); return true; }()); } diff --git a/lib/web_ui/test/engine/global_styles_test.dart b/lib/web_ui/test/engine/global_styles_test.dart index ec801bd94839d..f87dd1633dbc3 100644 --- a/lib/web_ui/test/engine/global_styles_test.dart +++ b/lib/web_ui/test/engine/global_styles_test.dart @@ -17,7 +17,6 @@ void testMain() { setUp(() { styleElement = createDomHTMLStyleElement(null); - domDocument.body!.append(styleElement); applyGlobalCssRulesToSheet( styleElement, defaultCssFont: _kDefaultCssFont, @@ -101,6 +100,7 @@ bool hasCssRule( required String selector, required String declaration, }) { + domDocument.body!.append(styleElement); assert(styleElement.sheet != null); // regexr.com/740ff @@ -110,7 +110,10 @@ bool hasCssRule( final DomCSSStyleSheet sheet = styleElement.sheet! as DomCSSStyleSheet; // Check that the cssText of any rule matches the ruleLike RegExp. - return sheet.cssRules + final bool result = sheet.cssRules .map((DomCSSRule rule) => rule.cssText) .any((String rule) => ruleLike.hasMatch(rule)); + + styleElement.remove(); + return result; }