1
1
use super :: {
2
+ attributes:: Attributes ,
2
3
document:: Document ,
3
4
node:: { ElementData , NodeData , NodeId } ,
4
5
} ;
5
- use html5ever:: {
6
- local_name, serialize,
7
- serialize:: { Serialize , SerializeOpts , Serializer , TraversalScope } ,
8
- } ;
6
+ use html5ever:: { local_name, namespace_url, ns, QualName } ;
9
7
use std:: { io, io:: Write } ;
10
8
11
9
pub ( crate ) fn serialize_to < W : Write > (
@@ -14,7 +12,8 @@ pub(crate) fn serialize_to<W: Write>(
14
12
skip_style_tags : bool ,
15
13
) -> io:: Result < ( ) > {
16
14
let sink = Sink :: new ( document, NodeId :: document_id ( ) , skip_style_tags) ;
17
- serialize ( writer, & sink, SerializeOpts :: default ( ) )
15
+ let mut serializer = HtmlSerializer :: new ( writer) ;
16
+ sink. serialize ( & mut serializer)
18
17
}
19
18
20
19
/// Intermediary structure for serializing an HTML document.
@@ -32,46 +31,35 @@ impl<'a> Sink<'a> {
32
31
skip_style_tags,
33
32
}
34
33
}
34
+ #[ inline]
35
35
fn for_node ( & self , node : NodeId ) -> Sink < ' a > {
36
36
Sink :: new ( self . document , node, self . skip_style_tags )
37
37
}
38
+ #[ inline]
38
39
fn data ( & self ) -> & NodeData {
39
40
& self . document [ self . node ] . data
40
41
}
42
+ #[ inline]
41
43
fn should_skip_element ( & self , element : & ElementData ) -> bool {
42
44
self . skip_style_tags && element. name . local == local_name ! ( "style" )
43
45
}
44
- fn serialize_children < S : Serializer > ( & self , serializer : & mut S ) -> io:: Result < ( ) > {
46
+ fn serialize_children < W : Write > ( & self , serializer : & mut HtmlSerializer < W > ) -> io:: Result < ( ) > {
45
47
for child in self . document . children ( self . node ) {
46
- Serialize :: serialize (
47
- & self . for_node ( child) ,
48
- serializer,
49
- TraversalScope :: IncludeNode ,
50
- ) ?
48
+ self . for_node ( child) . serialize ( serializer) ?
51
49
}
52
50
Ok ( ( ) )
53
51
}
54
- }
55
-
56
- impl < ' a > Serialize for Sink < ' a > {
57
- fn serialize < S : Serializer > ( & self , serializer : & mut S , _: TraversalScope ) -> io:: Result < ( ) > {
52
+ fn serialize < W : Write > ( & self , serializer : & mut HtmlSerializer < W > ) -> io:: Result < ( ) > {
58
53
match self . data ( ) {
59
54
NodeData :: Element { element, .. } => {
60
55
if self . should_skip_element ( element) {
61
56
return Ok ( ( ) ) ;
62
57
}
63
- serializer. start_elem (
64
- element. name . clone ( ) ,
65
- element
66
- . attributes
67
- . map
68
- . iter ( )
69
- . map ( |( name, value) | ( name, & * * value) ) ,
70
- ) ?;
58
+ serializer. start_elem ( & element. name , & element. attributes ) ?;
71
59
72
60
self . serialize_children ( serializer) ?;
73
61
74
- serializer. end_elem ( element. name . clone ( ) ) ?;
62
+ serializer. end_elem ( & element. name ) ?;
75
63
Ok ( ( ) )
76
64
}
77
65
NodeData :: Document => self . serialize_children ( serializer) ,
@@ -85,6 +73,130 @@ impl<'a> Serialize for Sink<'a> {
85
73
}
86
74
}
87
75
76
+ struct ElemInfo {
77
+ ignore_children : bool ,
78
+ }
79
+
80
+ struct HtmlSerializer < Wr : Write > {
81
+ writer : Wr ,
82
+ stack : Vec < ElemInfo > ,
83
+ }
84
+
85
+ impl < Wr : Write > HtmlSerializer < Wr > {
86
+ fn new ( writer : Wr ) -> Self {
87
+ HtmlSerializer {
88
+ writer,
89
+ stack : vec ! [ ElemInfo {
90
+ ignore_children: false ,
91
+ } ] ,
92
+ }
93
+ }
94
+
95
+ fn parent ( & mut self ) -> & mut ElemInfo {
96
+ self . stack . last_mut ( ) . expect ( "Stack is empty" )
97
+ }
98
+
99
+ fn start_elem ( & mut self , name : & QualName , attrs : & Attributes ) -> io:: Result < ( ) > {
100
+ if self . parent ( ) . ignore_children {
101
+ self . stack . push ( ElemInfo {
102
+ ignore_children : true ,
103
+ } ) ;
104
+ return Ok ( ( ) ) ;
105
+ }
106
+
107
+ self . writer . write_all ( b"<" ) ?;
108
+ self . writer . write_all ( name. local . as_bytes ( ) ) ?;
109
+ for ( name, value) in & attrs. map {
110
+ self . writer . write_all ( b" " ) ?;
111
+
112
+ match name. ns {
113
+ ns ! ( ) => ( ) ,
114
+ ns ! ( xml) => self . writer . write_all ( b"xml:" ) ?,
115
+ ns ! ( xmlns) => {
116
+ if name. local != local_name ! ( "xmlns" ) {
117
+ self . writer . write_all ( b"xmlns:" ) ?;
118
+ }
119
+ }
120
+ ns ! ( xlink) => self . writer . write_all ( b"xlink:" ) ?,
121
+ _ => {
122
+ self . writer . write_all ( b"unknown_namespace:" ) ?;
123
+ }
124
+ }
125
+
126
+ self . writer . write_all ( name. local . as_bytes ( ) ) ?;
127
+ self . writer . write_all ( b"=\" " ) ?;
128
+ self . writer . write_all ( value. as_bytes ( ) ) ?;
129
+ self . writer . write_all ( b"\" " ) ?;
130
+ }
131
+ self . writer . write_all ( b">" ) ?;
132
+
133
+ let ignore_children = name. ns == ns ! ( html)
134
+ && matches ! (
135
+ name. local,
136
+ local_name!( "area" )
137
+ | local_name!( "base" )
138
+ | local_name!( "basefont" )
139
+ | local_name!( "bgsound" )
140
+ | local_name!( "br" )
141
+ | local_name!( "col" )
142
+ | local_name!( "embed" )
143
+ | local_name!( "frame" )
144
+ | local_name!( "hr" )
145
+ | local_name!( "img" )
146
+ | local_name!( "input" )
147
+ | local_name!( "keygen" )
148
+ | local_name!( "link" )
149
+ | local_name!( "meta" )
150
+ | local_name!( "param" )
151
+ | local_name!( "source" )
152
+ | local_name!( "track" )
153
+ | local_name!( "wbr" )
154
+ ) ;
155
+
156
+ self . stack . push ( ElemInfo { ignore_children } ) ;
157
+
158
+ Ok ( ( ) )
159
+ }
160
+
161
+ fn end_elem ( & mut self , name : & QualName ) -> io:: Result < ( ) > {
162
+ let info = match self . stack . pop ( ) {
163
+ Some ( info) => info,
164
+ _ => panic ! ( "no ElemInfo" ) ,
165
+ } ;
166
+ if info. ignore_children {
167
+ return Ok ( ( ) ) ;
168
+ }
169
+
170
+ self . writer . write_all ( b"</" ) ?;
171
+ self . writer . write_all ( name. local . as_bytes ( ) ) ?;
172
+ self . writer . write_all ( b">" )
173
+ }
174
+
175
+ fn write_text ( & mut self , text : & str ) -> io:: Result < ( ) > {
176
+ self . writer . write_all ( text. as_bytes ( ) )
177
+ }
178
+
179
+ fn write_comment ( & mut self , text : & str ) -> io:: Result < ( ) > {
180
+ self . writer . write_all ( b"<!--" ) ?;
181
+ self . writer . write_all ( text. as_bytes ( ) ) ?;
182
+ self . writer . write_all ( b"-->" )
183
+ }
184
+
185
+ fn write_doctype ( & mut self , name : & str ) -> io:: Result < ( ) > {
186
+ self . writer . write_all ( b"<!DOCTYPE " ) ?;
187
+ self . writer . write_all ( name. as_bytes ( ) ) ?;
188
+ self . writer . write_all ( b">" )
189
+ }
190
+
191
+ fn write_processing_instruction ( & mut self , target : & str , data : & str ) -> io:: Result < ( ) > {
192
+ self . writer . write_all ( b"<?" ) ?;
193
+ self . writer . write_all ( target. as_bytes ( ) ) ?;
194
+ self . writer . write_all ( b" " ) ?;
195
+ self . writer . write_all ( data. as_bytes ( ) ) ?;
196
+ self . writer . write_all ( b">" )
197
+ }
198
+ }
199
+
88
200
#[ cfg( test) ]
89
201
mod tests {
90
202
use super :: Document ;
0 commit comments