@@ -109,6 +109,8 @@ pub struct Node {
109
109
pub children : RefCell < Vec < Handle > > ,
110
110
/// Represents this node's data.
111
111
pub data : NodeData ,
112
+ /// Flag to control whether to free any children on destruction.
113
+ leak_children_on_drop : Cell < bool > ,
112
114
}
113
115
114
116
impl Node {
@@ -118,8 +120,38 @@ impl Node {
118
120
data : data,
119
121
parent : Cell :: new ( None ) ,
120
122
children : RefCell :: new ( Vec :: new ( ) ) ,
123
+ leak_children_on_drop : Cell :: new ( true ) ,
121
124
} )
122
125
}
126
+
127
+ /// Drop any child nodes remaining in this node at destruction.
128
+ ///
129
+ /// RcDom's destructor automatically drops any nodes and children that are
130
+ /// present in the document. This setting only affects nodes that are dropped
131
+ /// by manipulating the tree before RcDom's destructor runs (such as manually
132
+ /// removing children from a node after parsing is complete).
133
+ ///
134
+ /// Unsafety: due to the representation of children, this can trigger
135
+ /// stack overflow if dropping a node with a very deep tree of children.
136
+ /// This is not a recommended configuration to use when interacting with
137
+ /// arbitrary HTML content.
138
+ pub unsafe fn free_child_nodes_on_drop ( & self ) {
139
+ self . leak_children_on_drop . set ( false ) ;
140
+ }
141
+ }
142
+
143
+ impl Drop for Node {
144
+ fn drop ( & mut self ) {
145
+ if !self . children . borrow ( ) . is_empty ( ) {
146
+ if self . leak_children_on_drop . get ( ) {
147
+ warn ! ( "Dropping node with children outside of RcDom's destructor. \
148
+ Leaking memory for {} children.", self . children. borrow( ) . len( ) ) ;
149
+ for child in mem:: replace ( & mut * self . children . borrow_mut ( ) , vec ! [ ] ) {
150
+ mem:: forget ( child) ;
151
+ }
152
+ }
153
+ }
154
+ }
123
155
}
124
156
125
157
impl fmt:: Debug for Node {
@@ -195,6 +227,18 @@ pub struct RcDom {
195
227
pub quirks_mode : QuirksMode ,
196
228
}
197
229
230
+ impl Drop for RcDom {
231
+ fn drop ( & mut self ) {
232
+ // Ensure that node destructors execute linearly, rather
233
+ // than recursing through a tree of arbitrary depth.
234
+ let mut to_be_processed = vec ! [ self . document. clone( ) ] ;
235
+ while let Some ( node) = to_be_processed. pop ( ) {
236
+ to_be_processed. extend_from_slice ( & * node. children . borrow ( ) ) ;
237
+ node. children . borrow_mut ( ) . clear ( ) ;
238
+ }
239
+ }
240
+ }
241
+
198
242
impl TreeSink for RcDom {
199
243
type Output = Self ;
200
244
fn finish ( self ) -> Self {
@@ -418,61 +462,71 @@ impl Default for RcDom {
418
462
}
419
463
}
420
464
465
+ enum SerializeOp {
466
+ Open ( Handle ) ,
467
+ Close ( QualName )
468
+ }
469
+
421
470
impl Serialize for Handle {
422
471
fn serialize < S > ( & self , serializer : & mut S , traversal_scope : TraversalScope ) -> io:: Result < ( ) >
423
472
where
424
473
S : Serializer ,
425
474
{
426
- match ( & traversal_scope, & self . data ) {
427
- (
428
- _,
429
- & NodeData :: Element {
430
- ref name,
431
- ref attrs,
432
- ..
433
- } ,
434
- ) => {
435
- if traversal_scope == IncludeNode {
436
- try!( serializer. start_elem (
437
- name. clone ( ) ,
438
- attrs. borrow ( ) . iter ( ) . map ( |at| ( & at. name , & at. value [ ..] ) )
439
- ) ) ;
440
- }
441
-
442
- for handle in self . children . borrow ( ) . iter ( ) {
443
- try!( handle. clone ( ) . serialize ( serializer, IncludeNode ) ) ;
444
- }
475
+ let mut ops = match traversal_scope {
476
+ IncludeNode => vec ! [ SerializeOp :: Open ( self . clone( ) ) ] ,
477
+ ChildrenOnly ( _) => self
478
+ . children
479
+ . borrow ( )
480
+ . iter ( )
481
+ . map ( |h| SerializeOp :: Open ( h. clone ( ) ) ) . collect ( ) ,
482
+ } ;
445
483
446
- if traversal_scope == IncludeNode {
447
- try!( serializer. end_elem ( name. clone ( ) ) ) ;
484
+ while !ops. is_empty ( ) {
485
+ match ops. remove ( 0 ) {
486
+ SerializeOp :: Open ( handle) => {
487
+ match & handle. data {
488
+ & NodeData :: Element {
489
+ ref name,
490
+ ref attrs,
491
+ ..
492
+ } => {
493
+ try!( serializer. start_elem (
494
+ name. clone ( ) ,
495
+ attrs. borrow ( ) . iter ( ) . map ( |at| ( & at. name , & at. value [ ..] ) )
496
+ ) ) ;
497
+
498
+ ops. insert ( 0 , SerializeOp :: Close ( name. clone ( ) ) ) ;
499
+
500
+ for child in handle. children . borrow ( ) . iter ( ) . rev ( ) {
501
+ ops. insert ( 0 , SerializeOp :: Open ( child. clone ( ) ) ) ;
502
+ }
503
+ }
504
+
505
+ & NodeData :: Doctype { ref name, .. } => serializer. write_doctype ( & name) ?,
506
+
507
+ & NodeData :: Text { ref contents } => {
508
+ serializer. write_text ( & contents. borrow ( ) ) ?
509
+ }
510
+
511
+ & NodeData :: Comment { ref contents } => {
512
+ serializer. write_comment ( & contents) ?
513
+ } ,
514
+
515
+ & NodeData :: ProcessingInstruction {
516
+ ref target,
517
+ ref contents,
518
+ } => serializer. write_processing_instruction ( target, contents) ?,
519
+
520
+ & NodeData :: Document => panic ! ( "Can't serialize Document node itself" ) ,
521
+ }
448
522
}
449
- Ok ( ( ) )
450
- } ,
451
523
452
- ( & ChildrenOnly ( _) , & NodeData :: Document ) => {
453
- for handle in self . children . borrow ( ) . iter ( ) {
454
- try!( handle. clone ( ) . serialize ( serializer, IncludeNode ) ) ;
524
+ SerializeOp :: Close ( name) => {
525
+ try!( serializer. end_elem ( name) ) ;
455
526
}
456
- Ok ( ( ) )
457
- } ,
458
-
459
- ( & ChildrenOnly ( _) , _) => Ok ( ( ) ) ,
460
-
461
- ( & IncludeNode , & NodeData :: Doctype { ref name, .. } ) => serializer. write_doctype ( & name) ,
462
- ( & IncludeNode , & NodeData :: Text { ref contents } ) => {
463
- serializer. write_text ( & contents. borrow ( ) )
464
- } ,
465
- ( & IncludeNode , & NodeData :: Comment { ref contents } ) => {
466
- serializer. write_comment ( & contents)
467
- } ,
468
- (
469
- & IncludeNode ,
470
- & NodeData :: ProcessingInstruction {
471
- ref target,
472
- ref contents,
473
- } ,
474
- ) => serializer. write_processing_instruction ( target, contents) ,
475
- ( & IncludeNode , & NodeData :: Document ) => panic ! ( "Can't serialize Document node itself" ) ,
527
+ }
476
528
}
529
+
530
+ Ok ( ( ) )
477
531
}
478
532
}
0 commit comments