1111// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
1212
1313use std:: cell:: RefCell ;
14- use std:: collections:: BTreeMap ;
14+ use std:: collections:: { BTreeMap , BTreeSet } ;
1515use std:: ops:: Deref ;
1616use std:: rc:: Rc ;
1717
18- use perspective_js:: utils:: global;
1918use wasm_bindgen:: JsCast ;
20- use web_sys:: HtmlStyleElement ;
2119
2220use crate :: * ;
2321
@@ -28,6 +26,17 @@ type CSSResource = (&'static str, &'static str);
2826/// their styles registered).
2927static DOM_STYLES : & [ CSSResource ] = & [ css ! ( "dom/checkbox" ) , css ! ( "dom/select" ) ] ;
3028
29+ thread_local ! {
30+ /// Cache of `CssStyleSheet` objects, which can safely be re-used across
31+ /// `HTMLPerspectiveViewerElement` instances
32+ static STYLE_SHEET_CACHE : RefCell <BTreeMap <& ' static str , web_sys:: CssStyleSheet >> = RefCell :: new(
33+ DOM_STYLES
34+ . iter( )
35+ . map( |x| ( x. 0 , StyleCache :: into_style( x. 1 ) ) )
36+ . collect( ) ,
37+ ) ;
38+ }
39+
3140/// A state object for `<style>` snippets used by a yew `Component` with a
3241/// `<StyleProvider>` at the root.
3342#[ derive( Clone ) ]
@@ -48,8 +57,8 @@ impl PartialEq for StyleCache {
4857}
4958
5059impl StyleCache {
51- pub fn new ( is_shadow : bool ) -> StyleCache {
52- StyleCache ( Rc :: new ( StyleCacheData :: new ( is_shadow) ) )
60+ pub fn new ( is_shadow : bool , elem : & web_sys :: HtmlElement ) -> StyleCache {
61+ StyleCache ( Rc :: new ( StyleCacheData :: new ( is_shadow, elem ) ) )
5362 }
5463
5564 /// Insert a new stylesheet into this manager, _immediately_ inserting it
@@ -63,63 +72,61 @@ impl StyleCache {
6372 /// attached _before_ the style's target nodes are attached.
6473 pub fn add_style ( & self , name : & ' static str , css : & ' static str ) {
6574 let mut map = self . 0 . styles . borrow_mut ( ) ;
66- if !map. contains_key ( name) {
67- let style = Self :: into_style ( name, css, self . 0 . is_shadow ) ;
68- let first = map. values ( ) . next ( ) . cloned ( ) ;
69- map. insert ( name, style. clone ( ) ) ;
70- let mut values = map. values ( ) ;
71- if let Some ( mut x) = first {
72- while let Some ( y) = values. next ( )
73- && y. get_attribute ( "name" ) . as_deref ( ) < Some ( name)
74- {
75- x = y. clone ( ) ;
75+ STYLE_SHEET_CACHE . with_borrow_mut ( |cache| {
76+ if !map. contains ( name) {
77+ map. insert ( name) ;
78+ if !cache. contains_key ( name) {
79+ let style = Self :: into_style ( css) ;
80+ cache. insert ( name, style) ;
7681 }
7782
78- x. parent_node ( )
79- . unwrap_or_else ( || x. get_root_node ( ) )
80- . insert_before ( & style, x. next_sibling ( ) . as_ref ( ) )
81- . unwrap ( ) ;
83+ self . adopted_style_sheets . splice (
84+ map. iter ( ) . position ( |x| x == & name) . unwrap ( ) as u32 ,
85+ 0 ,
86+ cache. get ( name) . unwrap ( ) ,
87+ ) ;
8288 }
83- }
89+ } ) ;
8490 }
8591
8692 /// Concert a CSS string to an `HtmlStyleElement`, which are memoized due
8793 /// to their size and DOM performance impact.
88- fn into_style ( name : & str , css : & str , is_shadow : bool ) -> web_sys:: HtmlStyleElement {
89- let elem = global:: document ( ) . create_element ( "style" ) . unwrap ( ) ;
90- if is_shadow {
91- elem. set_text_content ( Some ( css) ) ;
92- } else {
93- let new_css = css. replace ( ":host" , ":root" ) ;
94- elem. set_text_content ( Some ( & new_css) ) ;
95- }
96-
97- elem. set_attribute ( "name" , name) . unwrap ( ) ;
98- elem. unchecked_into ( )
99- }
100-
101- pub fn iter_styles ( & self ) -> impl Iterator < Item = ( & ' static str , HtmlStyleElement ) > {
102- self . styles . borrow ( ) . clone ( ) . into_iter ( )
94+ fn into_style ( css : & str ) -> web_sys:: CssStyleSheet {
95+ let sheet = web_sys:: CssStyleSheet :: new ( ) . unwrap ( ) ;
96+ sheet. replace_sync ( css) . unwrap ( ) ;
97+ sheet
10398 }
10499}
105100
106101/// Using a `BTreeMap` so the resulting `<style>` elements have a stable order
107102/// when rendered to the DOM.
108103pub struct StyleCacheData {
109- styles : RefCell < BTreeMap < & ' static str , web_sys :: HtmlStyleElement > > ,
110- is_shadow : bool ,
104+ styles : RefCell < BTreeSet < & ' static str > > ,
105+ adopted_style_sheets : js_sys :: Array ,
111106}
112107
113108impl StyleCacheData {
114- fn new ( is_shadow : bool ) -> Self {
115- let styles = DOM_STYLES
116- . iter ( )
117- . map ( |x| ( x. 0 , StyleCache :: into_style ( x. 0 , x. 1 , is_shadow) ) )
118- . collect ( ) ;
109+ fn new ( is_shadow : bool , elem : & web_sys:: HtmlElement ) -> Self {
110+ let styles: RefCell < BTreeSet < & ' static str > > =
111+ RefCell :: new ( DOM_STYLES . iter ( ) . map ( |x| x. 0 ) . collect ( ) ) ;
112+
113+ let root: & JsValue = if is_shadow {
114+ & elem. shadow_root ( ) . unwrap ( )
115+ } else {
116+ & web_sys:: window ( ) . unwrap ( ) . document ( ) . unwrap ( )
117+ } ;
118+
119+ let adopted_style_sheets = js_sys:: Reflect :: get ( root, & "adoptedStyleSheets" . into ( ) )
120+ . unwrap ( )
121+ . unchecked_into :: < js_sys:: Array > ( ) ;
122+
123+ for name in styles. borrow ( ) . iter ( ) {
124+ STYLE_SHEET_CACHE . with_borrow ( |x| adopted_style_sheets. push ( x. get ( name) . unwrap ( ) ) ) ;
125+ }
119126
120127 Self {
121- styles : RefCell :: new ( styles ) ,
122- is_shadow ,
128+ styles,
129+ adopted_style_sheets ,
123130 }
124131 }
125132}
0 commit comments