@@ -361,10 +361,7 @@ impl CitadelLayoutEngine {
361361 self . clear_layout ( ) ;
362362
363363 // Build Taffy tree from DOM with viewport culling
364- let root = dom. root ( ) ;
365- if let Ok ( root_guard) = root. read ( ) {
366- self . build_node_recursive ( & * root_guard, dom, stylesheet) ?;
367- }
364+ self . build_taffy_tree ( dom, stylesheet) ?;
368365
369366 // Update viewport context
370367 self . viewport_context . width = viewport_size. width ;
@@ -443,6 +440,10 @@ impl CitadelLayoutEngine {
443440
444441 /// Build Taffy layout tree from DOM
445442 fn build_taffy_tree ( & mut self , dom : & Dom , stylesheet : & CitadelStylesheet ) -> ParserResult < ( ) > {
443+ // Check security limits before building
444+ let node_count = self . count_dom_nodes ( dom) ;
445+ self . check_security_limits ( node_count) ?;
446+
446447 let root = dom. root ( ) ;
447448 if let Ok ( root_guard) = root. read ( ) {
448449 self . build_node_recursive ( & * root_guard, dom, stylesheet) ?;
@@ -474,6 +475,29 @@ impl CitadelLayoutEngine {
474475 return Ok ( taffy_node) ;
475476 }
476477
478+ // Viewport culling optimization
479+ if self . viewport_culling_enabled {
480+ if let Some ( estimated_bounds) = self . estimate_node_bounds ( dom_node, stylesheet) {
481+ let viewport_size = LayoutSize {
482+ width : self . viewport_context . width ,
483+ height : self . viewport_context . height ,
484+ } ;
485+
486+ if !self . intersects_viewport ( & estimated_bounds, & viewport_size) {
487+ // Create a minimal node for out-of-viewport elements
488+ let taffy_node = self . taffy
489+ . new_leaf ( Style {
490+ display : taffy:: Display :: None ,
491+ ..Style :: default ( )
492+ } )
493+ . map_err ( |e| ParserError :: LayoutError ( format ! ( "Failed to create culled node: {:?}" , e) ) ) ?;
494+
495+ self . register_node_mapping ( dom_node. id ( ) , taffy_node) ;
496+ return Ok ( taffy_node) ;
497+ }
498+ }
499+ }
500+
477501 // Create Taffy style from computed style
478502 let taffy_style = self . convert_to_taffy_style ( & computed_style) ;
479503
@@ -1257,6 +1281,28 @@ impl CitadelLayoutEngine {
12571281 Ok ( ( ) )
12581282 }
12591283
1284+ /// Count DOM nodes for security validation
1285+ fn count_dom_nodes ( & self , dom : & Dom ) -> usize {
1286+ let mut count = 0 ;
1287+ let root = dom. root ( ) ;
1288+ if let Ok ( root_guard) = root. read ( ) {
1289+ self . count_node_recursive ( & * root_guard, & mut count) ;
1290+ }
1291+ count
1292+ }
1293+
1294+ /// Recursively count DOM nodes
1295+ fn count_node_recursive ( & self , node : & Node , count : & mut usize ) {
1296+ * count += 1 ;
1297+ if let Ok ( children) = node. children ( ) {
1298+ for child in children. iter ( ) {
1299+ if let Ok ( child_guard) = child. read ( ) {
1300+ self . count_node_recursive ( & * child_guard, count) ;
1301+ }
1302+ }
1303+ }
1304+ }
1305+
12601306 // ==================== PERFORMANCE OPTIMIZATION METHODS ====================
12611307
12621308 /// Generate cache key for layout result
@@ -1730,6 +1776,8 @@ mod tests {
17301776 width : 1000.0 ,
17311777 height : 800.0 ,
17321778 root_font_size : 16.0 ,
1779+ zoom_factor : 1.0 ,
1780+ device_pixel_ratio : 1.0 ,
17331781 } ;
17341782 let layout_engine = CitadelLayoutEngine :: with_context (
17351783 security_context,
0 commit comments