/[suikacvs]/markup/html/whatpm/Whatpm/ContentChecker.pm
Suika

Contents of /markup/html/whatpm/Whatpm/ContentChecker.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (hide annotations) (download)
Sun May 13 09:52:08 2007 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +128 -81 lines
++ whatpm/t/ChangeLog	13 May 2007 09:51:36 -0000
	* content-model-1.dat: Tests for |a| content model are added.
	Tests for |legend| content model are added.

2007-05-13  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	13 May 2007 09:50:22 -0000
	* ContentChecker.pm: Don't generate duplicate
	error when an element type is put in the "minus" list
	and the element type is not allowed explicitly in the particular
	element content model.
	(html:a checker): New checker.
	(html:details, html:datagrid): New checkers.
	(html:legend): New checker.

2007-05-13  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3    
4 wakaba 1.3 ## ANY
5     my $AnyChecker = sub {
6 wakaba 1.4 my ($self, $todo) = @_;
7     my $el = $todo->{node};
8     my $new_todos = [];
9 wakaba 1.3 my @nodes = (@{$el->child_nodes});
10     while (@nodes) {
11     my $node = shift @nodes;
12     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
13    
14     my $nt = $node->node_type;
15     if ($nt == 1) {
16     my $node_ns = $node->namespace_uri;
17     $node_ns = '' unless defined $node_ns;
18     my $node_ln = $node->manakai_local_name;
19     if ($self->{minuses}->{$node_ns}->{$node_ln}) {
20     $self->{onerror}->(node => $node, type => 'element not allowed');
21     }
22 wakaba 1.4 push @$new_todos, {type => 'element', node => $node};
23 wakaba 1.3 } elsif ($nt == 5) {
24     unshift @nodes, @{$node->child_nodes};
25     }
26     }
27 wakaba 1.4 return ($new_todos);
28 wakaba 1.3 }; # $AnyChecker
29    
30 wakaba 1.1 my $ElementDefault = {
31     checker => sub {
32 wakaba 1.4 my ($self, $todo) = @_;
33     $self->{onerror}->(node => $todo->{node}, type => 'element not supported');
34     return $AnyChecker->($self, $todo);
35 wakaba 1.1 },
36     };
37    
38     my $Element = {};
39    
40     my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
41    
42     my $HTMLMetadataElements = [
43     [$HTML_NS, 'link'],
44     [$HTML_NS, 'meta'],
45     [$HTML_NS, 'style'],
46     [$HTML_NS, 'script'],
47     [$HTML_NS, 'event-source'],
48     [$HTML_NS, 'command'],
49 wakaba 1.3 [$HTML_NS, 'base'],
50 wakaba 1.1 [$HTML_NS, 'title'],
51     ];
52    
53 wakaba 1.2 my $HTMLSectioningElements = {
54     $HTML_NS => {qw/body 1 section 1 nav 1 article 1 blockquote 1 aside 1/},
55     };
56 wakaba 1.1
57     my $HTMLBlockLevelElements = [
58     [$HTML_NS, 'section'],
59     [$HTML_NS, 'nav'],
60     [$HTML_NS, 'article'],
61     [$HTML_NS, 'blockquote'],
62     [$HTML_NS, 'aside'],
63 wakaba 1.2 [$HTML_NS, 'h1'],
64     [$HTML_NS, 'h2'],
65     [$HTML_NS, 'h3'],
66     [$HTML_NS, 'h4'],
67     [$HTML_NS, 'h5'],
68     [$HTML_NS, 'h6'],
69 wakaba 1.1 [$HTML_NS, 'header'],
70     [$HTML_NS, 'footer'],
71     [$HTML_NS, 'address'],
72     [$HTML_NS, 'p'],
73     [$HTML_NS, 'hr'],
74     [$HTML_NS, 'dialog'],
75     [$HTML_NS, 'pre'],
76     [$HTML_NS, 'ol'],
77     [$HTML_NS, 'ul'],
78     [$HTML_NS, 'dl'],
79     [$HTML_NS, 'ins'],
80     [$HTML_NS, 'del'],
81     [$HTML_NS, 'figure'],
82     [$HTML_NS, 'map'],
83     [$HTML_NS, 'table'],
84     [$HTML_NS, 'script'],
85     [$HTML_NS, 'noscript'],
86     [$HTML_NS, 'event-source'],
87     [$HTML_NS, 'details'],
88     [$HTML_NS, 'datagrid'],
89     [$HTML_NS, 'menu'],
90     [$HTML_NS, 'div'],
91     [$HTML_NS, 'font'],
92     ];
93    
94     my $HTMLStrictlyInlineLevelElements = [
95     [$HTML_NS, 'br'],
96     [$HTML_NS, 'a'],
97     [$HTML_NS, 'q'],
98     [$HTML_NS, 'cite'],
99     [$HTML_NS, 'em'],
100     [$HTML_NS, 'strong'],
101     [$HTML_NS, 'small'],
102     [$HTML_NS, 'm'],
103     [$HTML_NS, 'dfn'],
104     [$HTML_NS, 'abbr'],
105     [$HTML_NS, 'time'],
106     [$HTML_NS, 'meter'],
107     [$HTML_NS, 'progress'],
108     [$HTML_NS, 'code'],
109     [$HTML_NS, 'var'],
110     [$HTML_NS, 'samp'],
111     [$HTML_NS, 'kbd'],
112     [$HTML_NS, 'sub'],
113     [$HTML_NS, 'sup'],
114     [$HTML_NS, 'span'],
115     [$HTML_NS, 'i'],
116     [$HTML_NS, 'b'],
117     [$HTML_NS, 'bdo'],
118     [$HTML_NS, 'ins'],
119     [$HTML_NS, 'del'],
120     [$HTML_NS, 'img'],
121     [$HTML_NS, 'iframe'],
122     [$HTML_NS, 'embed'],
123     [$HTML_NS, 'object'],
124     [$HTML_NS, 'video'],
125     [$HTML_NS, 'audio'],
126     [$HTML_NS, 'canvas'],
127     [$HTML_NS, 'area'],
128     [$HTML_NS, 'script'],
129     [$HTML_NS, 'noscript'],
130     [$HTML_NS, 'event-source'],
131     [$HTML_NS, 'command'],
132     [$HTML_NS, 'font'],
133     ];
134    
135     my $HTMLStructuredInlineLevelElements = [
136     [$HTML_NS, 'blockquote'],
137     [$HTML_NS, 'pre'],
138     [$HTML_NS, 'ol'],
139     [$HTML_NS, 'ul'],
140     [$HTML_NS, 'dl'],
141     [$HTML_NS, 'table'],
142     [$HTML_NS, 'menu'],
143     ];
144    
145 wakaba 1.6 my $HTMLInteractiveElements = {
146     $HTML_NS => {a => 1, details => 1, datagrid => 1},
147     };
148     ## NOTE: |html:a| and |html:datagrid| are not allowed as a descendant
149     ## of interactive elements
150 wakaba 1.1
151     my $HTMLTransparentElements = [
152     [$HTML_NS, 'ins'],
153     [$HTML_NS, 'font'],
154 wakaba 1.2 [$HTML_NS, 'noscript'], ## NOTE: If scripting is disabled.
155 wakaba 1.1 ];
156    
157 wakaba 1.2 #my $HTMLSemiTransparentElements = [
158     # [$HTML_NS, 'video'],
159     # [$HTML_NS, 'audio'],
160     #];
161 wakaba 1.1
162     my $HTMLEmbededElements = [
163     [$HTML_NS, 'img'],
164     [$HTML_NS, 'iframe'],
165     [$HTML_NS, 'embed'],
166     [$HTML_NS, 'object'],
167     [$HTML_NS, 'video'],
168     [$HTML_NS, 'audio'],
169     [$HTML_NS, 'canvas'],
170     ];
171    
172     ## Empty
173     my $HTMLEmptyChecker = sub {
174 wakaba 1.4 my ($self, $todo) = @_;
175     my $el = $todo->{node};
176     my $new_todos = [];
177 wakaba 1.1 my @nodes = (@{$el->child_nodes});
178    
179     while (@nodes) {
180     my $node = shift @nodes;
181 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
182    
183 wakaba 1.1 my $nt = $node->node_type;
184     if ($nt == 1) {
185 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
186     $self->{onerror}->(node => $node, type => 'element not allowed');
187     my ($sib, $ch) = $self->_check_get_children ($node);
188     unshift @nodes, @$sib;
189 wakaba 1.4 push @$new_todos, @$ch;
190 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
191 wakaba 1.3 if ($node->data =~ /[^\x09-\x0D\x20]/) {
192     $self->{onerror}->(node => $node, type => 'character not allowed');
193     }
194 wakaba 1.1 } elsif ($nt == 5) {
195     unshift @nodes, @{$node->child_nodes};
196     }
197     }
198 wakaba 1.4 return ($new_todos);
199 wakaba 1.1 };
200    
201     ## Text
202     my $HTMLTextChecker = sub {
203 wakaba 1.4 my ($self, $todo) = @_;
204     my $el = $todo->{node};
205     my $new_todos = [];
206 wakaba 1.1 my @nodes = (@{$el->child_nodes});
207    
208     while (@nodes) {
209     my $node = shift @nodes;
210 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
211    
212 wakaba 1.1 my $nt = $node->node_type;
213     if ($nt == 1) {
214 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
215     $self->{onerror}->(node => $node, type => 'element not allowed');
216     my ($sib, $ch) = $self->_check_get_children ($node);
217     unshift @nodes, @$sib;
218 wakaba 1.4 push @$new_todos, @$ch;
219 wakaba 1.1 } elsif ($nt == 5) {
220     unshift @nodes, @{$node->child_nodes};
221     }
222     }
223 wakaba 1.4 return ($new_todos);
224 wakaba 1.1 };
225    
226     ## Zero or more |html:style| elements,
227     ## followed by zero or more block-level elements
228     my $HTMLStylableBlockChecker = sub {
229 wakaba 1.4 my ($self, $todo) = @_;
230     my $el = $todo->{node};
231     my $new_todos = [];
232 wakaba 1.1 my @nodes = (@{$el->child_nodes});
233    
234     my $has_non_style;
235     while (@nodes) {
236     my $node = shift @nodes;
237 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
238    
239 wakaba 1.1 my $nt = $node->node_type;
240     if ($nt == 1) {
241 wakaba 1.2 my $node_ns = $node->namespace_uri;
242     $node_ns = '' unless defined $node_ns;
243     my $node_ln = $node->manakai_local_name;
244 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
245 wakaba 1.1 if ($node->manakai_element_type_match ($HTML_NS, 'style')) {
246 wakaba 1.6 $not_allowed = 1 if $has_non_style;
247 wakaba 1.1 } else {
248     $has_non_style = 1;
249     CHK: {
250     for (@{$HTMLBlockLevelElements}) {
251     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
252     last CHK;
253     }
254     }
255 wakaba 1.6 $not_allowed = 1;
256 wakaba 1.1 } # CHK
257     }
258 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
259     if $not_allowed;
260 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
261     unshift @nodes, @$sib;
262 wakaba 1.4 push @$new_todos, @$ch;
263 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
264     if ($node->data =~ /[^\x09-\x0D\x20]/) {
265 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
266 wakaba 1.1 }
267     } elsif ($nt == 5) {
268     unshift @nodes, @{$node->child_nodes};
269     }
270     }
271 wakaba 1.4 return ($new_todos);
272 wakaba 1.1 }; # $HTMLStylableBlockChecker
273    
274     ## Zero or more block-level elements
275     my $HTMLBlockChecker = sub {
276 wakaba 1.4 my ($self, $todo) = @_;
277     my $el = $todo->{node};
278     my $new_todos = [];
279 wakaba 1.1 my @nodes = (@{$el->child_nodes});
280    
281     while (@nodes) {
282     my $node = shift @nodes;
283 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
284    
285 wakaba 1.1 my $nt = $node->node_type;
286     if ($nt == 1) {
287 wakaba 1.2 my $node_ns = $node->namespace_uri;
288     $node_ns = '' unless defined $node_ns;
289     my $node_ln = $node->manakai_local_name;
290 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
291 wakaba 1.1 CHK: {
292     for (@{$HTMLBlockLevelElements}) {
293     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
294     last CHK;
295     }
296     }
297 wakaba 1.6 $not_allowed = 1;
298 wakaba 1.1 } # CHK
299 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
300     if $not_allowed;
301 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
302     unshift @nodes, @$sib;
303 wakaba 1.4 push @$new_todos, @$ch;
304 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
305     if ($node->data =~ /[^\x09-\x0D\x20]/) {
306 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
307 wakaba 1.1 }
308     } elsif ($nt == 5) {
309     unshift @nodes, @{$node->child_nodes};
310     }
311     }
312 wakaba 1.4 return ($new_todos);
313 wakaba 1.1 }; # $HTMLBlockChecker
314    
315     ## Inline-level content
316     my $HTMLInlineChecker = sub {
317 wakaba 1.4 my ($self, $todo) = @_;
318     my $el = $todo->{node};
319     my $new_todos = [];
320 wakaba 1.1 my @nodes = (@{$el->child_nodes});
321    
322     while (@nodes) {
323     my $node = shift @nodes;
324 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
325    
326 wakaba 1.1 my $nt = $node->node_type;
327     if ($nt == 1) {
328 wakaba 1.2 my $node_ns = $node->namespace_uri;
329     $node_ns = '' unless defined $node_ns;
330     my $node_ln = $node->manakai_local_name;
331 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
332 wakaba 1.1 CHK: {
333     for (@{$HTMLStrictlyInlineLevelElements},
334     @{$HTMLStructuredInlineLevelElements}) {
335     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
336     last CHK;
337     }
338     }
339 wakaba 1.6 $not_allowed = 1;
340 wakaba 1.1 } # CHK
341 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
342     if $not_allowed;
343 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
344     unshift @nodes, @$sib;
345 wakaba 1.4 push @$new_todos, @$ch;
346 wakaba 1.1 } elsif ($nt == 5) {
347     unshift @nodes, @{$node->child_nodes};
348     }
349     }
350 wakaba 1.4
351     for (@$new_todos) {
352     $_->{inline} = 1;
353     }
354     return ($new_todos);
355     }; # $HTMLInlineChecker
356 wakaba 1.1
357     my $HTMLSignificantInlineChecker = $HTMLInlineChecker;
358     ## TODO: check significant content
359    
360     ## Strictly inline-level content
361     my $HTMLStrictlyInlineChecker = sub {
362 wakaba 1.4 my ($self, $todo) = @_;
363     my $el = $todo->{node};
364     my $new_todos = [];
365 wakaba 1.1 my @nodes = (@{$el->child_nodes});
366    
367     while (@nodes) {
368     my $node = shift @nodes;
369 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
370    
371 wakaba 1.1 my $nt = $node->node_type;
372     if ($nt == 1) {
373 wakaba 1.2 my $node_ns = $node->namespace_uri;
374     $node_ns = '' unless defined $node_ns;
375     my $node_ln = $node->manakai_local_name;
376 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
377 wakaba 1.1 CHK: {
378     for (@{$HTMLStrictlyInlineLevelElements}) {
379     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
380     last CHK;
381     }
382     }
383 wakaba 1.6 $not_allowed = 1;
384 wakaba 1.1 } # CHK
385 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
386     if $not_allowed;
387 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
388     unshift @nodes, @$sib;
389 wakaba 1.4 push @$new_todos, @$ch;
390 wakaba 1.1 } elsif ($nt == 5) {
391     unshift @nodes, @{$node->child_nodes};
392     }
393     }
394 wakaba 1.4
395     for (@$new_todos) {
396     $_->{inline} = 1;
397     $_->{strictly_inline} = 1;
398     }
399     return ($new_todos);
400 wakaba 1.1 }; # $HTMLStrictlyInlineChecker
401    
402     my $HTMLSignificantStrictlyInlineChecker = $HTMLStrictlyInlineChecker;
403     ## TODO: check significant content
404    
405 wakaba 1.4 ## Inline-level or strictly inline-kevek content
406     my $HTMLInlineOrStrictlyInlineChecker = sub {
407     my ($self, $todo) = @_;
408     my $el = $todo->{node};
409     my $new_todos = [];
410     my @nodes = (@{$el->child_nodes});
411    
412     while (@nodes) {
413     my $node = shift @nodes;
414     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
415    
416     my $nt = $node->node_type;
417     if ($nt == 1) {
418     my $node_ns = $node->namespace_uri;
419     $node_ns = '' unless defined $node_ns;
420     my $node_ln = $node->manakai_local_name;
421 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
422 wakaba 1.4 CHK: {
423     for (@{$HTMLStrictlyInlineLevelElements},
424     $todo->{strictly_inline} ? () : @{$HTMLStructuredInlineLevelElements}) {
425     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
426     last CHK;
427     }
428     }
429 wakaba 1.6 $not_allowed = 1;
430 wakaba 1.4 } # CHK
431 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
432     if $not_allowed;
433 wakaba 1.4 my ($sib, $ch) = $self->_check_get_children ($node);
434     unshift @nodes, @$sib;
435     push @$new_todos, @$ch;
436     } elsif ($nt == 5) {
437     unshift @nodes, @{$node->child_nodes};
438     }
439     }
440    
441     for (@$new_todos) {
442     $_->{inline} = 1;
443     $_->{strictly_inline} = 1;
444     }
445     return ($new_todos);
446     }; # $HTMLInlineOrStrictlyInlineChecker
447    
448 wakaba 1.6 my $HTMLSignificantInlineOrStrictlyInlineChecker
449     = $HTMLInlineOrStrictlyInlineChecker;
450     ## TODO: check significant content
451    
452 wakaba 1.1 my $HTMLBlockOrInlineChecker = sub {
453 wakaba 1.4 my ($self, $todo) = @_;
454     my $el = $todo->{node};
455     my $new_todos = [];
456 wakaba 1.1 my @nodes = (@{$el->child_nodes});
457    
458     my $content = 'block-or-inline'; # or 'block' or 'inline'
459     my @block_not_inline;
460     while (@nodes) {
461     my $node = shift @nodes;
462 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
463    
464 wakaba 1.1 my $nt = $node->node_type;
465     if ($nt == 1) {
466 wakaba 1.2 my $node_ns = $node->namespace_uri;
467     $node_ns = '' unless defined $node_ns;
468     my $node_ln = $node->manakai_local_name;
469 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
470 wakaba 1.1 if ($content eq 'block') {
471     CHK: {
472     for (@{$HTMLBlockLevelElements}) {
473     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
474     last CHK;
475     }
476     }
477 wakaba 1.6 $not_allowed = 1;
478 wakaba 1.1 } # CHK
479     } elsif ($content eq 'inline') {
480     CHK: {
481     for (@{$HTMLStrictlyInlineLevelElements},
482     @{$HTMLStructuredInlineLevelElements}) {
483     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
484     last CHK;
485     }
486     }
487 wakaba 1.6 $not_allowed = 1;
488 wakaba 1.1 } # CHK
489     } else {
490     my $is_block;
491     my $is_inline;
492     for (@{$HTMLBlockLevelElements}) {
493     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
494     $is_block = 1;
495     last;
496     }
497     }
498    
499     for (@{$HTMLStrictlyInlineLevelElements},
500     @{$HTMLStructuredInlineLevelElements}) {
501     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
502     $is_inline = 1;
503     last;
504     }
505     }
506    
507 wakaba 1.6 push @block_not_inline, $node
508     if $is_block and not $is_inline and not $not_allowed;
509 wakaba 1.1 unless ($is_block) {
510     $content = 'inline';
511     for (@block_not_inline) {
512 wakaba 1.2 $self->{onerror}->(node => $_, type => 'element not allowed');
513 wakaba 1.1 }
514 wakaba 1.6 $not_allowed = 1 unless $is_inline;
515 wakaba 1.1 }
516     }
517 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
518     if $not_allowed;
519 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
520     unshift @nodes, @$sib;
521 wakaba 1.4 push @$new_todos, @$ch;
522 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
523     if ($node->data =~ /[^\x09-\x0D\x20]/) {
524     if ($content eq 'block') {
525 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
526 wakaba 1.1 } else {
527     $content = 'inline';
528     for (@block_not_inline) {
529 wakaba 1.2 $self->{onerror}->(node => $_, type => 'element not allowed');
530 wakaba 1.1 }
531     }
532     }
533     } elsif ($nt == 5) {
534     unshift @nodes, @{$node->child_nodes};
535     }
536     }
537 wakaba 1.4
538     if ($content eq 'inline') {
539     for (@$new_todos) {
540     $_->{inline} = 1;
541     }
542     }
543     return ($new_todos);
544 wakaba 1.1 };
545    
546 wakaba 1.2 ## Zero or more XXX element, then either block-level or inline-level
547     my $GetHTMLZeroOrMoreThenBlockOrInlineChecker = sub ($$) {
548     my ($elnsuri, $ellname) = @_;
549     return sub {
550 wakaba 1.4 my ($self, $todo) = @_;
551     my $el = $todo->{node};
552     my $new_todos = [];
553 wakaba 1.2 my @nodes = (@{$el->child_nodes});
554    
555     my $has_non_style;
556     my $content = 'block-or-inline'; # or 'block' or 'inline'
557     my @block_not_inline;
558     while (@nodes) {
559     my $node = shift @nodes;
560     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
561    
562     my $nt = $node->node_type;
563     if ($nt == 1) {
564     my $node_ns = $node->namespace_uri;
565     $node_ns = '' unless defined $node_ns;
566     my $node_ln = $node->manakai_local_name;
567 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
568 wakaba 1.2 if ($node->manakai_element_type_match ($elnsuri, $ellname)) {
569 wakaba 1.6 $not_allowed = 1 if $has_non_style;
570 wakaba 1.2 } elsif ($content eq 'block') {
571     $has_non_style = 1;
572     CHK: {
573     for (@{$HTMLBlockLevelElements}) {
574     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
575     last CHK;
576     }
577     }
578 wakaba 1.6 $not_allowed = 1;
579 wakaba 1.2 } # CHK
580     } elsif ($content eq 'inline') {
581     $has_non_style = 1;
582     CHK: {
583     for (@{$HTMLStrictlyInlineLevelElements},
584     @{$HTMLStructuredInlineLevelElements}) {
585     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
586     last CHK;
587     }
588     }
589 wakaba 1.6 $not_allowed = 1;
590 wakaba 1.2 } # CHK
591     } else {
592     $has_non_style = 1;
593     my $is_block;
594     my $is_inline;
595 wakaba 1.1 for (@{$HTMLBlockLevelElements}) {
596     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
597 wakaba 1.2 $is_block = 1;
598     last;
599 wakaba 1.1 }
600     }
601 wakaba 1.2
602 wakaba 1.1 for (@{$HTMLStrictlyInlineLevelElements},
603     @{$HTMLStructuredInlineLevelElements}) {
604     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
605 wakaba 1.2 $is_inline = 1;
606     last;
607 wakaba 1.1 }
608     }
609 wakaba 1.2
610 wakaba 1.6 push @block_not_inline, $node
611     if $is_block and not $is_inline and not $not_allowed;
612 wakaba 1.2 unless ($is_block) {
613     $content = 'inline';
614     for (@block_not_inline) {
615     $self->{onerror}->(node => $_, type => 'element not allowed');
616     }
617 wakaba 1.6 $not_allowed = 1 unless $is_inline;
618 wakaba 1.1 }
619     }
620 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
621     if $not_allowed;
622 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
623     unshift @nodes, @$sib;
624 wakaba 1.4 push @$new_todos, @$ch;
625 wakaba 1.2 } elsif ($nt == 3 or $nt == 4) {
626     if ($node->data =~ /[^\x09-\x0D\x20]/) {
627     $has_non_style = 1;
628     if ($content eq 'block') {
629     $self->{onerror}->(node => $node, type => 'character not allowed');
630     } else {
631     $content = 'inline';
632     for (@block_not_inline) {
633     $self->{onerror}->(node => $_, type => 'element not allowed');
634     }
635 wakaba 1.1 }
636     }
637 wakaba 1.2 } elsif ($nt == 5) {
638     unshift @nodes, @{$node->child_nodes};
639 wakaba 1.1 }
640     }
641 wakaba 1.4
642     if ($content eq 'inline') {
643     for (@$new_todos) {
644     $_->{inline} = 1;
645     }
646     }
647     return ($new_todos);
648 wakaba 1.2 };
649     }; # $GetHTMLZeroOrMoreThenBlockOrInlineChecker
650 wakaba 1.1
651     my $HTMLTransparentChecker = $HTMLBlockOrInlineChecker;
652    
653     $Element->{$HTML_NS}->{html} = {
654     checker => sub {
655 wakaba 1.4 my ($self, $todo) = @_;
656     my $el = $todo->{node};
657     my $new_todos = [];
658 wakaba 1.1 my @nodes = (@{$el->child_nodes});
659    
660     my $phase = 'before head';
661     while (@nodes) {
662     my $node = shift @nodes;
663 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
664    
665 wakaba 1.1 my $nt = $node->node_type;
666     if ($nt == 1) {
667 wakaba 1.2 my $node_ns = $node->namespace_uri;
668     $node_ns = '' unless defined $node_ns;
669     my $node_ln = $node->manakai_local_name;
670 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
671 wakaba 1.1 if ($phase eq 'before head') {
672     if ($node->manakai_element_type_match ($HTML_NS, 'head')) {
673     $phase = 'after head';
674     } elsif ($node->manakai_element_type_match ($HTML_NS, 'body')) {
675 wakaba 1.2 $self->{onerror}
676 wakaba 1.3 ->(node => $node, type => 'ps element missing:head');
677 wakaba 1.1 $phase = 'after body';
678     } else {
679 wakaba 1.6 $not_allowed = 1;
680 wakaba 1.1 # before head
681     }
682     } elsif ($phase eq 'after head') {
683     if ($node->manakai_element_type_match ($HTML_NS, 'body')) {
684     $phase = 'after body';
685     } else {
686 wakaba 1.6 $not_allowed = 1;
687 wakaba 1.1 # after head
688     }
689     } else { #elsif ($phase eq 'after body') {
690 wakaba 1.6 $not_allowed = 1;
691 wakaba 1.1 # after body
692     }
693 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
694     if $not_allowed;
695 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
696     unshift @nodes, @$sib;
697 wakaba 1.4 push @$new_todos, @$ch;
698 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
699     if ($node->data =~ /[^\x09-\x0D\x20]/) {
700 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
701 wakaba 1.1 }
702     } elsif ($nt == 5) {
703     unshift @nodes, @{$node->child_nodes};
704     }
705     }
706 wakaba 1.3
707     if ($phase eq 'before head') {
708     $self->{onerror}->(node => $el, type => 'child element missing:head');
709     $self->{onerror}->(node => $el, type => 'child element missing:body');
710     } elsif ($phase eq 'after head') {
711     $self->{onerror}->(node => $el, type => 'child element missing:body');
712     }
713    
714 wakaba 1.4 return ($new_todos);
715 wakaba 1.1 },
716     };
717    
718     $Element->{$HTML_NS}->{head} = {
719     checker => sub {
720 wakaba 1.4 my ($self, $todo) = @_;
721     my $el = $todo->{node};
722     my $new_todos = [];
723 wakaba 1.1 my @nodes = (@{$el->child_nodes});
724    
725     my $has_title;
726 wakaba 1.3 my $phase = 'initial'; # 'after charset', 'after base'
727 wakaba 1.1 while (@nodes) {
728     my $node = shift @nodes;
729 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
730    
731 wakaba 1.1 my $nt = $node->node_type;
732     if ($nt == 1) {
733 wakaba 1.2 my $node_ns = $node->namespace_uri;
734     $node_ns = '' unless defined $node_ns;
735     my $node_ln = $node->manakai_local_name;
736 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
737 wakaba 1.1 if ($node->manakai_element_type_match ($HTML_NS, 'title')) {
738 wakaba 1.3 $phase = 'after base';
739 wakaba 1.1 unless ($has_title) {
740     $has_title = 1;
741     } else {
742 wakaba 1.6 $not_allowed = 1;
743 wakaba 1.1 }
744     } elsif ($node->manakai_element_type_match ($HTML_NS, 'meta')) {
745     if ($node->has_attribute_ns (undef, 'charset')) {
746 wakaba 1.3 if ($phase eq 'initial') {
747     $phase = 'after charset';
748 wakaba 1.1 } else {
749 wakaba 1.6 $not_allowed = 1;
750 wakaba 1.3 ## NOTE: See also |base|'s "contexts" field in the spec
751 wakaba 1.1 }
752     } else {
753 wakaba 1.3 $phase = 'after base';
754 wakaba 1.1 }
755     } elsif ($node->manakai_element_type_match ($HTML_NS, 'base')) {
756 wakaba 1.3 if ($phase eq 'initial' or $phase eq 'after charset') {
757     $phase = 'after base';
758 wakaba 1.1 } else {
759 wakaba 1.6 $not_allowed = 1;
760 wakaba 1.1 }
761     } else {
762 wakaba 1.3 $phase = 'after base';
763 wakaba 1.1 CHK: {
764     for (@{$HTMLMetadataElements}) {
765     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
766     last CHK;
767     }
768     }
769 wakaba 1.6 $not_allowed = 1;
770 wakaba 1.1 } # CHK
771     }
772 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
773     if $not_allowed;
774 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
775     unshift @nodes, @$sib;
776 wakaba 1.4 push @$new_todos, @$ch;
777 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
778     if ($node->data =~ /[^\x09-\x0D\x20]/) {
779 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
780 wakaba 1.1 }
781     } elsif ($nt == 5) {
782     unshift @nodes, @{$node->child_nodes};
783     }
784     }
785     unless ($has_title) {
786 wakaba 1.3 $self->{onerror}->(node => $el, type => 'child element missing:title');
787 wakaba 1.1 }
788 wakaba 1.4 return ($new_todos);
789 wakaba 1.1 },
790     };
791    
792     $Element->{$HTML_NS}->{title} = {
793     checker => $HTMLTextChecker,
794     };
795    
796     $Element->{$HTML_NS}->{base} = {
797     checker => $HTMLEmptyChecker,
798     };
799    
800     $Element->{$HTML_NS}->{link} = {
801     checker => $HTMLEmptyChecker,
802     };
803    
804     $Element->{$HTML_NS}->{meta} = {
805     checker => $HTMLEmptyChecker,
806     };
807    
808     ## NOTE: |html:style| has no conformance creteria on content model
809 wakaba 1.3 $Element->{$HTML_NS}->{style} = {
810     checker => $AnyChecker,
811     };
812 wakaba 1.1
813     $Element->{$HTML_NS}->{body} = {
814     checker => $HTMLBlockChecker,
815     };
816    
817     $Element->{$HTML_NS}->{section} = {
818     checker => $HTMLStylableBlockChecker,
819     };
820    
821     $Element->{$HTML_NS}->{nav} = {
822     checker => $HTMLBlockOrInlineChecker,
823     };
824    
825     $Element->{$HTML_NS}->{article} = {
826     checker => $HTMLStylableBlockChecker,
827     };
828    
829     $Element->{$HTML_NS}->{blockquote} = {
830     checker => $HTMLBlockChecker,
831     };
832    
833     $Element->{$HTML_NS}->{aside} = {
834 wakaba 1.2 checker => $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'style'),
835 wakaba 1.1 };
836    
837     $Element->{$HTML_NS}->{h1} = {
838     checker => $HTMLSignificantStrictlyInlineChecker,
839     };
840    
841     $Element->{$HTML_NS}->{h2} = {
842     checker => $HTMLSignificantStrictlyInlineChecker,
843     };
844    
845     $Element->{$HTML_NS}->{h3} = {
846     checker => $HTMLSignificantStrictlyInlineChecker,
847     };
848    
849     $Element->{$HTML_NS}->{h4} = {
850     checker => $HTMLSignificantStrictlyInlineChecker,
851     };
852    
853     $Element->{$HTML_NS}->{h5} = {
854     checker => $HTMLSignificantStrictlyInlineChecker,
855     };
856    
857     $Element->{$HTML_NS}->{h6} = {
858     checker => $HTMLSignificantStrictlyInlineChecker,
859     };
860    
861     ## TODO: header
862    
863 wakaba 1.2 $Element->{$HTML_NS}->{footer} = {
864     checker => sub { ## block -hn -header -footer -sectioning or inline
865 wakaba 1.4 my ($self, $todo) = @_;
866     my $el = $todo->{node};
867     my $new_todos = [];
868 wakaba 1.2 my @nodes = (@{$el->child_nodes});
869    
870     my $content = 'block-or-inline'; # or 'block' or 'inline'
871     my @block_not_inline;
872     while (@nodes) {
873     my $node = shift @nodes;
874     $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
875    
876     my $nt = $node->node_type;
877     if ($nt == 1) {
878     my $node_ns = $node->namespace_uri;
879     $node_ns = '' unless defined $node_ns;
880     my $node_ln = $node->manakai_local_name;
881 wakaba 1.6 my $not_allowed;
882 wakaba 1.2 if ($self->{minuses}->{$node_ns}->{$node_ln}) {
883 wakaba 1.6 $not_allowed = 1;
884 wakaba 1.2 } elsif ($node_ns eq $HTML_NS and
885     {
886     qw/h1 1 h2 1 h3 1 h4 1 h5 1 h6 1 header 1 footer 1/
887     }->{$node_ln}) {
888 wakaba 1.6 $not_allowed = 1;
889 wakaba 1.2 } elsif ($HTMLSectioningElements->{$node_ns}->{$node_ln}) {
890 wakaba 1.6 $not_allowed = 1;
891 wakaba 1.2 }
892     if ($content eq 'block') {
893     CHK: {
894     for (@{$HTMLBlockLevelElements}) {
895     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
896     last CHK;
897     }
898     }
899 wakaba 1.6 $not_allowed = 1;
900 wakaba 1.2 } # CHK
901     } elsif ($content eq 'inline') {
902     CHK: {
903     for (@{$HTMLStrictlyInlineLevelElements},
904     @{$HTMLStructuredInlineLevelElements}) {
905     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
906     last CHK;
907     }
908     }
909 wakaba 1.6 $not_allowed = 1;
910 wakaba 1.2 } # CHK
911     } else {
912     my $is_block;
913     my $is_inline;
914     for (@{$HTMLBlockLevelElements}) {
915     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
916     $is_block = 1;
917     last;
918     }
919     }
920    
921     for (@{$HTMLStrictlyInlineLevelElements},
922     @{$HTMLStructuredInlineLevelElements}) {
923     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
924     $is_inline = 1;
925     last;
926     }
927     }
928    
929 wakaba 1.6 push @block_not_inline, $node
930     if $is_block and not $is_inline and not $not_allowed;
931 wakaba 1.2 unless ($is_block) {
932     $content = 'inline';
933     for (@block_not_inline) {
934     $self->{onerror}->(node => $_, type => 'element not allowed');
935     }
936 wakaba 1.6 $not_allowed = 1 unless $is_inline;
937 wakaba 1.2 }
938     }
939 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
940     if $not_allowed;
941 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
942     unshift @nodes, @$sib;
943 wakaba 1.4 push @$new_todos, @$ch;
944 wakaba 1.2 } elsif ($nt == 3 or $nt == 4) {
945     if ($node->data =~ /[^\x09-\x0D\x20]/) {
946     if ($content eq 'block') {
947     $self->{onerror}->(node => $node, type => 'character not allowed');
948     } else {
949     $content = 'inline';
950     for (@block_not_inline) {
951     $self->{onerror}->(node => $_, type => 'element not allowed');
952     }
953     }
954     }
955     } elsif ($nt == 5) {
956     unshift @nodes, @{$node->child_nodes};
957     }
958     }
959    
960     my $end = $self->_add_minuses
961     ({$HTML_NS => {qw/h1 1 h2 1 h3 1 h4 1 h5 1 h6 1/}},
962     $HTMLSectioningElements);
963 wakaba 1.4 push @$new_todos, $end;
964 wakaba 1.2
965 wakaba 1.4 if ($content eq 'inline') {
966     for (@$new_todos) {
967     $_->{inline} = 1;
968     }
969     }
970    
971     return ($new_todos);
972 wakaba 1.2 },
973     };
974 wakaba 1.1
975     $Element->{$HTML_NS}->{address} = {
976     checker => $HTMLInlineChecker,
977     };
978    
979     $Element->{$HTML_NS}->{p} = {
980     checker => $HTMLSignificantInlineChecker,
981     };
982    
983     $Element->{$HTML_NS}->{hr} = {
984     checker => $HTMLEmptyChecker,
985     };
986    
987     $Element->{$HTML_NS}->{br} = {
988     checker => $HTMLEmptyChecker,
989     };
990    
991     $Element->{$HTML_NS}->{dialog} = {
992     checker => sub {
993 wakaba 1.4 my ($self, $todo) = @_;
994     my $el = $todo->{node};
995     my $new_todos = [];
996 wakaba 1.1 my @nodes = (@{$el->child_nodes});
997    
998     my $phase = 'before dt';
999     while (@nodes) {
1000     my $node = shift @nodes;
1001 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1002    
1003 wakaba 1.1 my $nt = $node->node_type;
1004     if ($nt == 1) {
1005 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1006 wakaba 1.1 if ($phase eq 'before dt') {
1007     if ($node->manakai_element_type_match ($HTML_NS, 'dt')) {
1008     $phase = 'before dd';
1009     } elsif ($node->manakai_element_type_match ($HTML_NS, 'dd')) {
1010 wakaba 1.2 $self->{onerror}
1011 wakaba 1.3 ->(node => $node, type => 'ps element missing:dt');
1012 wakaba 1.1 $phase = 'before dt';
1013     } else {
1014 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1015 wakaba 1.1 }
1016     } else { # before dd
1017     if ($node->manakai_element_type_match ($HTML_NS, 'dd')) {
1018     $phase = 'before dt';
1019     } elsif ($node->manakai_element_type_match ($HTML_NS, 'dt')) {
1020 wakaba 1.2 $self->{onerror}
1021 wakaba 1.3 ->(node => $node, type => 'ps element missing:dd');
1022 wakaba 1.1 $phase = 'before dd';
1023     } else {
1024 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1025 wakaba 1.1 }
1026     }
1027 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1028     unshift @nodes, @$sib;
1029 wakaba 1.4 push @$new_todos, @$ch;
1030 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1031     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1032 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1033 wakaba 1.1 }
1034     } elsif ($nt == 5) {
1035     unshift @nodes, @{$node->child_nodes};
1036     }
1037     }
1038     if ($phase eq 'before dd') {
1039 wakaba 1.3 $self->{onerror}->(node => $el, type => 'ps element missing:dd');
1040 wakaba 1.1 }
1041 wakaba 1.4 return ($new_todos);
1042 wakaba 1.1 },
1043     };
1044    
1045     $Element->{$HTML_NS}->{pre} = {
1046     checker => $HTMLStrictlyInlineChecker,
1047     };
1048    
1049     $Element->{$HTML_NS}->{ol} = {
1050     checker => sub {
1051 wakaba 1.4 my ($self, $todo) = @_;
1052     my $el = $todo->{node};
1053     my $new_todos = [];
1054 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1055    
1056     while (@nodes) {
1057     my $node = shift @nodes;
1058 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1059    
1060 wakaba 1.1 my $nt = $node->node_type;
1061     if ($nt == 1) {
1062 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1063 wakaba 1.1 unless ($node->manakai_element_type_match ($HTML_NS, 'li')) {
1064 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1065 wakaba 1.1 }
1066 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1067     unshift @nodes, @$sib;
1068 wakaba 1.4 push @$new_todos, @$ch;
1069 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1070     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1071 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1072 wakaba 1.1 }
1073     } elsif ($nt == 5) {
1074     unshift @nodes, @{$node->child_nodes};
1075     }
1076     }
1077 wakaba 1.4
1078     if ($todo->{inline}) {
1079     for (@$new_todos) {
1080     $_->{inline} = 1;
1081     }
1082     }
1083     return ($new_todos);
1084 wakaba 1.1 },
1085     };
1086    
1087     $Element->{$HTML_NS}->{ul} = {
1088     checker => $Element->{$HTML_NS}->{ol}->{checker},
1089     };
1090    
1091 wakaba 1.5
1092     $Element->{$HTML_NS}->{li} = {
1093     checker => sub {
1094     my ($self, $todo) = @_;
1095     if ($todo->{inline}) {
1096     return $HTMLInlineChecker->($self, $todo);
1097     } else {
1098     return $HTMLBlockOrInlineChecker->($self, $todo);
1099     }
1100     },
1101     };
1102 wakaba 1.1
1103     $Element->{$HTML_NS}->{dl} = {
1104     checker => sub {
1105 wakaba 1.4 my ($self, $todo) = @_;
1106     my $el = $todo->{node};
1107     my $new_todos = [];
1108 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1109    
1110     my $phase = 'before dt';
1111     while (@nodes) {
1112     my $node = shift @nodes;
1113 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1114    
1115 wakaba 1.1 my $nt = $node->node_type;
1116     if ($nt == 1) {
1117 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1118 wakaba 1.1 if ($phase eq 'in dds') {
1119     if ($node->manakai_element_type_match ($HTML_NS, 'dd')) {
1120     #$phase = 'in dds';
1121     } elsif ($node->manakai_element_type_match ($HTML_NS, 'dt')) {
1122     $phase = 'in dts';
1123     } else {
1124 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1125 wakaba 1.1 }
1126     } elsif ($phase eq 'in dts') {
1127     if ($node->manakai_element_type_match ($HTML_NS, 'dt')) {
1128     #$phase = 'in dts';
1129     } elsif ($node->manakai_element_type_match ($HTML_NS, 'dd')) {
1130     $phase = 'in dds';
1131     } else {
1132 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1133 wakaba 1.1 }
1134     } else { # before dt
1135     if ($node->manakai_element_type_match ($HTML_NS, 'dt')) {
1136     $phase = 'in dts';
1137     } elsif ($node->manakai_element_type_match ($HTML_NS, 'dd')) {
1138 wakaba 1.2 $self->{onerror}
1139 wakaba 1.3 ->(node => $node, type => 'ps element missing:dt');
1140 wakaba 1.1 $phase = 'in dds';
1141     } else {
1142 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1143 wakaba 1.1 }
1144     }
1145 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1146     unshift @nodes, @$sib;
1147 wakaba 1.4 push @$new_todos, @$ch;
1148 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1149     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1150 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1151 wakaba 1.1 }
1152     } elsif ($nt == 5) {
1153     unshift @nodes, @{$node->child_nodes};
1154     }
1155     }
1156     if ($phase eq 'in dts') {
1157 wakaba 1.3 $self->{onerror}->(node => $el, type => 'ps element missing:dd');
1158 wakaba 1.1 }
1159 wakaba 1.4
1160     if ($todo->{inline}) {
1161     for (@$new_todos) {
1162     $_->{inline} = 1;
1163     }
1164     }
1165     return ($new_todos);
1166 wakaba 1.1 },
1167     };
1168    
1169     $Element->{$HTML_NS}->{dt} = {
1170     checker => $HTMLStrictlyInlineChecker,
1171     };
1172    
1173 wakaba 1.4 $Element->{$HTML_NS}->{dd} = {
1174 wakaba 1.5 checker => $Element->{$HTML_NS}->{li}->{checker},
1175 wakaba 1.4 };
1176 wakaba 1.1
1177 wakaba 1.6 $Element->{$HTML_NS}->{a} = {
1178     checker => sub {
1179     my ($self, $todo) = @_;
1180    
1181     my $end = $self->_add_minuses ($HTMLInteractiveElements);
1182     my ($sib, $ch)
1183     = $HTMLSignificantInlineOrStrictlyInlineChecker->($self, $todo);
1184     push @$sib, $end;
1185     return ($sib, $ch);
1186     },
1187     };
1188 wakaba 1.1
1189 wakaba 1.4 $Element->{$HTML_NS}->{q} = {
1190     checker => $HTMLInlineOrStrictlyInlineChecker,
1191     };
1192 wakaba 1.1
1193     $Element->{$HTML_NS}->{cite} = {
1194     checker => $HTMLStrictlyInlineChecker,
1195     };
1196    
1197 wakaba 1.4 $Element->{$HTML_NS}->{em} = {
1198     checker => $HTMLInlineOrStrictlyInlineChecker,
1199     };
1200    
1201     $Element->{$HTML_NS}->{strong} = {
1202     checker => $HTMLInlineOrStrictlyInlineChecker,
1203     };
1204 wakaba 1.1
1205 wakaba 1.4 $Element->{$HTML_NS}->{small} = {
1206     checker => $HTMLInlineOrStrictlyInlineChecker,
1207     };
1208    
1209     $Element->{$HTML_NS}->{m} = {
1210     checker => $HTMLInlineOrStrictlyInlineChecker,
1211     };
1212    
1213     $Element->{$HTML_NS}->{dfn} = {
1214     checker => sub {
1215     my ($self, $todo) = @_;
1216    
1217     my $end = $self->_add_minuses ({$HTML_NS => {dfn => 1}});
1218     my ($sib, $ch) = $HTMLStrictlyInlineChecker->($self, $todo);
1219     push @$sib, $end;
1220     return ($sib, $ch);
1221     },
1222     };
1223 wakaba 1.1
1224     $Element->{$HTML_NS}->{abbr} = {
1225     checker => $HTMLStrictlyInlineChecker,
1226     };
1227    
1228     $Element->{$HTML_NS}->{time} = {
1229     checker => $HTMLStrictlyInlineChecker,
1230     };
1231    
1232     $Element->{$HTML_NS}->{meter} = {
1233     checker => $HTMLStrictlyInlineChecker,
1234     };
1235    
1236     $Element->{$HTML_NS}->{progress} = {
1237     checker => $HTMLStrictlyInlineChecker,
1238     };
1239    
1240 wakaba 1.4 $Element->{$HTML_NS}->{code} = {
1241     checker => $HTMLInlineOrStrictlyInlineChecker,
1242     };
1243 wakaba 1.1
1244     $Element->{$HTML_NS}->{var} = {
1245     checker => $HTMLStrictlyInlineChecker,
1246     };
1247    
1248 wakaba 1.4 $Element->{$HTML_NS}->{samp} = {
1249     checker => $HTMLInlineOrStrictlyInlineChecker,
1250     };
1251 wakaba 1.1
1252     $Element->{$HTML_NS}->{kbd} = {
1253     checker => $HTMLStrictlyInlineChecker,
1254     };
1255    
1256     $Element->{$HTML_NS}->{sub} = {
1257     checker => $HTMLStrictlyInlineChecker,
1258     };
1259    
1260     $Element->{$HTML_NS}->{sup} = {
1261     checker => $HTMLStrictlyInlineChecker,
1262     };
1263    
1264 wakaba 1.4 $Element->{$HTML_NS}->{span} = {
1265     checker => $HTMLInlineOrStrictlyInlineChecker,
1266     };
1267 wakaba 1.1
1268     $Element->{$HTML_NS}->{i} = {
1269     checker => $HTMLStrictlyInlineChecker,
1270     };
1271    
1272     $Element->{$HTML_NS}->{b} = {
1273     checker => $HTMLStrictlyInlineChecker,
1274     };
1275    
1276     $Element->{$HTML_NS}->{bdo} = {
1277     checker => $HTMLStrictlyInlineChecker,
1278     };
1279    
1280     $Element->{$HTML_NS}->{ins} = {
1281     checker => $HTMLTransparentChecker,
1282     };
1283    
1284     $Element->{$HTML_NS}->{del} = {
1285     checker => sub {
1286 wakaba 1.4 my ($self, $todo) = @_;
1287 wakaba 1.1
1288 wakaba 1.4 my $parent = $todo->{node}->manakai_parent_element;
1289 wakaba 1.1 if (defined $parent) {
1290     my $nsuri = $parent->namespace_uri;
1291     $nsuri = '' unless defined $nsuri;
1292     my $ln = $parent->manakai_local_name;
1293     my $eldef = $Element->{$nsuri}->{$ln} ||
1294     $Element->{$nsuri}->{''} ||
1295     $ElementDefault;
1296 wakaba 1.4 return $eldef->{checker}->($self, $todo);
1297 wakaba 1.1 } else {
1298 wakaba 1.4 return $HTMLBlockOrInlineChecker->($self, $todo);
1299 wakaba 1.1 }
1300     },
1301     };
1302    
1303     ## TODO: figure
1304    
1305     $Element->{$HTML_NS}->{img} = {
1306     checker => $HTMLEmptyChecker,
1307     };
1308    
1309     $Element->{$HTML_NS}->{iframe} = {
1310     checker => $HTMLTextChecker,
1311     };
1312    
1313     $Element->{$HTML_NS}->{embed} = {
1314     checker => $HTMLEmptyChecker,
1315     };
1316    
1317     $Element->{$HTML_NS}->{param} = {
1318     checker => $HTMLEmptyChecker,
1319     };
1320    
1321     ## TODO: object
1322    
1323 wakaba 1.2 $Element->{$HTML_NS}->{video} = {
1324     checker => sub {
1325 wakaba 1.4 my ($self, $todo) = @_;
1326 wakaba 1.2
1327 wakaba 1.4 if ($todo->{node}->has_attribute_ns (undef, 'src')) {
1328     return $HTMLBlockOrInlineChecker->($self, $todo);
1329 wakaba 1.2 } else {
1330     return $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'source')
1331 wakaba 1.4 ->($self, $todo);
1332 wakaba 1.2 }
1333     },
1334     };
1335    
1336     $Element->{$HTML_NS}->{audio} = {
1337     checker => $Element->{$HTML_NS}->{audio}->{checker},
1338     };
1339 wakaba 1.1
1340     $Element->{$HTML_NS}->{source} = {
1341     checker => $HTMLEmptyChecker,
1342     };
1343    
1344     $Element->{$HTML_NS}->{canvas} = {
1345     checker => $HTMLInlineChecker,
1346     };
1347    
1348     $Element->{$HTML_NS}->{map} = {
1349     checker => $HTMLBlockChecker,
1350     };
1351    
1352     $Element->{$HTML_NS}->{area} = {
1353     checker => $HTMLEmptyChecker,
1354     };
1355     ## TODO: only in map
1356    
1357     $Element->{$HTML_NS}->{table} = {
1358     checker => sub {
1359 wakaba 1.4 my ($self, $todo) = @_;
1360     my $el = $todo->{node};
1361     my $new_todos = [];
1362 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1363    
1364     my $phase = 'before caption';
1365     my $has_tfoot;
1366     while (@nodes) {
1367     my $node = shift @nodes;
1368 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1369    
1370 wakaba 1.1 my $nt = $node->node_type;
1371     if ($nt == 1) {
1372 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1373 wakaba 1.1 if ($phase eq 'in tbodys') {
1374     if ($node->manakai_element_type_match ($HTML_NS, 'tbody')) {
1375     #$phase = 'in tbodys';
1376     } elsif (not $has_tfoot and
1377     $node->manakai_element_type_match ($HTML_NS, 'tfoot')) {
1378     $phase = 'after tfoot';
1379     $has_tfoot = 1;
1380     } else {
1381 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1382 wakaba 1.1 }
1383     } elsif ($phase eq 'in trs') {
1384     if ($node->manakai_element_type_match ($HTML_NS, 'tr')) {
1385     #$phase = 'in trs';
1386     } elsif (not $has_tfoot and
1387     $node->manakai_element_type_match ($HTML_NS, 'tfoot')) {
1388     $phase = 'after tfoot';
1389     $has_tfoot = 1;
1390     } else {
1391 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1392 wakaba 1.1 }
1393     } elsif ($phase eq 'after thead') {
1394     if ($node->manakai_element_type_match ($HTML_NS, 'tbody')) {
1395     $phase = 'in tbodys';
1396     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tr')) {
1397     $phase = 'in trs';
1398     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tfoot')) {
1399     $phase = 'in tbodys';
1400     $has_tfoot = 1;
1401     } else {
1402 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1403 wakaba 1.1 }
1404     } elsif ($phase eq 'in colgroup') {
1405     if ($node->manakai_element_type_match ($HTML_NS, 'colgroup')) {
1406     $phase = 'in colgroup';
1407     } elsif ($node->manakai_element_type_match ($HTML_NS, 'thead')) {
1408     $phase = 'after thead';
1409     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tbody')) {
1410     $phase = 'in tbodys';
1411     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tr')) {
1412     $phase = 'in trs';
1413     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tfoot')) {
1414     $phase = 'in tbodys';
1415     $has_tfoot = 1;
1416     } else {
1417 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1418 wakaba 1.1 }
1419     } elsif ($phase eq 'before caption') {
1420     if ($node->manakai_element_type_match ($HTML_NS, 'caption')) {
1421     $phase = 'in colgroup';
1422     } elsif ($node->manakai_element_type_match ($HTML_NS, 'colgroup')) {
1423     $phase = 'in colgroup';
1424     } elsif ($node->manakai_element_type_match ($HTML_NS, 'thead')) {
1425     $phase = 'after thead';
1426     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tbody')) {
1427     $phase = 'in tbodys';
1428     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tr')) {
1429     $phase = 'in trs';
1430     } elsif ($node->manakai_element_type_match ($HTML_NS, 'tfoot')) {
1431     $phase = 'in tbodys';
1432     $has_tfoot = 1;
1433     } else {
1434 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1435 wakaba 1.1 }
1436     } else { # after tfoot
1437 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1438 wakaba 1.1 }
1439 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1440     unshift @nodes, @$sib;
1441 wakaba 1.4 push @$new_todos, @$ch;
1442 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1443     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1444 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1445 wakaba 1.1 }
1446     } elsif ($nt == 5) {
1447     unshift @nodes, @{$node->child_nodes};
1448     }
1449     }
1450 wakaba 1.4 return ($new_todos);
1451 wakaba 1.1 },
1452     };
1453    
1454     $Element->{$HTML_NS}->{caption} = {
1455     checker => $HTMLSignificantStrictlyInlineChecker,
1456     };
1457    
1458     $Element->{$HTML_NS}->{colgroup} = {
1459     checker => sub {
1460 wakaba 1.4 my ($self, $todo) = @_;
1461     my $el = $todo->{node};
1462     my $new_todos = [];
1463 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1464    
1465     while (@nodes) {
1466     my $node = shift @nodes;
1467 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1468    
1469 wakaba 1.1 my $nt = $node->node_type;
1470     if ($nt == 1) {
1471 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1472 wakaba 1.1 unless ($node->manakai_element_type_match ($HTML_NS, 'col')) {
1473 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1474 wakaba 1.1 }
1475 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1476     unshift @nodes, @$sib;
1477 wakaba 1.4 push @$new_todos, @$ch;
1478 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1479     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1480 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1481 wakaba 1.1 }
1482     } elsif ($nt == 5) {
1483     unshift @nodes, @{$node->child_nodes};
1484     }
1485     }
1486 wakaba 1.4 return ($new_todos);
1487 wakaba 1.1 },
1488     };
1489    
1490     $Element->{$HTML_NS}->{col} = {
1491     checker => $HTMLEmptyChecker,
1492     };
1493    
1494     $Element->{$HTML_NS}->{tbody} = {
1495     checker => sub {
1496 wakaba 1.4 my ($self, $todo) = @_;
1497     my $el = $todo->{node};
1498     my $new_todos = [];
1499 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1500    
1501     my $has_tr;
1502     while (@nodes) {
1503     my $node = shift @nodes;
1504 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1505    
1506 wakaba 1.1 my $nt = $node->node_type;
1507     if ($nt == 1) {
1508 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1509 wakaba 1.1 if ($node->manakai_element_type_match ($HTML_NS, 'tr')) {
1510     $has_tr = 1;
1511     } else {
1512 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1513 wakaba 1.1 }
1514 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1515     unshift @nodes, @$sib;
1516 wakaba 1.4 push @$new_todos, @$ch;
1517 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1518     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1519 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1520 wakaba 1.1 }
1521     } elsif ($nt == 5) {
1522     unshift @nodes, @{$node->child_nodes};
1523     }
1524     }
1525     unless ($has_tr) {
1526 wakaba 1.3 $self->{onerror}->(node => $el, type => 'child element missing:tr');
1527 wakaba 1.1 }
1528 wakaba 1.4 return ($new_todos);
1529 wakaba 1.1 },
1530     };
1531    
1532     $Element->{$HTML_NS}->{thead} = {
1533     checker => $Element->{$HTML_NS}->{tbody},
1534     };
1535    
1536     $Element->{$HTML_NS}->{tfoot} = {
1537     checker => $Element->{$HTML_NS}->{tbody},
1538     };
1539    
1540     $Element->{$HTML_NS}->{tr} = {
1541     checker => sub {
1542 wakaba 1.4 my ($self, $todo) = @_;
1543     my $el = $todo->{node};
1544     my $new_todos = [];
1545 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1546    
1547     my $has_td;
1548     while (@nodes) {
1549     my $node = shift @nodes;
1550 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1551    
1552 wakaba 1.1 my $nt = $node->node_type;
1553     if ($nt == 1) {
1554 wakaba 1.2 ## NOTE: |minuses| list is not checked since redundant
1555 wakaba 1.1 if ($node->manakai_element_type_match ($HTML_NS, 'td') or
1556     $node->manakai_element_type_match ($HTML_NS, 'th')) {
1557     $has_td = 1;
1558     } else {
1559 wakaba 1.2 $self->{onerror}->(node => $node, type => 'element not allowed');
1560 wakaba 1.1 }
1561 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1562     unshift @nodes, @$sib;
1563 wakaba 1.4 push @$new_todos, @$ch;
1564 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1565     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1566 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1567 wakaba 1.1 }
1568     } elsif ($nt == 5) {
1569     unshift @nodes, @{$node->child_nodes};
1570     }
1571     }
1572     unless ($has_td) {
1573 wakaba 1.3 $self->{onerror}->(node => $el, type => 'child element missing:td|th');
1574 wakaba 1.1 }
1575 wakaba 1.4 return ($new_todos);
1576 wakaba 1.1 },
1577     };
1578    
1579     $Element->{$HTML_NS}->{td} = {
1580     checker => $HTMLBlockOrInlineChecker,
1581     };
1582    
1583     $Element->{$HTML_NS}->{th} = {
1584     checker => $HTMLBlockOrInlineChecker,
1585     };
1586    
1587     ## TODO: forms
1588    
1589 wakaba 1.2 $Element->{$HTML_NS}->{script} = {
1590     checker => sub {
1591 wakaba 1.4 my ($self, $todo) = @_;
1592 wakaba 1.2
1593 wakaba 1.4 if ($todo->{node}->has_attribute_ns (undef, 'src')) {
1594     return $HTMLEmptyChecker->($self, $todo);
1595 wakaba 1.2 } else {
1596     ## NOTE: No content model conformance in HTML5 spec.
1597 wakaba 1.4 return $AnyChecker->($self, $todo);
1598 wakaba 1.2 }
1599     },
1600     };
1601    
1602     ## NOTE: When script is disabled.
1603     $Element->{$HTML_NS}->{noscript} = {
1604     checker => sub {
1605 wakaba 1.4 my ($self, $todo) = @_;
1606 wakaba 1.1
1607 wakaba 1.2 my $end = $self->_add_minuses ({$HTML_NS => {noscript => 1}});
1608 wakaba 1.4 my ($sib, $ch) = $HTMLBlockOrInlineChecker->($self, $todo);
1609 wakaba 1.2 push @$sib, $end;
1610     return ($sib, $ch);
1611     },
1612     };
1613 wakaba 1.1
1614     $Element->{$HTML_NS}->{'event-source'} = {
1615     checker => $HTMLEmptyChecker,
1616     };
1617    
1618     $Element->{$HTML_NS}->{details} = {
1619 wakaba 1.6 checker => sub {
1620     my ($self, $todo) = @_;
1621    
1622     my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}});
1623     my ($sib, $ch)
1624     = $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'legend')
1625     ->($self, $todo);
1626     push @$sib, $end;
1627     return ($sib, $ch);
1628     },
1629 wakaba 1.1 };
1630    
1631     $Element->{$HTML_NS}->{datagrid} = {
1632 wakaba 1.6 checker => sub {
1633     my ($self, $todo) = @_;
1634    
1635     my $end = $self->_add_minuses ({$HTML_NS => {a => 1, datagrid => 1}});
1636     my ($sib, $ch) = $HTMLBlockChecker->($self, $todo);
1637     push @$sib, $end;
1638     return ($sib, $ch);
1639     },
1640 wakaba 1.1 };
1641    
1642     $Element->{$HTML_NS}->{command} = {
1643     checker => $HTMLEmptyChecker,
1644     };
1645    
1646     $Element->{$HTML_NS}->{menu} = {
1647     checker => sub {
1648 wakaba 1.4 my ($self, $todo) = @_;
1649     my $el = $todo->{node};
1650     my $new_todos = [];
1651 wakaba 1.1 my @nodes = (@{$el->child_nodes});
1652    
1653     my $content = 'li or inline';
1654     while (@nodes) {
1655     my $node = shift @nodes;
1656 wakaba 1.2 $self->_remove_minuses ($node) and next if ref $node eq 'HASH';
1657    
1658 wakaba 1.1 my $nt = $node->node_type;
1659     if ($nt == 1) {
1660 wakaba 1.2 my $node_ns = $node->namespace_uri;
1661     $node_ns = '' unless defined $node_ns;
1662     my $node_ln = $node->manakai_local_name;
1663 wakaba 1.6 my $not_allowed = $self->{minuses}->{$node_ns}->{$node_ln};
1664 wakaba 1.1 if ($node->manakai_element_type_match ($HTML_NS, 'li')) {
1665     if ($content eq 'inline') {
1666 wakaba 1.6 $not_allowed = 1;
1667 wakaba 1.1 } elsif ($content eq 'li or inline') {
1668     $content = 'li';
1669     }
1670     } else {
1671     CHK: {
1672     for (@{$HTMLStrictlyInlineLevelElements},
1673     @{$HTMLStructuredInlineLevelElements}) {
1674     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
1675     $content = 'inline';
1676     last CHK;
1677     }
1678     }
1679 wakaba 1.6 $not_allowed = 1;
1680 wakaba 1.1 } # CHK
1681     }
1682 wakaba 1.6 $self->{onerror}->(node => $node, type => 'element not allowed')
1683     if $not_allowed;
1684 wakaba 1.2 my ($sib, $ch) = $self->_check_get_children ($node);
1685     unshift @nodes, @$sib;
1686 wakaba 1.4 push @$new_todos, @$ch;
1687 wakaba 1.1 } elsif ($nt == 3 or $nt == 4) {
1688     if ($node->data =~ /[^\x09-\x0D\x20]/) {
1689     if ($content eq 'li') {
1690 wakaba 1.2 $self->{onerror}->(node => $node, type => 'character not allowed');
1691 wakaba 1.1 } elsif ($content eq 'li or inline') {
1692     $content = 'inline';
1693     }
1694     }
1695     } elsif ($nt == 5) {
1696     unshift @nodes, @{$node->child_nodes};
1697     }
1698     }
1699 wakaba 1.4
1700     for (@$new_todos) {
1701     $_->{inline} = 1;
1702     }
1703     return ($new_todos);
1704 wakaba 1.1 },
1705     };
1706    
1707 wakaba 1.6 $Element->{$HTML_NS}->{legend} = {
1708     checker => sub {
1709     my ($self, $todo) = @_;
1710    
1711     my $parent = $todo->{node}->manakai_parent_element;
1712     if (defined $parent) {
1713     my $nsuri = $parent->namespace_uri;
1714     $nsuri = '' unless defined $nsuri;
1715     my $ln = $parent->manakai_local_name;
1716     if ($nsuri eq $HTML_NS and $ln eq 'figure') {
1717     return $HTMLInlineChecker->($self, $todo);
1718     } else {
1719     return $HTMLSignificantStrictlyInlineChecker->($self, $todo);
1720     }
1721     } else {
1722     return $HTMLInlineChecker->($self, $todo);
1723     }
1724    
1725     ## ISSUE: Content model is defined only for fieldset/legend,
1726     ## details/legend, and figure/legend.
1727     },
1728     };
1729 wakaba 1.1
1730     $Element->{$HTML_NS}->{div} = {
1731 wakaba 1.2 checker => $GetHTMLZeroOrMoreThenBlockOrInlineChecker->($HTML_NS, 'style'),
1732 wakaba 1.1 };
1733    
1734     $Element->{$HTML_NS}->{font} = {
1735     checker => $HTMLTransparentChecker,
1736     };
1737    
1738     my $Attr = {
1739    
1740     };
1741    
1742 wakaba 1.2 sub new ($) {
1743     return bless {}, shift;
1744     } # new
1745    
1746 wakaba 1.1 sub check_element ($$$) {
1747     my ($self, $el, $onerror) = @_;
1748    
1749 wakaba 1.2 $self->{minuses} = {};
1750     $self->{onerror} = $onerror;
1751    
1752 wakaba 1.4 my @todo = ({type => 'element', node => $el});
1753     while (@todo) {
1754     my $todo = shift @todo;
1755     if ($todo->{type} eq 'element') {
1756     my $nsuri = $todo->{node}->namespace_uri;
1757     $nsuri = '' unless defined $nsuri;
1758     my $ln = $todo->{node}->manakai_local_name;
1759     my $eldef = $Element->{$nsuri}->{$ln} ||
1760     $Element->{$nsuri}->{''} ||
1761     $ElementDefault;
1762     my ($new_todos) = $eldef->{checker}->($self, $todo);
1763     push @todo, @$new_todos;
1764     } elsif ($todo->{type} eq 'plus') {
1765     $self->_remove_minuses ($todo);
1766     }
1767 wakaba 1.1 }
1768     } # check_element
1769    
1770 wakaba 1.2 sub _add_minuses ($@) {
1771     my $self = shift;
1772     my $r = {};
1773     for my $list (@_) {
1774     for my $ns (keys %$list) {
1775     for my $ln (keys %{$list->{$ns}}) {
1776     unless ($self->{minuses}->{$ns}->{$ln}) {
1777     $self->{minuses}->{$ns}->{$ln} = 1;
1778     $r->{$ns}->{$ln} = 1;
1779     }
1780     }
1781     }
1782     }
1783 wakaba 1.4 return {type => 'plus', list => $r};
1784 wakaba 1.2 } # _add_minuses
1785    
1786     sub _remove_minuses ($$) {
1787 wakaba 1.4 my ($self, $todo) = @_;
1788     for my $ns (keys %{$todo->{list}}) {
1789     for my $ln (keys %{$todo->{list}->{$ns}}) {
1790     delete $self->{minuses}->{$ns}->{$ln} if $todo->{list}->{$ns}->{$ln};
1791 wakaba 1.2 }
1792     }
1793     1;
1794     } # _remove_minuses
1795    
1796     sub _check_get_children ($$) {
1797     my ($self, $node) = @_;
1798 wakaba 1.4 my $new_todos = [];
1799 wakaba 1.2 my $sib = [];
1800     TP: {
1801     my $node_ns = $node->namespace_uri;
1802     $node_ns = '' unless defined $node_ns;
1803     my $node_ln = $node->manakai_local_name;
1804     if ($node_ns eq $HTML_NS) {
1805     if ($node_ln eq 'noscript') {
1806     my $end = $self->_add_minuses ({$HTML_NS, {noscript => 1}});
1807     push @$sib, $end;
1808     }
1809     }
1810     for (@{$HTMLTransparentElements}) {
1811     if ($node->manakai_element_type_match ($_->[0], $_->[1])) {
1812     unshift @$sib, @{$node->child_nodes};
1813     last TP;
1814     }
1815     }
1816     if ($node->manakai_element_type_match ($HTML_NS, 'video') or
1817     $node->manakai_element_type_match ($HTML_NS, 'audio')) {
1818     if ($node->has_attribute_ns (undef, 'src')) {
1819     unshift @$sib, @{$node->child_nodes};
1820     last TP;
1821     } else {
1822     my @cn = @{$node->child_nodes};
1823     CN: while (@cn) {
1824     my $cn = shift @cn;
1825     my $cnt = $cn->node_type;
1826     if ($cnt == 1) {
1827     if ($cn->manakai_element_type_match ($HTML_NS, 'source')) {
1828     #
1829     } else {
1830     last CN;
1831     }
1832     } elsif ($cnt == 3 or $cnt == 4) {
1833     if ($cn->data =~ /[^\x09-\x0D\x20]/) {
1834     last CN;
1835     }
1836     }
1837     } # CN
1838     unshift @$sib, @cn;
1839     }
1840     }
1841 wakaba 1.4 push @$new_todos, {type => 'element', node => $node};
1842 wakaba 1.2 } # TP
1843 wakaba 1.4 return ($sib, $new_todos);
1844 wakaba 1.2 } # _check_get_children
1845    
1846 wakaba 1.1 1;
1847 wakaba 1.6 # $Date: 2007/05/13 08:45:47 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24