mirror of
https://github.com/google/blockly.git
synced 2026-06-03 09:50:06 +02:00
fix: CSS back to <style> for Safari/cascade reasons (#9908)
Reverts the storage mechanism introduced in #9611 (constructable stylesheets via `adoptedStyleSheets`) while keeping the per-root injection-site tracking that #9611 added for shadow-DOM support. Motivations: - Safari 15.4 compatibility. `new CSSStyleSheet()` and `adoptedStyleSheets` require Safari 16.4+ - Cascade order. `adoptedStyleSheets` apply after `<style>`/`<link>` elements in the document, so Blockly's defaults silently overrode host stylesheets. Prepending a `<style>` to the head (or to the shadow root) restores the pre-#9611 behavior where any author stylesheet declared later wins on specificity ties. Trade-offs: - Per-shadow-root CSS text is duplicated rather than shared via a single adopted sheet object. Negligible for typical use. - `Css.register()` calls made after the first `inject()` no longer reach already-injected roots (same as #9611's behavior); subsequent `inject()` calls into other roots still pick them up. Web-component consumers can legitimately register late, so this is preferred to reinstating the pre-#9611 throw. Fixes #9876
This commit is contained in:
@@ -5,9 +5,8 @@
|
||||
*/
|
||||
|
||||
// Former goog.module ID: Blockly.Css
|
||||
/** Has CSS already been injected? */
|
||||
const injectionSites = new WeakSet<Document | ShadowRoot>();
|
||||
const registeredStyleSheets: Array<CSSStyleSheet> = [];
|
||||
const registeredCss: Array<string> = [];
|
||||
import * as userAgent from './utils/useragent.js';
|
||||
|
||||
/**
|
||||
@@ -17,11 +16,7 @@ import * as userAgent from './utils/useragent.js';
|
||||
* @param cssContent Multiline CSS string or an array of single lines of CSS.
|
||||
*/
|
||||
export function register(cssContent: string) {
|
||||
if (typeof window === 'undefined' || !window.CSSStyleSheet) return;
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
sheet.replace(cssContent);
|
||||
registeredStyleSheets.push(sheet);
|
||||
registeredCss.push(cssContent);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,24 +36,26 @@ export function inject(
|
||||
hasCss: boolean,
|
||||
pathToMedia: string,
|
||||
) {
|
||||
if (!hasCss || typeof window === 'undefined' || !window.CSSStyleSheet) {
|
||||
return;
|
||||
}
|
||||
if (!hasCss || typeof window === 'undefined') return;
|
||||
|
||||
const root = container.getRootNode() as Document | ShadowRoot;
|
||||
// Only inject the CSS once.
|
||||
if (injectionSites.has(root)) return;
|
||||
injectionSites.add(root);
|
||||
|
||||
// Strip off any trailing slash (either Unix or Windows).
|
||||
const mediaPath = pathToMedia.replace(/[\\/]$/, '');
|
||||
const cssContent = content.replace(/<<<PATH>>>/g, mediaPath);
|
||||
const cssText = [content, ...registeredCss]
|
||||
.join('\n')
|
||||
.replace(/<<<PATH>>>/g, mediaPath);
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
sheet.replace(cssContent);
|
||||
root.adoptedStyleSheets.push(sheet);
|
||||
|
||||
registeredStyleSheets.forEach((sheet) => root.adoptedStyleSheets.push(sheet));
|
||||
const styleEl = document.createElement('style');
|
||||
styleEl.id = 'blockly-common-style';
|
||||
styleEl.textContent = cssText;
|
||||
// Prepend so Blockly's rules sit at the start of the cascade; any user
|
||||
// stylesheet declared later wins by document order. Style elements appended
|
||||
// to the light DOM don't apply inside shadow roots, so for the shadow DOM
|
||||
// case we prepend the style element to the shadow root itself.
|
||||
(root instanceof ShadowRoot ? root : document.head).prepend(styleEl);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1126,17 +1126,15 @@ export class ConstantProvider {
|
||||
* @param selector The CSS selector to interpolate into the stylesheet.
|
||||
*/
|
||||
protected injectCSS_(root: Document | ShadowRoot, selector: string) {
|
||||
if (
|
||||
typeof window === 'undefined' ||
|
||||
!window.CSSStyleSheet ||
|
||||
injectionSites.get(selector)?.has(root)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (typeof window === 'undefined') return;
|
||||
if (injectionSites.get(selector)?.has(root)) return;
|
||||
|
||||
const sheet = new CSSStyleSheet();
|
||||
sheet.replace(this.getCSS_(selector).join('\n'));
|
||||
root.adoptedStyleSheets.push(sheet);
|
||||
const styleEl = document.createElement('style');
|
||||
styleEl.className = 'blockly-renderer-style';
|
||||
styleEl.textContent = this.getCSS_(selector).join('\n');
|
||||
// See css.ts inject() for the rationale on prepending and shadow root
|
||||
// handling.
|
||||
(root instanceof ShadowRoot ? root : document.head).prepend(styleEl);
|
||||
|
||||
const sitesForSelector =
|
||||
injectionSites.get(selector) ?? new WeakSet<Document | ShadowRoot>();
|
||||
|
||||
Reference in New Issue
Block a user