From 806cba2b755a93ac2b083cf87fb50e8b84947dae Mon Sep 17 00:00:00 2001 From: Hofer Ivan Date: Tue, 11 Jan 2022 20:54:07 +0100 Subject: [PATCH] [fix] ensure style manager instances don't conflict with each other (#7114) --- src/runtime/internal/dom.ts | 2 +- src/runtime/internal/style_manager.ts | 35 ++++++++++++++++----------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index 972b1ff537..287c16b5fc 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -157,7 +157,7 @@ export function get_root_for_style(node: Node): ShadowRoot | Document { export function append_empty_stylesheet(node: Node) { const style_element = element('style') as HTMLStyleElement; append_stylesheet(get_root_for_style(node), style_element); - return style_element; + return style_element.sheet as CSSStyleSheet; } function append_stylesheet(node: ShadowRoot | Document, style: HTMLStyleElement) { diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index 0993b3bf18..6907e5af02 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,12 +1,14 @@ import { append_empty_stylesheet, get_root_for_style } from './dom'; import { raf } from './environment'; -interface ExtendedDoc extends Document { - __svelte_stylesheet: CSSStyleSheet; - __svelte_rules: Record; +interface StyleInformation { + stylesheet: CSSStyleSheet; + rules: Record; } -const active_docs = new Set(); +// we need to store the information for multiple documents because a Svelte application could also contain iframes +// https://github.com/sveltejs/svelte/issues/3624 +const managed_styles = new Map(); let active = 0; // https://github.com/darkskyapp/string-hash/blob/master/index.js @@ -18,6 +20,12 @@ function hash(str: string) { return hash >>> 0; } +function create_style_information(doc: Document | ShadowRoot, node: Element & ElementCSSInlineStyle) { + const info = { stylesheet: append_empty_stylesheet(node), rules: {} }; + managed_styles.set(doc, info); + return info; +} + export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: number, duration: number, delay: number, ease: (t: number) => number, fn: (t: number, u: number) => string, uid: number = 0) { const step = 16.666 / duration; let keyframes = '{\n'; @@ -29,13 +37,12 @@ export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; const name = `__svelte_${hash(rule)}_${uid}`; - const doc = get_root_for_style(node) as ExtendedDoc; - active_docs.add(doc); - const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = append_empty_stylesheet(node).sheet as CSSStyleSheet); - const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); + const doc = get_root_for_style(node); + + const { stylesheet, rules } = managed_styles.get(doc) || create_style_information(doc, node); - if (!current_rules[name]) { - current_rules[name] = true; + if (!rules[name]) { + rules[name] = true; stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); } @@ -63,12 +70,12 @@ export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string export function clear_rules() { raf(() => { if (active) return; - active_docs.forEach(doc => { - const stylesheet = doc.__svelte_stylesheet; + managed_styles.forEach(info => { + const { stylesheet } = info; let i = stylesheet.cssRules.length; while (i--) stylesheet.deleteRule(i); - doc.__svelte_rules = {}; + info.rules = {}; }); - active_docs.clear(); + managed_styles.clear(); }); }