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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.125 - (hide annotations) (download)
Thu Sep 11 02:28:29 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.124: +2 -0 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	11 Sep 2008 02:28:18 -0000
2008-09-11  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: |fieldset| |name| added (HTML5 revision 2162).

1 wakaba 1.1 package Whatpm::ContentChecker;
2     use strict;
3     require Whatpm::ContentChecker;
4    
5 wakaba 1.117 use Char::Class::XML qw/InXML_NCNameStartChar10 InXMLNCNameChar10/;
6    
7 wakaba 1.1 my $HTML_NS = q<http://www.w3.org/1999/xhtml>;
8    
9 wakaba 1.58 sub FEATURE_HTML5_ROLE () {
10     Whatpm::ContentChecker::FEATURE_STATUS_WD
11     ## TODO: svg:*/@role
12     }
13    
14 wakaba 1.89 sub FEATURE_HTML5_COMPLETE () {
15     Whatpm::ContentChecker::FEATURE_STATUS_REC |
16     Whatpm::ContentChecker::FEATURE_ALLOWED
17     }
18    
19 wakaba 1.54 sub FEATURE_HTML5_LC () {
20     Whatpm::ContentChecker::FEATURE_STATUS_LC |
21     Whatpm::ContentChecker::FEATURE_ALLOWED
22     }
23     sub FEATURE_HTML5_AT_RISK () {
24     Whatpm::ContentChecker::FEATURE_STATUS_WD |
25     Whatpm::ContentChecker::FEATURE_ALLOWED
26     }
27     sub FEATURE_HTML5_WD () {
28     Whatpm::ContentChecker::FEATURE_STATUS_WD |
29     Whatpm::ContentChecker::FEATURE_ALLOWED
30     }
31     sub FEATURE_HTML5_FD () {
32     Whatpm::ContentChecker::FEATURE_STATUS_WD |
33     Whatpm::ContentChecker::FEATURE_ALLOWED
34     }
35     sub FEATURE_HTML5_DEFAULT () {
36     Whatpm::ContentChecker::FEATURE_STATUS_WD |
37     Whatpm::ContentChecker::FEATURE_ALLOWED
38 wakaba 1.49 }
39 wakaba 1.54 sub FEATURE_HTML5_DROPPED () {
40     ## NOTE: Was part of HTML5, but was dropped.
41 wakaba 1.49 Whatpm::ContentChecker::FEATURE_STATUS_WD
42     }
43 wakaba 1.118 sub FEATURE_HTML5_LC_DROPPED () {
44     ## NOTE: Was part of HTML5, but was dropped.
45     Whatpm::ContentChecker::FEATURE_STATUS_LC
46     }
47 wakaba 1.119 sub FEATURE_WF2X () {
48     ## NOTE: Defined in WF2 and then incorporated into the HTML5 spec.
49     Whatpm::ContentChecker::FEATURE_STATUS_LC
50     }
51 wakaba 1.54 sub FEATURE_WF2 () {
52 wakaba 1.119 ## NOTE: Part of WF2, still not added to the HTML5 spec.
53     Whatpm::ContentChecker::FEATURE_STATUS_LC
54 wakaba 1.54 }
55 wakaba 1.119 sub FEATURE_WF2X_DEPRECATED () {
56 wakaba 1.56 Whatpm::ContentChecker::FEATURE_STATUS_LC
57 wakaba 1.119 ## NOTE: MUST NOT be used according to WF2.
58 wakaba 1.56 }
59 wakaba 1.49
60 wakaba 1.61 ## NOTE: Metainformation Attributes Module by W3C XHTML2 WG.
61 wakaba 1.121 sub FEATURE_RDFA_PR () {
62     Whatpm::ContentChecker::FEATURE_STATUS_CR
63     ## NOTE: Diff from FEATURE_RDFA_CR
64     }
65 wakaba 1.120 sub FEATURE_RDFA_CR () {
66     Whatpm::ContentChecker::FEATURE_STATUS_CR
67     }
68 wakaba 1.61 sub FEATURE_RDFA_LC () {
69     Whatpm::ContentChecker::FEATURE_STATUS_LC
70     }
71 wakaba 1.58
72 wakaba 1.82 sub FEATURE_RDFA_ED () {
73     Whatpm::ContentChecker::FEATURE_STATUS_WD
74     }
75    
76 wakaba 1.58 ## NOTE: XHTML Role LCWD has almost no information on how the |role|
77     ## attribute can be used- the only requirements for that matter is:
78     ## "the attribute MUST be referenced using its namespace-qualified form" (and
79     ## this is a host language conformance!).
80 wakaba 1.82 sub FEATURE_ROLE_LC () {
81     Whatpm::ContentChecker::FEATURE_STATUS_LC
82     }
83    
84     sub FEATURE_XHTML2_ED () {
85     Whatpm::ContentChecker::FEATURE_STATUS_WD
86     }
87 wakaba 1.58
88 wakaba 1.55 sub FEATURE_XHTMLBASIC11_CR () {
89     ## NOTE: Only additions to M12N10_REC are marked.
90     Whatpm::ContentChecker::FEATURE_STATUS_CR
91     }
92     sub FEATURE_XHTMLBASIC11_CR_DEPRECATED () {
93     Whatpm::ContentChecker::FEATURE_STATUS_CR |
94     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
95     }
96    
97 wakaba 1.82 sub FEATURE_M12N11_LC () {
98     Whatpm::ContentChecker::FEATURE_STATUS_LC
99     }
100    
101 wakaba 1.99 sub FEATURE_RUBY_REC () {
102     Whatpm::ContentChecker::FEATURE_STATUS_CR
103     }
104    
105 wakaba 1.49 ## NOTE: M12N10 status is based on its abstract module definition.
106     ## It contains a number of problems. (However, again, it's a REC!)
107 wakaba 1.54 sub FEATURE_M12N10_REC () {
108     ## NOTE: Oh, XHTML m12n 1.0 passed the CR phase! W3C Process suck!
109     Whatpm::ContentChecker::FEATURE_STATUS_REC
110     }
111     sub FEATURE_M12N10_REC_DEPRECATED () {
112     Whatpm::ContentChecker::FEATURE_STATUS_REC |
113     Whatpm::ContentChecker::FEATURE_DEPRECATED_INFO
114     }
115 wakaba 1.120 ## NOTE: XHTML M12N 1.1 is in the PR status at the time of writing and no
116 wakaba 1.58 ## addition from 1.0.
117 wakaba 1.49
118     ## NOTE: XHTML10 status is based on its transitional and frameset DTDs
119     ## (second edition). Only missing attributes from M12N10 abstract
120     ## definition are added.
121 wakaba 1.54 sub FEATURE_XHTML10_REC () {
122     Whatpm::ContentChecker::FEATURE_STATUS_CR
123     }
124    
125 wakaba 1.61 ## NOTE: Diff from HTML4.
126     sub FEATURE_ISOHTML_PREPARATION () { ## Informative documentation
127     Whatpm::ContentChecker::FEATURE_STATUS_CR
128     }
129 wakaba 1.58
130 wakaba 1.49 ## NOTE: HTML4 status is based on its transitional and frameset DTDs (HTML
131     ## 4.01). Only missing attributes from XHTML10 are added.
132 wakaba 1.54 sub FEATURE_HTML4_REC_RESERVED () {
133     Whatpm::ContentChecker::FEATURE_STATUS_WD
134     }
135    
136     ## TODO: According to HTML4 definition, authors SHOULD use style sheets
137     ## rather than presentational attributes (deprecated or not deprecated).
138 wakaba 1.48
139 wakaba 1.61 ## NOTE: Diff from HTML4.
140     sub FEATURE_HTML32_REC_OBSOLETE () {
141     Whatpm::ContentChecker::FEATURE_STATUS_CR |
142     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD
143     ## NOTE: Lowercase normative "should".
144     }
145    
146     sub FEATURE_RFC2659 () { ## Experimental RFC
147     Whatpm::ContentChecker::FEATURE_STATUS_CR
148     }
149    
150     ## NOTE: HTML 2.x - diff from HTML 2.0 and not in newer versions.
151     sub FEATURE_HTML2X_RFC () { ## Proposed Standard, obsolete
152     Whatpm::ContentChecker::FEATURE_STATUS_CR
153     }
154    
155     ## NOTE: Diff from HTML 2.0.
156     sub FEATURE_RFC1942 () { ## Experimental RFC, obsolete
157     Whatpm::ContentChecker::FEATURE_STATUS_CR
158     }
159    
160     ## NOTE: Diff from HTML 3.2.
161     sub FEATURE_HTML20_RFC () { ## Proposed Standard, obsolete
162     Whatpm::ContentChecker::FEATURE_STATUS_CR
163     }
164 wakaba 1.58
165 wakaba 1.29 ## December 2007 HTML5 Classification
166    
167     my $HTMLMetadataContent = {
168     $HTML_NS => {
169     title => 1, base => 1, link => 1, style => 1, script => 1, noscript => 1,
170 wakaba 1.118 'event-source' => 1, eventsource => 1,
171     command => 1, datatemplate => 1,
172 wakaba 1.29 ## NOTE: A |meta| with no |name| element is not allowed as
173     ## a metadata content other than |head| element.
174     meta => 1,
175     },
176     ## NOTE: RDF is mentioned in the HTML5 spec.
177     ## TODO: Other RDF elements?
178     q<http://www.w3.org/1999/02/22-rdf-syntax-ns#> => {RDF => 1},
179     };
180    
181 wakaba 1.72 my $HTMLFlowContent = {
182 wakaba 1.29 $HTML_NS => {
183     section => 1, nav => 1, article => 1, blockquote => 1, aside => 1,
184     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
185     footer => 1, address => 1, p => 1, hr => 1, dialog => 1, pre => 1,
186     ol => 1, ul => 1, dl => 1, figure => 1, map => 1, table => 1,
187 wakaba 1.119 form => 1, fieldset => 1,
188 wakaba 1.72 details => 1, ## ISSUE: "Flow element" in spec.
189     datagrid => 1, ## ISSUE: "Flow element" in spec.
190 wakaba 1.29 datatemplate => 1,
191     div => 1, ## ISSUE: No category in spec.
192     ## NOTE: |style| is only allowed if |scoped| attribute is specified.
193     ## Additionally, it must be before any other element or
194     ## non-inter-element-whitespace text node.
195     style => 1,
196    
197 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
198 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
199     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
200 wakaba 1.99 b => 1, bdo => 1, ruby => 1,
201 wakaba 1.118 script => 1, noscript => 1, 'event-source' => 1, eventsource => 1,
202     command => 1, bb => 1,
203 wakaba 1.119 input => 1, button => 1, label => 1, select => 1, datalist => 1,
204 wakaba 1.121 textarea => 1, output => 1,
205 wakaba 1.29 datagrid => 1, ## ISSUE: "Interactive element" in the spec.
206     ## NOTE: |area| is allowed only as a descendant of |map|.
207     area => 1,
208    
209 wakaba 1.124 ## NOTE: Transparent.
210     a => 1, ins => 1, del => 1, font => 1,
211 wakaba 1.29
212 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
213 wakaba 1.29 menu => 1,
214    
215     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
216     canvas => 1,
217     },
218    
219     ## NOTE: Embedded
220     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
221     q<http://www.w3.org/2000/svg> => {svg => 1},
222     };
223    
224 wakaba 1.58 my $HTMLSectioningContent = {
225 wakaba 1.57 $HTML_NS => {
226     section => 1, nav => 1, article => 1, aside => 1,
227     ## NOTE: |body| is only allowed in |html| element.
228     body => 1,
229     },
230     };
231    
232 wakaba 1.58 my $HTMLSectioningRoot = {
233 wakaba 1.29 $HTML_NS => {
234 wakaba 1.58 blockquote => 1, datagrid => 1, figure => 1, td => 1,
235 wakaba 1.29 },
236     };
237    
238     my $HTMLHeadingContent = {
239     $HTML_NS => {
240     h1 => 1, h2 => 1, h3 => 1, h4 => 1, h5 => 1, h6 => 1, header => 1,
241     },
242     };
243    
244     my $HTMLPhrasingContent = {
245 wakaba 1.72 ## NOTE: All phrasing content is also flow content.
246 wakaba 1.29 $HTML_NS => {
247 wakaba 1.38 br => 1, q => 1, cite => 1, em => 1, strong => 1, small => 1, mark => 1,
248 wakaba 1.29 dfn => 1, abbr => 1, time => 1, progress => 1, meter => 1, code => 1,
249     var => 1, samp => 1, kbd => 1, sub => 1, sup => 1, span => 1, i => 1,
250 wakaba 1.99 b => 1, bdo => 1, ruby => 1,
251 wakaba 1.118 script => 1, noscript => 1, 'event-source' => 1, eventsource => 1,
252     command => 1, bb => 1,
253 wakaba 1.119 input => 1, button => 1, label => 1, select => 1, datalist => 1,
254 wakaba 1.121 textarea => 1, output => 1,
255 wakaba 1.29 datagrid => 1, ## ISSUE: "Interactive element" in the spec.
256     ## NOTE: |area| is allowed only as a descendant of |map|.
257     area => 1,
258    
259     ## NOTE: Transparent.
260 wakaba 1.124 a => 1, ins => 1, del => 1, font => 1,
261 wakaba 1.29
262 wakaba 1.72 ## NOTE: If there is a |menu| ancestor, phrasing. Otherwise, flow.
263 wakaba 1.29 menu => 1,
264    
265     img => 1, iframe => 1, embed => 1, object => 1, video => 1, audio => 1,
266     canvas => 1,
267     },
268    
269     ## NOTE: Embedded
270     q<http://www.w3.org/1998/Math/MathML> => {math => 1},
271     q<http://www.w3.org/2000/svg> => {svg => 1},
272    
273     ## NOTE: And non-inter-element-whitespace text nodes.
274     };
275    
276 wakaba 1.40 ## $HTMLEmbeddedContent: See Whatpm::ContentChecker.
277 wakaba 1.29
278     my $HTMLInteractiveContent = {
279     $HTML_NS => {
280     a => 1,
281 wakaba 1.121 input => 1, button => 1, label => 1, select => 1, textarea => 1,
282 wakaba 1.36 datagrid => 1, ## ISSUE: Categorized as "Inetractive element"
283 wakaba 1.115 bb => 1,
284 wakaba 1.29 },
285     };
286    
287 wakaba 1.36 ## NOTE: $HTMLTransparentElements: See Whatpm::ContentChecker.
288     ## NOTE: Semi-transparent elements: See Whatpm::ContentChecker.
289    
290     ## -- Common attribute syntacx checkers
291    
292 wakaba 1.1 our $AttrChecker;
293 wakaba 1.82 our $AttrStatus;
294 wakaba 1.1
295     my $GetHTMLEnumeratedAttrChecker = sub {
296     my $states = shift; # {value => conforming ? 1 : -1}
297     return sub {
298     my ($self, $attr) = @_;
299     my $value = lc $attr->value; ## TODO: ASCII case insensitibility?
300     if ($states->{$value} > 0) {
301     #
302     } elsif ($states->{$value}) {
303 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'enumerated:non-conforming',
304     level => $self->{level}->{must});
305 wakaba 1.1 } else {
306 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'enumerated:invalid',
307     level => $self->{level}->{must});
308 wakaba 1.1 }
309     };
310     }; # $GetHTMLEnumeratedAttrChecker
311    
312     my $GetHTMLBooleanAttrChecker = sub {
313     my $local_name = shift;
314     return sub {
315     my ($self, $attr) = @_;
316 wakaba 1.88 my $value = lc $attr->value; ## TODO: case
317 wakaba 1.1 unless ($value eq $local_name or $value eq '') {
318 wakaba 1.88 $self->{onerror}->(node => $attr, type => 'boolean:invalid',
319 wakaba 1.104 level => $self->{level}->{must});
320 wakaba 1.1 }
321     };
322     }; # $GetHTMLBooleanAttrChecker
323    
324 wakaba 1.8 ## Unordered set of space-separated tokens
325 wakaba 1.92 my $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker = sub {
326     my $allowed_words = shift;
327     return sub {
328     my ($self, $attr) = @_;
329     my %word;
330 wakaba 1.96 for my $word (grep {length $_} split /[\x09-\x0D\x20]+/, $attr->value) {
331 wakaba 1.92 unless ($word{$word}) {
332     $word{$word} = 1;
333     if (not defined $allowed_words or
334     $allowed_words->{$word}) {
335     #
336     } else {
337 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'word not allowed',
338 wakaba 1.92 value => $word,
339 wakaba 1.104 level => $self->{level}->{must});
340 wakaba 1.92 }
341     } else {
342 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
343     value => $word,
344     level => $self->{level}->{must});
345 wakaba 1.92 }
346 wakaba 1.8 }
347 wakaba 1.92 };
348     }; # $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker
349 wakaba 1.8
350 wakaba 1.1 ## |rel| attribute (unordered set of space separated tokens,
351     ## whose allowed values are defined by the section on link types)
352     my $HTMLLinkTypesAttrChecker = sub {
353 wakaba 1.66 my ($a_or_area, $todo, $self, $attr, $item, $element_state) = @_;
354 wakaba 1.1 my %word;
355 wakaba 1.96 for my $word (grep {length $_} split /[\x09-\x0D\x20]+/, $attr->value) {
356 wakaba 1.1 unless ($word{$word}) {
357     $word{$word} = 1;
358 wakaba 1.18 } elsif ($word eq 'up') {
359     #
360 wakaba 1.1 } else {
361 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
362     value => $word,
363     level => $self->{level}->{must});
364 wakaba 1.1 }
365     }
366     ## NOTE: Case sensitive match (since HTML5 spec does not say link
367     ## types are case-insensitive and it says "The value should not
368     ## be confusingly similar to any other defined value (e.g.
369     ## differing only in case).").
370     ## NOTE: Though there is no explicit "MUST NOT" for undefined values,
371     ## "MAY"s and "only ... MAY" restrict non-standard non-registered
372     ## values to be used conformingly.
373 wakaba 1.66
374     my $is_hyperlink;
375     my $is_resource;
376 wakaba 1.1 require Whatpm::_LinkTypeList;
377     our $LinkType;
378     for my $word (keys %word) {
379     my $def = $LinkType->{$word};
380     if (defined $def) {
381     if ($def->{status} eq 'accepted') {
382     if (defined $def->{effect}->[$a_or_area]) {
383     #
384     } else {
385     $self->{onerror}->(node => $attr,
386 wakaba 1.104 type => 'link type:bad context',
387     value => $word,
388 wakaba 1.110 level => $self->{level}->{must});
389 wakaba 1.1 }
390     } elsif ($def->{status} eq 'proposal') {
391 wakaba 1.104 $self->{onerror}->(node => $attr,
392     type => 'link type:proposed',
393     value => $word,
394     level => $self->{level}->{should});
395 wakaba 1.20 if (defined $def->{effect}->[$a_or_area]) {
396     #
397     } else {
398     $self->{onerror}->(node => $attr,
399 wakaba 1.104 type => 'link type:bad context',
400     value => $word,
401     level => $self->{level}->{must});
402 wakaba 1.20 }
403 wakaba 1.1 } else { # rejected or synonym
404     $self->{onerror}->(node => $attr,
405 wakaba 1.104 type => 'link type:non-conforming',
406     value => $word,
407     level => $self->{level}->{must});
408 wakaba 1.1 }
409 wakaba 1.4 if (defined $def->{effect}->[$a_or_area]) {
410     if ($word eq 'alternate') {
411     #
412     } elsif ($def->{effect}->[$a_or_area] eq 'hyperlink') {
413 wakaba 1.66 $is_hyperlink = 1;
414 wakaba 1.4 }
415     }
416 wakaba 1.1 if ($def->{unique}) {
417     unless ($self->{has_link_type}->{$word}) {
418     $self->{has_link_type}->{$word} = 1;
419     } else {
420     $self->{onerror}->(node => $attr,
421 wakaba 1.104 type => 'link type:duplicate',
422     value => $word,
423     level => $self->{level}->{must});
424 wakaba 1.1 }
425     }
426 wakaba 1.66
427     if (defined $def->{effect}->[$a_or_area] and $word ne 'alternate') {
428     $is_hyperlink = 1 if $def->{effect}->[$a_or_area] eq 'hyperlink';
429     $is_resource = 1 if $def->{effect}->[$a_or_area] eq 'external resource';
430     }
431 wakaba 1.1 } else {
432 wakaba 1.104 $self->{onerror}->(node => $attr,
433     type => 'unknown link type',
434     value => $word,
435     level => $self->{level}->{uncertain});
436 wakaba 1.1 }
437     }
438 wakaba 1.66 $is_hyperlink = 1 if $word{alternate} and not $word{stylesheet};
439 wakaba 1.1 ## TODO: The Pingback 1.0 specification, which is referenced by HTML5,
440     ## says that using both X-Pingback: header field and HTML
441     ## <link rel=pingback> is deprecated and if both appears they
442     ## SHOULD contain exactly the same value.
443     ## ISSUE: Pingback 1.0 specification defines the exact representation
444     ## of its link element, which cannot be tested by the current arch.
445     ## ISSUE: Pingback 1.0 specification says that the document MUST NOT
446     ## include any string that matches to the pattern for the rel=pingback link,
447     ## which again inpossible to test.
448     ## ISSUE: rel=pingback href MUST NOT include entities other than predefined 4.
449 wakaba 1.12
450     ## NOTE: <link rel="up index"><link rel="up up index"> is not an error.
451 wakaba 1.17 ## NOTE: We can't check "If the page is part of multiple hierarchies,
452     ## then they SHOULD be described in different paragraphs.".
453 wakaba 1.66
454     $todo->{has_hyperlink_link_type} = 1 if $is_hyperlink;
455     if ($is_hyperlink or $a_or_area) {
456     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
457     }
458     if ($is_resource and not $a_or_area) {
459     $element_state->{uri_info}->{href}->{type}->{resource} = 1;
460     }
461 wakaba 1.96
462     $element_state->{link_rel} = \%word;
463 wakaba 1.1 }; # $HTMLLinkTypesAttrChecker
464 wakaba 1.20
465     ## TODO: "When an author uses a new type not defined by either this specification or the Wiki page, conformance checkers should offer to add the value to the Wiki, with the details described above, with the "proposal" status."
466 wakaba 1.1
467     ## URI (or IRI)
468     my $HTMLURIAttrChecker = sub {
469 wakaba 1.66 my ($self, $attr, $item, $element_state) = @_;
470 wakaba 1.1 ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
471     my $value = $attr->value;
472     Whatpm::URIChecker->check_iri_reference ($value, sub {
473 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
474 wakaba 1.106 }), $self->{level};
475 wakaba 1.17 $self->{has_uri_attr} = 1; ## TODO: <html manifest>
476 wakaba 1.66
477     my $attr_name = $attr->name;
478     $element_state->{uri_info}->{$attr_name}->{node} = $attr;
479     ## TODO: absolute
480     push @{$self->{return}->{uri}->{$value} ||= []},
481     $element_state->{uri_info}->{$attr_name};
482 wakaba 1.1 }; # $HTMLURIAttrChecker
483    
484     ## A space separated list of one or more URIs (or IRIs)
485     my $HTMLSpaceURIsAttrChecker = sub {
486     my ($self, $attr) = @_;
487 wakaba 1.66
488     my $type = {ping => 'action',
489     profile => 'namespace',
490     archive => 'resource'}->{$attr->name};
491    
492 wakaba 1.1 my $i = 0;
493     for my $value (split /[\x09-\x0D\x20]+/, $attr->value) {
494     Whatpm::URIChecker->check_iri_reference ($value, sub {
495 wakaba 1.104 $self->{onerror}->(value => $value, @_, node => $attr, index => $i);
496 wakaba 1.106 }, $self->{level});
497 wakaba 1.66
498     ## TODO: absolute
499     push @{$self->{return}->{uri}->{$value} ||= []},
500 wakaba 1.67 {node => $attr, type => {$type => 1}};
501 wakaba 1.66
502 wakaba 1.1 $i++;
503     }
504 wakaba 1.67 ## ISSUE: Relative references? (especially, in profile="")
505 wakaba 1.1 ## ISSUE: Leading or trailing white spaces are conformant?
506     ## ISSUE: A sequence of white space characters are conformant?
507     ## ISSUE: A zero-length string is conformant? (It does contain a relative reference, i.e. same as base URI.)
508     ## NOTE: Duplication seems not an error.
509 wakaba 1.4 $self->{has_uri_attr} = 1;
510 wakaba 1.1 }; # $HTMLSpaceURIsAttrChecker
511    
512     my $HTMLDatetimeAttrChecker = sub {
513     my ($self, $attr) = @_;
514     my $value = $attr->value;
515     ## ISSUE: "space", not "space character" (in parsing algorihtm, "space character")
516     if ($value =~ /\A([0-9]{4})-([0-9]{2})-([0-9]{2})(?>[\x09-\x0D\x20]+(?>T[\x09-\x0D\x20]*)?|T[\x09-\x0D\x20]*)([0-9]{2}):([0-9]{2})(?>:([0-9]{2}))?(?>\.([0-9]+))?[\x09-\x0D\x20]*(?>Z|[+-]([0-9]{2}):([0-9]{2}))\z/) {
517     my ($y, $M, $d, $h, $m, $s, $f, $zh, $zm)
518     = ($1, $2, $3, $4, $5, $6, $7, $8, $9);
519     if (0 < $M and $M < 13) { ## ISSUE: This is not explicitly specified (though in parsing algorithm)
520 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad day',
521     level => $self->{level}->{must})
522 wakaba 1.1 if $d < 1 or
523     $d > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$M];
524 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad day',
525     level => $self->{level}->{must})
526 wakaba 1.1 if $M == 2 and $d == 29 and
527     not ($y % 400 == 0 or ($y % 4 == 0 and $y % 100 != 0));
528     } else {
529 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad month',
530     level => $self->{level}->{must});
531 wakaba 1.1 }
532 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad hour',
533     level => $self->{level}->{must}) if $h > 23;
534     $self->{onerror}->(node => $attr, type => 'datetime:bad minute',
535     level => $self->{level}->{must}) if $m > 59;
536     $self->{onerror}->(node => $attr, type => 'datetime:bad second',
537     level => $self->{level}->{must})
538 wakaba 1.1 if defined $s and $s > 59;
539 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:bad timezone hour',
540     level => $self->{level}->{must}) if $zh > 23;
541     $self->{onerror}->(node => $attr, type => 'datetime:bad timezone minute',
542     level => $self->{level}->{must}) if $zm > 59;
543 wakaba 1.1 ## ISSUE: Maybe timezone -00:00 should have same semantics as in RFC 3339.
544     } else {
545 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'datetime:syntax error',
546     level => $self->{level}->{must});
547 wakaba 1.1 }
548     }; # $HTMLDatetimeAttrChecker
549    
550     my $HTMLIntegerAttrChecker = sub {
551     my ($self, $attr) = @_;
552     my $value = $attr->value;
553     unless ($value =~ /\A-?[0-9]+\z/) {
554 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'integer:syntax error',
555     level => $self->{level}->{must});
556 wakaba 1.1 }
557     }; # $HTMLIntegerAttrChecker
558    
559     my $GetHTMLNonNegativeIntegerAttrChecker = sub {
560     my $range_check = shift;
561     return sub {
562     my ($self, $attr) = @_;
563     my $value = $attr->value;
564     if ($value =~ /\A[0-9]+\z/) {
565     unless ($range_check->($value + 0)) {
566 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'nninteger:out of range',
567     level => $self->{level}->{must});
568 wakaba 1.1 }
569     } else {
570     $self->{onerror}->(node => $attr,
571 wakaba 1.104 type => 'nninteger:syntax error',
572     level => $self->{level}->{must});
573 wakaba 1.1 }
574     };
575     }; # $GetHTMLNonNegativeIntegerAttrChecker
576    
577     my $GetHTMLFloatingPointNumberAttrChecker = sub {
578     my $range_check = shift;
579     return sub {
580     my ($self, $attr) = @_;
581     my $value = $attr->value;
582 wakaba 1.90 if ($value =~ /\A-?[0-9]+(?>\.[0-9]*)?\z/ or
583     $value =~ /\A-?\.[0-9]+\z/) {
584 wakaba 1.1 unless ($range_check->($value + 0)) {
585 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'float:out of range',
586     level => $self->{level}->{must});
587 wakaba 1.1 }
588     } else {
589     $self->{onerror}->(node => $attr,
590 wakaba 1.104 type => 'float:syntax error',
591     level => $self->{level}->{must});
592 wakaba 1.1 }
593     };
594     }; # $GetHTMLFloatingPointNumberAttrChecker
595    
596 wakaba 1.86 ## HTML4 %Length;
597     my $HTMLLengthAttrChecker = sub {
598     my ($self, $attr) = @_;
599     my $value = $attr->value;
600     unless ($value =~ /\A[0-9]+%?\z/) {
601     $self->{onerror}->(node => $attr, type => 'length:syntax error',
602 wakaba 1.104 level => $self->{level}->{must});
603 wakaba 1.86 }
604    
605     ## NOTE: HTML4 definition is too vague - it does not define the syntax
606     ## of percentage value at all (!).
607     }; # $HTMLLengthAttrChecker
608    
609 wakaba 1.1 ## "A valid MIME type, optionally with parameters. [RFC 2046]"
610     ## ISSUE: RFC 2046 does not define syntax of media types.
611     ## ISSUE: The definition of "a valid MIME type" is unknown.
612     ## Syntactical correctness?
613     my $HTMLIMTAttrChecker = sub {
614     my ($self, $attr) = @_;
615     my $value = $attr->value;
616     ## ISSUE: RFC 2045 Content-Type header field allows insertion
617     ## of LWS/comments between tokens. Is it allowed in HTML? Maybe no.
618     ## ISSUE: RFC 2231 extension? Maybe no.
619     my $lws0 = qr/(?>(?>\x0D\x0A)?[\x09\x20])*/;
620     my $token = qr/[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+/;
621     my $qs = qr/"(?>[\x00-\x0C\x0E-\x21\x23-\x5B\x5D-\x7E]|\x0D\x0A[\x09\x20]|\x5C[\x00-\x7F])*"/;
622     if ($value =~ m#\A$lws0($token)$lws0/$lws0($token)$lws0((?>;$lws0$token$lws0=$lws0(?>$token|$qs)$lws0)*)\z#) {
623     my @type = ($1, $2);
624     my $param = $3;
625     while ($param =~ s/^;$lws0($token)$lws0=$lws0(?>($token)|($qs))$lws0//) {
626     if (defined $2) {
627     push @type, $1 => $2;
628     } else {
629     my $n = $1;
630     my $v = $2;
631     $v =~ s/\\(.)/$1/gs;
632     push @type, $n => $v;
633     }
634     }
635     require Whatpm::IMTChecker;
636 wakaba 1.109 my $ic = Whatpm::IMTChecker->new;
637     $ic->{level} = $self->{level};
638     $ic->check_imt (sub {
639 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
640 wakaba 1.1 }, @type);
641     } else {
642 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'IMT:syntax error',
643     level => $self->{level}->{must});
644 wakaba 1.1 }
645     }; # $HTMLIMTAttrChecker
646    
647     my $HTMLLanguageTagAttrChecker = sub {
648 wakaba 1.7 ## NOTE: See also $AtomLanguageTagAttrChecker in Atom.pm.
649    
650 wakaba 1.1 my ($self, $attr) = @_;
651 wakaba 1.6 my $value = $attr->value;
652     require Whatpm::LangTag;
653     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
654 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
655 wakaba 1.106 }, $self->{level});
656 wakaba 1.1 ## ISSUE: RFC 4646 (3066bis)?
657 wakaba 1.6
658     ## TODO: testdata
659 wakaba 1.1 }; # $HTMLLanguageTagAttrChecker
660    
661     ## "A valid media query [MQ]"
662     my $HTMLMQAttrChecker = sub {
663     my ($self, $attr) = @_;
664 wakaba 1.104 $self->{onerror}->(node => $attr,
665     type => 'media query',
666     level => $self->{level}->{uncertain});
667 wakaba 1.1 ## ISSUE: What is "a valid media query"?
668     }; # $HTMLMQAttrChecker
669    
670     my $HTMLEventHandlerAttrChecker = sub {
671     my ($self, $attr) = @_;
672 wakaba 1.104 $self->{onerror}->(node => $attr,
673     type => 'event handler',
674     level => $self->{level}->{uncertain});
675 wakaba 1.1 ## TODO: MUST contain valid ECMAScript code matching the
676     ## ECMAScript |FunctionBody| production. [ECMA262]
677     ## ISSUE: MUST be ES3? E4X? ES4? JS1.x?
678     ## ISSUE: Automatic semicolon insertion does not apply?
679     ## ISSUE: Other script languages?
680     }; # $HTMLEventHandlerAttrChecker
681    
682     my $HTMLUsemapAttrChecker = sub {
683     my ($self, $attr) = @_;
684 wakaba 1.100 ## MUST be a valid hash-name reference to a |map| element.
685 wakaba 1.1 my $value = $attr->value;
686     if ($value =~ s/^#//) {
687 wakaba 1.100 ## NOTE: |usemap="#"| is conforming, though it identifies no |map| element
688     ## according to the "rules for parsing a hash-name reference" algorithm.
689     ## The document is non-conforming anyway, since |<map name="">| (empty
690     ## name) is non-conforming.
691 wakaba 1.1 push @{$self->{usemap}}, [$value => $attr];
692     } else {
693 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'hashref:syntax error',
694     level => $self->{level}->{must});
695 wakaba 1.1 }
696 wakaba 1.100 ## NOTE: Space characters in hash-name references are conforming.
697 wakaba 1.1 ## ISSUE: UA algorithm for matching is case-insensitive; IDs only different in cases should be reported
698     }; # $HTMLUsemapAttrChecker
699    
700 wakaba 1.76 ## Valid browsing context name
701     my $HTMLBrowsingContextNameAttrChecker = sub {
702     my ($self, $attr) = @_;
703     my $value = $attr->value;
704     if ($value =~ /^_/) {
705     $self->{onerror}->(node => $attr, type => 'window name:reserved',
706 wakaba 1.104 level => $self->{level}->{must},
707 wakaba 1.76 value => $value);
708     } elsif (length $value) {
709     #
710     } else {
711     $self->{onerror}->(node => $attr, type => 'window name:empty',
712 wakaba 1.104 level => $self->{level}->{must});
713 wakaba 1.76 }
714     }; # $HTMLBrowsingContextNameAttrChecker
715    
716     ## Valid browsing context name or keyword
717 wakaba 1.1 my $HTMLTargetAttrChecker = sub {
718     my ($self, $attr) = @_;
719     my $value = $attr->value;
720     if ($value =~ /^_/) {
721     $value = lc $value; ## ISSUE: ASCII case-insentitive?
722     unless ({
723 wakaba 1.76 _blank => 1,_self => 1, _parent => 1, _top => 1,
724 wakaba 1.1 }->{$value}) {
725     $self->{onerror}->(node => $attr,
726 wakaba 1.76 type => 'window name:reserved',
727 wakaba 1.104 level => $self->{level}->{must},
728 wakaba 1.76 value => $value);
729 wakaba 1.1 }
730 wakaba 1.76 } elsif (length $value) {
731     #
732 wakaba 1.1 } else {
733 wakaba 1.76 $self->{onerror}->(node => $attr, type => 'window name:empty',
734 wakaba 1.104 level => $self->{level}->{must});
735 wakaba 1.1 }
736     }; # $HTMLTargetAttrChecker
737    
738 wakaba 1.23 my $HTMLSelectorsAttrChecker = sub {
739     my ($self, $attr) = @_;
740    
741     ## ISSUE: Namespace resolution?
742    
743     my $value = $attr->value;
744    
745     require Whatpm::CSS::SelectorsParser;
746     my $p = Whatpm::CSS::SelectorsParser->new;
747     $p->{pseudo_class}->{$_} = 1 for qw/
748     active checked disabled empty enabled first-child first-of-type
749     focus hover indeterminate last-child last-of-type link only-child
750     only-of-type root target visited
751     lang nth-child nth-last-child nth-of-type nth-last-of-type not
752     -manakai-contains -manakai-current
753     /;
754    
755     $p->{pseudo_element}->{$_} = 1 for qw/
756     after before first-letter first-line
757     /;
758    
759 wakaba 1.104 $p->{level} = $self->{level};
760 wakaba 1.23 $p->{onerror} = sub {
761 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
762 wakaba 1.23 };
763     $p->parse_string ($value);
764     }; # $HTMLSelectorsAttrChecker
765    
766 wakaba 1.66 my $HTMLAccesskeyAttrChecker = sub {
767     my ($self, $attr) = @_;
768    
769     ## NOTE: "character" or |%Character;| in HTML4.
770    
771     my $value = $attr->value;
772     if (length $value != 1) {
773     $self->{onerror}->(node => $attr, type => 'char:syntax error',
774 wakaba 1.105 level => $self->{level}->{html4_fact});
775 wakaba 1.66 }
776    
777     ## NOTE: "Note. Authors should consider the input method of the expected
778     ## reader when specifying an accesskey." [HTML4] This is hard to implement,
779     ## since it depends on keyboard and so on.
780     ## NOTE: "We recommend that authors include the access key in label text
781     ## or wherever the access key is to apply." [HTML4] (informative)
782     }; # $HTMLAccesskeyAttrChecker
783    
784 wakaba 1.91 my $HTMLCharsetChecker = sub {
785     my ($charset_value, $self, $attr) = @_;
786    
787     ## NOTE: Though the case-sensitivility of |charset| attribute value
788     ## is not explicitly spelled in the HTML5 spec, the Character Set
789     ## registry of IANA, which is referenced from HTML5 spec, says that
790     ## charset name is case-insensitive.
791     $charset_value =~ tr/A-Z/a-z/; ## NOTE: ASCII Case-insensitive.
792    
793     require Message::Charset::Info;
794     my $charset = $Message::Charset::Info::IANACharset->{$charset_value};
795    
796     ## ISSUE: What is "valid character encoding name"? Syntactically valid?
797     ## Syntactically valid and registered? What about x-charset names?
798     unless (Message::Charset::Info::is_syntactically_valid_iana_charset_name
799     ($charset_value)) {
800     $self->{onerror}->(node => $attr,
801 wakaba 1.104 type => 'charset:syntax error',
802     value => $charset_value,
803     level => $self->{level}->{must});
804 wakaba 1.91 }
805    
806     if ($charset) {
807     ## ISSUE: What is "the preferred name for that encoding" (for a charset
808     ## with no "preferred MIME name" label)?
809     my $charset_status = $charset->{iana_names}->{$charset_value} || 0;
810     if (($charset_status &
811     Message::Charset::Info::PREFERRED_CHARSET_NAME ())
812     != Message::Charset::Info::PREFERRED_CHARSET_NAME ()) {
813     $self->{onerror}->(node => $attr,
814 wakaba 1.104 type => 'charset:not preferred',
815     value => $charset_value,
816     level => $self->{level}->{must});
817 wakaba 1.91 }
818     if (($charset_status &
819     Message::Charset::Info::REGISTERED_CHARSET_NAME ())
820     != Message::Charset::Info::REGISTERED_CHARSET_NAME ()) {
821     if ($charset_value =~ /^x-/) {
822     $self->{onerror}->(node => $attr,
823 wakaba 1.104 type => 'charset:private',
824     value => $charset_value,
825     level => $self->{level}->{good});
826 wakaba 1.91 } else {
827     $self->{onerror}->(node => $attr,
828 wakaba 1.104 type => 'charset:not registered',
829     value => $charset_value,
830     level => $self->{level}->{good});
831 wakaba 1.91 }
832     }
833     ## TODO: non-preferred-name error for following cases:
834     } elsif ($charset_value =~ /^x-/) {
835     $self->{onerror}->(node => $attr,
836 wakaba 1.104 type => 'charset:private',
837     value => $charset_value,
838     level => $self->{level}->{good});
839 wakaba 1.91 } else {
840     $self->{onerror}->(node => $attr,
841 wakaba 1.104 type => 'charset:not registered',
842     value => $charset_value,
843     level => $self->{level}->{good});
844 wakaba 1.91 }
845    
846     return ($charset, $charset_value);
847     }; # $HTMLCharsetChecker
848    
849 wakaba 1.68 my $HTMLColorAttrChecker = sub {
850     my ($self, $attr) = @_;
851    
852     ## NOTE: HTML4 "color" or |%Color;|
853    
854     my $value = $attr->value;
855    
856     if ($value !~ /\A(?>#[0-9A-F]+|black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua)\z/i) {
857 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'color:syntax error',
858 wakaba 1.105 level => $self->{level}->{html4_fact});
859 wakaba 1.68 }
860    
861     ## TODO: HTML4 has some guideline on usage of color.
862     }; # $HTMLColorAttrChecker
863    
864 wakaba 1.79 my $HTMLRefOrTemplateAttrChecker = sub {
865     my ($self, $attr) = @_;
866     $HTMLURIAttrChecker->(@_);
867    
868     my $attr_name = $attr->name;
869    
870     if ($attr_name eq 'ref') {
871     unless ($attr->owner_element->has_attribute_ns (undef, 'template')) {
872     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
873 wakaba 1.104 level => $self->{level}->{must});
874 wakaba 1.79 }
875     }
876    
877     my $doc = $attr->owner_document;
878     my $doc_uri = $doc->document_uri;
879     my $uri = $doc->implementation
880     ->create_uri_reference ($attr->value)
881     ->get_absolute_reference ($doc_uri);
882     my $no_frag_uri = $uri->clone;
883     $no_frag_uri->uri_fragment (undef);
884     if ((defined $doc_uri and $doc_uri eq $no_frag_uri) or
885     (not defined $doc_uri and $no_frag_uri eq '')) {
886     my $fragid = $uri->uri_fragment;
887     if (defined $fragid) {
888     push @{$self->{$attr_name}}, [$fragid => $attr];
889     } else {
890     DOCEL: {
891     last DOCEL unless $attr_name eq 'template';
892    
893     my $docel = $doc->document_element;
894     if ($docel) {
895     my $nsuri = $docel->namespace_uri;
896     if (defined $nsuri and $nsuri eq $HTML_NS) {
897     if ($docel->manakai_local_name eq 'datatemplate') {
898     last DOCEL;
899     }
900     }
901     }
902    
903     $self->{onerror}->(node => $attr, type => 'template:not template',
904 wakaba 1.104 level => $self->{level}->{must});
905 wakaba 1.79 } # DOCEL
906     }
907     } else {
908     ## TODO: An external document is referenced.
909     ## The document MUST be an HTML or XML document.
910     ## If there is a fragment identifier, it MUST point a part of the doc.
911     ## If the attribute is |template|, the pointed part MUST be a
912     ## |datatemplat| element.
913     ## If no fragment identifier is specified, the root element MUST be
914     ## a |datatemplate| element when the attribute is |template|.
915     }
916     }; # $HTMLRefOrTemplateAttrChecker
917    
918 wakaba 1.83 my $HTMLRepeatIndexAttrChecker = sub {
919     my ($self, $attr) = @_;
920    
921     if (defined $attr->namespace_uri) {
922     my $oe = $attr->owner_element;
923     my $oe_nsuri = $oe->namespace_uri;
924     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
925     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
926 wakaba 1.104 level => $self->{level}->{must});
927 wakaba 1.83 }
928     }
929    
930     $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 })->(@_);
931     }; # $HTMLRepeatIndexAttrChecker
932    
933 wakaba 1.1 my $HTMLAttrChecker = {
934 wakaba 1.58 ## TODO: aria-* ## TODO: svg:*/@aria-* [HTML5ROLE] -> [STATES]
935 wakaba 1.1 id => sub {
936     ## NOTE: |map| has its own variant of |id=""| checker
937     my ($self, $attr) = @_;
938     my $value = $attr->value;
939     if (length $value > 0) {
940     if ($self->{id}->{$value}) {
941 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
942     level => $self->{level}->{must});
943 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
944     } else {
945     $self->{id}->{$value} = [$attr];
946     }
947     if ($value =~ /[\x09-\x0D\x20]/) {
948 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
949     level => $self->{level}->{must});
950 wakaba 1.1 }
951     } else {
952     ## NOTE: MUST contain at least one character
953 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
954     level => $self->{level}->{must});
955 wakaba 1.1 }
956     },
957     title => sub {}, ## NOTE: No conformance creteria
958     lang => sub {
959     my ($self, $attr) = @_;
960 wakaba 1.6 my $value = $attr->value;
961     if ($value eq '') {
962     #
963     } else {
964     require Whatpm::LangTag;
965     Whatpm::LangTag->check_rfc3066_language_tag ($value, sub {
966 wakaba 1.104 $self->{onerror}->(@_, node => $attr);
967 wakaba 1.106 }, $self->{level});
968 wakaba 1.6 }
969 wakaba 1.1 ## ISSUE: RFC 4646 (3066bis)?
970 wakaba 1.6
971     ## TODO: test data
972 wakaba 1.111
973     ## NOTE: Inconsistency between |lang| and |xml:lang| attributes are
974     ## non-conforming. Such errors are detected by the checkers of
975     ## |{}xml:lang| and |{xml}:lang| attributes.
976 wakaba 1.1 },
977     dir => $GetHTMLEnumeratedAttrChecker->({ltr => 1, rtl => 1}),
978     class => sub {
979     my ($self, $attr) = @_;
980     my %word;
981 wakaba 1.96 for my $word (grep {length $_} split /[\x09-\x0D\x20]+/, $attr->value) {
982 wakaba 1.1 unless ($word{$word}) {
983     $word{$word} = 1;
984     push @{$self->{return}->{class}->{$word}||=[]}, $attr;
985     } else {
986 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate token',
987     value => $word,
988     level => $self->{level}->{must});
989 wakaba 1.1 }
990     }
991     },
992 wakaba 1.63 contenteditable => $GetHTMLEnumeratedAttrChecker->({
993     true => 1, false => 1, '' => 1,
994     }),
995 wakaba 1.1 contextmenu => sub {
996     my ($self, $attr) = @_;
997     my $value = $attr->value;
998     push @{$self->{contextmenu}}, [$value => $attr];
999     ## ISSUE: "The value must be the ID of a menu element in the DOM."
1000     ## What is "in the DOM"? A menu Element node that is not part
1001     ## of the Document tree is in the DOM? A menu Element node that
1002     ## belong to another Document tree is in the DOM?
1003     },
1004 wakaba 1.115 hidden => $GetHTMLBooleanAttrChecker->('hidden'),
1005 wakaba 1.60 irrelevant => $GetHTMLBooleanAttrChecker->('irrelevant'),
1006 wakaba 1.79 ref => $HTMLRefOrTemplateAttrChecker,
1007     registrationmark => sub {
1008     my ($self, $attr, $item, $element_state) = @_;
1009    
1010     ## NOTE: Any value is conforming.
1011    
1012     if ($self->{flag}->{in_rule}) {
1013     my $el = $attr->owner_element;
1014     my $ln = $el->manakai_local_name;
1015     if ($ln eq 'nest' or
1016     ($ln eq 'rule' and not $element_state->{in_rule_original})) {
1017     my $nsuri = $el->namespace_uri;
1018     if (defined $nsuri and $nsuri eq $HTML_NS) {
1019     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1020 wakaba 1.104 level => $self->{level}->{must});
1021 wakaba 1.79 }
1022     }
1023     } else {
1024     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1025 wakaba 1.104 level => $self->{level}->{must});
1026 wakaba 1.79 }
1027     },
1028 wakaba 1.80 repeat => sub {
1029     my ($self, $attr) = @_;
1030 wakaba 1.83
1031     if (defined $attr->namespace_uri) {
1032     my $oe = $attr->owner_element;
1033     my $oe_nsuri = $oe->namespace_uri;
1034     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
1035     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1036 wakaba 1.104 level => $self->{level}->{must});
1037 wakaba 1.83 }
1038     }
1039    
1040 wakaba 1.80 my $value = $attr->value;
1041     if ($value eq 'template') {
1042     #
1043     } elsif ($value =~ /\A-?[0-9]+\z/) {
1044     #
1045     } else {
1046     $self->{onerror}->(node => $attr, type => 'repeat:syntax error',
1047 wakaba 1.104 level => $self->{level}->{must});
1048 wakaba 1.80 }
1049    
1050     ## ISSUE: "Repetition templates may occur anywhere." Does that mean
1051     ## that the attribute MAY be specified to any element, or that the
1052     ## element with that attribute (i.e. a repetition template) can be
1053     ## inserted anywhere in a document tree?
1054     },
1055 wakaba 1.83 'repeat-min' => $HTMLRepeatIndexAttrChecker,
1056     'repeat-max' => $HTMLRepeatIndexAttrChecker,
1057     'repeat-start' => $HTMLRepeatIndexAttrChecker,
1058 wakaba 1.80 'repeat-template' => sub {
1059 wakaba 1.83 my ($self, $attr) = @_;
1060    
1061     if (defined $attr->namespace_uri) {
1062     my $oe = $attr->owner_element;
1063     my $oe_nsuri = $oe->namespace_uri;
1064     if (defined $oe_nsuri or $oe_nsuri eq $HTML_NS) {
1065     $self->{onerror}->(node => $attr, type => 'attribute not allowed',
1066 wakaba 1.104 level => $self->{level}->{must});
1067 wakaba 1.83 }
1068     }
1069    
1070 wakaba 1.80 ## ISSUE: This attribute has no conformance requirement.
1071     ## ISSUE: Repetition blocks MAY have this attribute. Then, is the
1072     ## attribute allowed on an element that is not a repetition block?
1073     },
1074     ## TODO: namespaced variant of repeat-* attributes
1075 wakaba 1.58 ## TODO: role [HTML5ROLE] ## TODO: global @role [XHTML1ROLE]
1076 wakaba 1.78 ## TODO: style [HTML5]
1077 wakaba 1.74 tabindex => $HTMLIntegerAttrChecker,
1078 wakaba 1.79 template => $HTMLRefOrTemplateAttrChecker,
1079 wakaba 1.111 'xml:lang' => sub {
1080     my ($self, $attr) = @_;
1081    
1082     if ($attr->owner_document->manakai_is_html) {
1083     $self->{onerror}->(type => 'in HTML:xml:lang',
1084     level => $self->{level}->{info},
1085     node => $attr);
1086     ## NOTE: This is not an error, but the attribute will be ignored.
1087     } else {
1088     $self->{onerror}->(type => 'in XML:xml:lang',
1089     level => $self->{level}->{html5_no_may},
1090     node => $attr);
1091     ## TODO: We need to add test for this error.
1092     }
1093    
1094     my $lang_attr = $attr->owner_element->get_attribute_node_ns
1095     (undef, 'lang');
1096     if ($lang_attr) {
1097     my $lang_attr_value = $lang_attr->value;
1098     $lang_attr_value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
1099     my $value = $attr->value;
1100     $value =~ tr/A-Z/a-z/; ## ASCII case-insensitive
1101     if ($lang_attr_value ne $value) {
1102     $self->{onerror}->(type => 'xml:lang ne lang',
1103     level => $self->{level}->{must},
1104     node => $attr);
1105     }
1106     } else {
1107     $self->{onerror}->(type => 'xml:lang not allowed',
1108     level => $self->{level}->{must},
1109     node => $attr);
1110     ## TODO: We need to add test for <x {xml}:lang {}xml:lang>.
1111     }
1112     },
1113 wakaba 1.74 xmlns => sub {
1114     my ($self, $attr) = @_;
1115     my $value = $attr->value;
1116     unless ($value eq $HTML_NS) {
1117 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
1118     level => $self->{level}->{must});
1119 wakaba 1.74 ## TODO: Should be new "bad namespace" error?
1120     }
1121     unless ($attr->owner_document->manakai_is_html) {
1122 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'in XML:xmlns',
1123     level => $self->{level}->{must});
1124 wakaba 1.74 ## TODO: Test
1125     }
1126    
1127     ## TODO: Should be resolved?
1128     push @{$self->{return}->{uri}->{$value} ||= []},
1129     {node => $attr, type => {namespace => 1}};
1130     },
1131 wakaba 1.1 };
1132    
1133 wakaba 1.79 ## ISSUE: Shouldn't the same-origin policy applied to the datatemplate feature?
1134    
1135 wakaba 1.49 my %HTMLAttrStatus = (
1136 wakaba 1.50 class => FEATURE_HTML5_DEFAULT,
1137     contenteditable => FEATURE_HTML5_DEFAULT,
1138     contextmenu => FEATURE_HTML5_WD,
1139     dir => FEATURE_HTML5_DEFAULT,
1140     draggable => FEATURE_HTML5_LC,
1141 wakaba 1.115 hidden => FEATURE_HTML5_DEFAULT,
1142 wakaba 1.50 id => FEATURE_HTML5_DEFAULT,
1143 wakaba 1.115 irrelevant => FEATURE_HTML5_DROPPED,
1144 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT,
1145     ref => FEATURE_HTML5_AT_RISK,
1146     registrationmark => FEATURE_HTML5_AT_RISK,
1147 wakaba 1.60 repeat => FEATURE_WF2,
1148     'repeat-max' => FEATURE_WF2,
1149     'repeat-min' => FEATURE_WF2,
1150     'repeat-start' => FEATURE_WF2,
1151     'repeat-template' => FEATURE_WF2,
1152 wakaba 1.58 role => FEATURE_HTML5_ROLE,
1153 wakaba 1.78 style => FEATURE_HTML5_DEFAULT,
1154 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT,
1155     template => FEATURE_HTML5_AT_RISK,
1156     title => FEATURE_HTML5_DEFAULT,
1157 wakaba 1.74 xmlns => FEATURE_HTML5_DEFAULT,
1158 wakaba 1.49 );
1159    
1160     my %HTMLM12NCommonAttrStatus = (
1161 wakaba 1.120 about => FEATURE_RDFA_CR,
1162 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1163 wakaba 1.120 content => FEATURE_RDFA_CR,
1164     datatype => FEATURE_RDFA_CR,
1165 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1166 wakaba 1.121 href => FEATURE_RDFA_CR,
1167 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1168 wakaba 1.61 instanceof => FEATURE_RDFA_LC,
1169 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1170     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1171     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1172     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1173     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1174     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1175     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1176     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1177     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1178     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1179 wakaba 1.120 property => FEATURE_RDFA_CR,
1180     rel => FEATURE_RDFA_CR,
1181     resource => FEATURE_RDFA_CR,
1182     rev => FEATURE_RDFA_CR,
1183 wakaba 1.78 #style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR_DEPRECATED |
1184     # FEATURE_M12N10_REC,
1185     style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
1186 wakaba 1.55 FEATURE_M12N10_REC,
1187 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1188 wakaba 1.120 typeof => FEATURE_RDFA_CR,
1189 wakaba 1.49 );
1190    
1191 wakaba 1.82 my %XHTML2CommonAttrStatus = (
1192     ## Core
1193 wakaba 1.108 class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1194     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1195 wakaba 1.82 #xml:id
1196     layout => FEATURE_XHTML2_ED,
1197 wakaba 1.108 title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1198 wakaba 1.82
1199     ## Hypertext
1200     cite => FEATURE_XHTML2_ED,
1201     href => FEATURE_XHTML2_ED,
1202     hreflang => FEATURE_XHTML2_ED,
1203     hrefmedia => FEATURE_XHTML2_ED,
1204     hreftype => FEATURE_XHTML2_ED,
1205     nextfocus => FEATURE_XHTML2_ED,
1206     prevfocus => FEATURE_XHTML2_ED,
1207     target => FEATURE_XHTML2_ED,
1208     #xml:base
1209    
1210     ## I18N
1211     #xml:lang
1212    
1213     ## Bi-directional
1214 wakaba 1.108 dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
1215 wakaba 1.82
1216     ## Edit
1217     edit => FEATURE_XHTML2_ED,
1218     datetime => FEATURE_XHTML2_ED,
1219    
1220     ## Embedding
1221     encoding => FEATURE_XHTML2_ED,
1222     src => FEATURE_XHTML2_ED,
1223     srctype => FEATURE_XHTML2_ED,
1224    
1225     ## Image Map
1226     usemap => FEATURE_XHTML2_ED,
1227     ismap => FEATURE_XHTML2_ED,
1228     shape => FEATURE_XHTML2_ED,
1229     coords => FEATURE_XHTML2_ED,
1230    
1231     ## Media
1232     media => FEATURE_XHTML2_ED,
1233    
1234     ## Metadata
1235     about => FEATURE_XHTML2_ED,
1236     content => FEATURE_XHTML2_ED,
1237     datatype => FEATURE_XHTML2_ED,
1238     instanceof => FEATURE_XHTML2_ED,
1239     property => FEATURE_XHTML2_ED,
1240     rel => FEATURE_XHTML2_ED,
1241     resource => FEATURE_XHTML2_ED,
1242     rev => FEATURE_XHTML2_ED,
1243    
1244     ## Role
1245 wakaba 1.108 role => FEATURE_HTML5_ROLE | FEATURE_XHTML2_ED,
1246 wakaba 1.82
1247     ## Style
1248 wakaba 1.108 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED, # "strongly discouraged"
1249 wakaba 1.82 );
1250    
1251     my %HTMLM12NXHTML2CommonAttrStatus = (
1252     %HTMLM12NCommonAttrStatus,
1253     %XHTML2CommonAttrStatus,
1254    
1255 wakaba 1.120 about => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1256 wakaba 1.82 class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1257 wakaba 1.120 content => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1258     datatype => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1259 wakaba 1.82 dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1260 wakaba 1.121 href => FEATURE_RDFA_CR,
1261 wakaba 1.82 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1262     instanceof => FEATURE_RDFA_LC | FEATURE_XHTML2_ED,
1263 wakaba 1.120 property => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1264     rel => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1265     resource => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1266     rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED,
1267 wakaba 1.82 #style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR_DEPRECATED |
1268     # FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1269     style => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
1270     FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1271     title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1272 wakaba 1.120 typeof => FEATURE_RDFA_CR,
1273 wakaba 1.82 );
1274    
1275 wakaba 1.1 for (qw/
1276     onabort onbeforeunload onblur onchange onclick oncontextmenu
1277     ondblclick ondrag ondragend ondragenter ondragleave ondragover
1278     ondragstart ondrop onerror onfocus onkeydown onkeypress
1279     onkeyup onload onmessage onmousedown onmousemove onmouseout
1280     onmouseover onmouseup onmousewheel onresize onscroll onselect
1281 wakaba 1.77 onstorage onsubmit onunload
1282 wakaba 1.1 /) {
1283     $HTMLAttrChecker->{$_} = $HTMLEventHandlerAttrChecker;
1284 wakaba 1.50 $HTMLAttrStatus{$_} = FEATURE_HTML5_DEFAULT;
1285 wakaba 1.1 }
1286    
1287 wakaba 1.82 ## NOTE: Non-standard global attributes in the HTML namespace.
1288     $AttrChecker->{$HTML_NS}->{''} = sub {}; # no syntactical checks
1289     $AttrStatus->{$HTML_NS}->{''} = 0; # disallowed and not part of any standard
1290    
1291     $AttrStatus->{$HTML_NS}->{active} = FEATURE_HTML5_DROPPED;
1292     for (qw/repeat repeat-max repeat-min repeat-start repeat-template/) {
1293     $AttrChecker->{$HTML_NS}->{$_} = $HTMLAttrChecker->{$_};
1294     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_WF2;
1295     }
1296    
1297 wakaba 1.120 for (qw/about content datatype property rel resource rev/) {
1298     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_RDFA_CR | FEATURE_XHTML2_ED;
1299 wakaba 1.82 }
1300 wakaba 1.120 $AttrStatus->{$HTML_NS}->{instanceof} = FEATURE_RDFA_LC | FEATURE_XHTML2_ED;
1301     $AttrStatus->{$HTML_NS}->{typeof} = FEATURE_RDFA_CR;
1302 wakaba 1.82 $AttrStatus->{$HTML_NS}->{role} = FEATURE_ROLE_LC;
1303     for (qw/cite coords datetime edit encoding href hreflang hrefmedia hreftype
1304     ismap layout media nextfocus prevfocus shape src srctype style
1305     target usemap/) {
1306     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_XHTML2_ED;
1307     }
1308     for (qw/class dir id title/) {
1309     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC | FEATURE_XHTML2_ED;
1310     }
1311     for (qw/onclick ondblclick onmousedown onmouseup onmouseover onmousemove
1312     onmouseout onkeypress onkeydown onkeyup/) {
1313     $AttrStatus->{$HTML_NS}->{$_} = FEATURE_M12N11_LC;
1314     }
1315    
1316 wakaba 1.73 my $HTMLDatasetAttrChecker = sub {
1317     ## NOTE: "Authors should ... when the attributes are ignored and
1318     ## any associated CSS dropped, the page is still usable." (semantic
1319     ## constraint.)
1320     }; # $HTMLDatasetAttrChecker
1321    
1322     my $HTMLDatasetAttrStatus = FEATURE_HTML5_DEFAULT;
1323    
1324 wakaba 1.1 my $GetHTMLAttrsChecker = sub {
1325     my $element_specific_checker = shift;
1326 wakaba 1.49 my $element_specific_status = shift;
1327 wakaba 1.1 return sub {
1328 wakaba 1.40 my ($self, $item, $element_state) = @_;
1329     for my $attr (@{$item->{node}->attributes}) {
1330 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
1331     $attr_ns = '' unless defined $attr_ns;
1332     my $attr_ln = $attr->manakai_local_name;
1333     my $checker;
1334 wakaba 1.73 my $status;
1335 wakaba 1.1 if ($attr_ns eq '') {
1336 wakaba 1.122 if ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
1337     $attr_ln !~ /[A-Z]/) {
1338 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
1339     $status = $HTMLDatasetAttrStatus;
1340     } else {
1341     $checker = $element_specific_checker->{$attr_ln}
1342     || $HTMLAttrChecker->{$attr_ln};
1343     $status = $element_specific_status->{$attr_ln};
1344     }
1345 wakaba 1.1 }
1346     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1347 wakaba 1.40 || $AttrChecker->{$attr_ns}->{''};
1348 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
1349     || $AttrStatus->{$attr_ns}->{''};
1350     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
1351 wakaba 1.1 if ($checker) {
1352 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
1353 wakaba 1.62 } elsif ($attr_ns eq '' and not $element_specific_status->{$attr_ln}) {
1354 wakaba 1.54 #
1355 wakaba 1.1 } else {
1356 wakaba 1.104 $self->{onerror}->(node => $attr,
1357     type => 'unknown attribute',
1358     level => $self->{level}->{uncertain});
1359 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1360     }
1361 wakaba 1.82 $self->_attr_status_info ($attr, $status);
1362 wakaba 1.1 }
1363     };
1364     }; # $GetHTMLAttrsChecker
1365    
1366 wakaba 1.40 my %HTMLChecker = (
1367     %Whatpm::ContentChecker::AnyChecker,
1368 wakaba 1.79 check_start => sub {
1369     my ($self, $item, $element_state) = @_;
1370    
1371     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1372     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1373     },
1374 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, \%HTMLAttrStatus),
1375 wakaba 1.40 );
1376    
1377     my %HTMLEmptyChecker = (
1378     %HTMLChecker,
1379     check_child_element => sub {
1380     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1381     $child_is_transparent, $element_state) = @_;
1382     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1383     $self->{onerror}->(node => $child_el,
1384     type => 'element not allowed:minus',
1385 wakaba 1.104 level => $self->{level}->{must});
1386 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1387     #
1388     } else {
1389     $self->{onerror}->(node => $child_el,
1390     type => 'element not allowed:empty',
1391 wakaba 1.104 level => $self->{level}->{must});
1392 wakaba 1.40 }
1393     },
1394     check_child_text => sub {
1395     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1396     if ($has_significant) {
1397     $self->{onerror}->(node => $child_node,
1398     type => 'character not allowed:empty',
1399 wakaba 1.104 level => $self->{level}->{must});
1400 wakaba 1.40 }
1401     },
1402     );
1403    
1404     my %HTMLTextChecker = (
1405     %HTMLChecker,
1406     check_child_element => sub {
1407     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1408     $child_is_transparent, $element_state) = @_;
1409     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1410     $self->{onerror}->(node => $child_el,
1411     type => 'element not allowed:minus',
1412 wakaba 1.104 level => $self->{level}->{must});
1413 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1414     #
1415     } else {
1416 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed:text',
1417     level => $self->{level}->{must});
1418 wakaba 1.40 }
1419     },
1420     );
1421    
1422 wakaba 1.72 my %HTMLFlowContentChecker = (
1423 wakaba 1.40 %HTMLChecker,
1424     check_child_element => sub {
1425     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1426     $child_is_transparent, $element_state) = @_;
1427     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1428     $self->{onerror}->(node => $child_el,
1429     type => 'element not allowed:minus',
1430 wakaba 1.104 level => $self->{level}->{must});
1431 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1432     #
1433     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1434     if ($element_state->{has_non_style} or
1435     not $child_el->has_attribute_ns (undef, 'scoped')) {
1436 wakaba 1.104 $self->{onerror}->(node => $child_el,
1437 wakaba 1.72 type => 'element not allowed:flow style',
1438 wakaba 1.104 level => $self->{level}->{must});
1439 wakaba 1.40 }
1440 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
1441 wakaba 1.43 $element_state->{has_non_style} = 1 unless $child_is_transparent;
1442 wakaba 1.40 } else {
1443     $element_state->{has_non_style} = 1;
1444 wakaba 1.104 $self->{onerror}->(node => $child_el,
1445 wakaba 1.72 type => 'element not allowed:flow',
1446 wakaba 1.104 level => $self->{level}->{must})
1447 wakaba 1.40 }
1448     },
1449     check_child_text => sub {
1450     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1451     if ($has_significant) {
1452     $element_state->{has_non_style} = 1;
1453     }
1454     },
1455     check_end => sub {
1456     my ($self, $item, $element_state) = @_;
1457 wakaba 1.95 ## NOTE: A modified copy of the code below is in |datagrid| checker.
1458 wakaba 1.40 if ($element_state->{has_significant}) {
1459 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
1460 wakaba 1.40 } elsif ($item->{transparent}) {
1461     #
1462     } else {
1463     $self->{onerror}->(node => $item->{node},
1464 wakaba 1.104 level => $self->{level}->{should},
1465 wakaba 1.40 type => 'no significant content');
1466     }
1467     },
1468     );
1469    
1470     my %HTMLPhrasingContentChecker = (
1471     %HTMLChecker,
1472     check_child_element => sub {
1473     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1474     $child_is_transparent, $element_state) = @_;
1475     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1476     $self->{onerror}->(node => $child_el,
1477     type => 'element not allowed:minus',
1478 wakaba 1.104 level => $self->{level}->{must});
1479 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1480     #
1481     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
1482     #
1483     } else {
1484     $self->{onerror}->(node => $child_el,
1485     type => 'element not allowed:phrasing',
1486 wakaba 1.104 level => $self->{level}->{must});
1487 wakaba 1.40 }
1488     },
1489 wakaba 1.72 check_end => $HTMLFlowContentChecker{check_end},
1490 wakaba 1.40 ## NOTE: The definition for |li| assumes that the only differences
1491 wakaba 1.72 ## between flow and phrasing content checkers are |check_child_element|
1492 wakaba 1.40 ## and |check_child_text|.
1493     );
1494    
1495 wakaba 1.72 my %HTMLTransparentChecker = %HTMLFlowContentChecker;
1496 wakaba 1.40 ## ISSUE: Significant content rule should be applied to transparent element
1497 wakaba 1.46 ## with parent?
1498 wakaba 1.40
1499 wakaba 1.1 our $Element;
1500     our $ElementDefault;
1501    
1502     $Element->{$HTML_NS}->{''} = {
1503 wakaba 1.40 %HTMLChecker,
1504 wakaba 1.1 };
1505    
1506     $Element->{$HTML_NS}->{html} = {
1507 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1508 wakaba 1.1 is_root => 1,
1509 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
1510 wakaba 1.16 manifest => $HTMLURIAttrChecker,
1511 wakaba 1.67 version => sub {
1512     ## NOTE: According to HTML4 prose, this is a "cdata" attribute.
1513     ## Though DTDs of various versions of HTML define the attribute
1514     ## as |#FIXED|, this conformance checker does no check for
1515     ## the attribute value, since what kind of check should be done
1516     ## is unknown.
1517     },
1518 wakaba 1.49 }, {
1519     %HTMLAttrStatus,
1520 wakaba 1.82 %XHTML2CommonAttrStatus,
1521     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1522     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1523     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1524 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1525     manifest => FEATURE_HTML5_DEFAULT,
1526 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1527 wakaba 1.82 version => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1528 wakaba 1.1 }),
1529 wakaba 1.40 check_start => sub {
1530     my ($self, $item, $element_state) = @_;
1531     $element_state->{phase} = 'before head';
1532 wakaba 1.79
1533 wakaba 1.66 $element_state->{uri_info}->{manifest}->{type}->{resource} = 1;
1534 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
1535     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
1536 wakaba 1.40 },
1537     check_child_element => sub {
1538     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1539     $child_is_transparent, $element_state) = @_;
1540     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1541     $self->{onerror}->(node => $child_el,
1542     type => 'element not allowed:minus',
1543 wakaba 1.104 level => $self->{level}->{must});
1544 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1545     #
1546     } elsif ($element_state->{phase} eq 'before head') {
1547     if ($child_nsuri eq $HTML_NS and $child_ln eq 'head') {
1548     $element_state->{phase} = 'after head';
1549     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1550     $self->{onerror}->(node => $child_el,
1551 wakaba 1.104 type => 'ps element missing',
1552     text => 'head',
1553     level => $self->{level}->{must});
1554 wakaba 1.40 $element_state->{phase} = 'after body';
1555     } else {
1556     $self->{onerror}->(node => $child_el,
1557 wakaba 1.104 type => 'element not allowed',
1558     level => $self->{level}->{must});
1559 wakaba 1.40 }
1560     } elsif ($element_state->{phase} eq 'after head') {
1561     if ($child_nsuri eq $HTML_NS and $child_ln eq 'body') {
1562     $element_state->{phase} = 'after body';
1563     } else {
1564     $self->{onerror}->(node => $child_el,
1565 wakaba 1.104 type => 'element not allowed',
1566     level => $self->{level}->{must});
1567 wakaba 1.40 }
1568     } elsif ($element_state->{phase} eq 'after body') {
1569     $self->{onerror}->(node => $child_el,
1570 wakaba 1.104 type => 'element not allowed',
1571     level => $self->{level}->{must});
1572 wakaba 1.40 } else {
1573     die "check_child_element: Bad |html| phase: $element_state->{phase}";
1574     }
1575     },
1576     check_child_text => sub {
1577     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1578     if ($has_significant) {
1579     $self->{onerror}->(node => $child_node,
1580 wakaba 1.104 type => 'character not allowed',
1581     level => $self->{level}->{must});
1582 wakaba 1.40 }
1583     },
1584     check_end => sub {
1585     my ($self, $item, $element_state) = @_;
1586     if ($element_state->{phase} eq 'after body') {
1587     #
1588     } elsif ($element_state->{phase} eq 'before head') {
1589     $self->{onerror}->(node => $item->{node},
1590 wakaba 1.104 type => 'child element missing',
1591     text => 'head',
1592     level => $self->{level}->{must});
1593 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1594 wakaba 1.104 type => 'child element missing',
1595     text => 'body',
1596     level => $self->{level}->{must});
1597 wakaba 1.40 } elsif ($element_state->{phase} eq 'after head') {
1598     $self->{onerror}->(node => $item->{node},
1599 wakaba 1.104 type => 'child element missing',
1600     text => 'body',
1601     level => $self->{level}->{must});
1602 wakaba 1.40 } else {
1603     die "check_end: Bad |html| phase: $element_state->{phase}";
1604     }
1605 wakaba 1.1
1606 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1607     },
1608     };
1609 wakaba 1.25
1610 wakaba 1.40 $Element->{$HTML_NS}->{head} = {
1611 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1612 wakaba 1.67 check_attrs => $GetHTMLAttrsChecker->({
1613     profile => $HTMLSpaceURIsAttrChecker, ## NOTE: MUST be profile URIs.
1614     }, {
1615 wakaba 1.49 %HTMLAttrStatus,
1616 wakaba 1.82 %XHTML2CommonAttrStatus,
1617     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1618     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1619     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1620 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1621 wakaba 1.49 profile => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1622     }),
1623 wakaba 1.40 check_child_element => sub {
1624     my ($self, $item, $child_el, $child_nsuri, $child_ln,
1625     $child_is_transparent, $element_state) = @_;
1626     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
1627     $self->{onerror}->(node => $child_el,
1628     type => 'element not allowed:minus',
1629 wakaba 1.104 level => $self->{level}->{must});
1630 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
1631     #
1632     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'title') {
1633     unless ($element_state->{has_title}) {
1634     $element_state->{has_title} = 1;
1635     } else {
1636     $self->{onerror}->(node => $child_el,
1637     type => 'element not allowed:head title',
1638 wakaba 1.104 level => $self->{level}->{must});
1639 wakaba 1.40 }
1640     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
1641     if ($child_el->has_attribute_ns (undef, 'scoped')) {
1642     $self->{onerror}->(node => $child_el,
1643     type => 'element not allowed:head style',
1644 wakaba 1.104 level => $self->{level}->{must});
1645 wakaba 1.1 }
1646 wakaba 1.40 } elsif ($HTMLMetadataContent->{$child_nsuri}->{$child_ln}) {
1647     #
1648    
1649     ## NOTE: |meta| is a metadata content. However, strictly speaking,
1650     ## a |meta| element with none of |charset|, |name|,
1651     ## or |http-equiv| attribute is not allowed. It is non-conforming
1652     ## anyway.
1653 wakaba 1.56
1654     ## TODO: |form| MUST be empty and in XML [WF2].
1655 wakaba 1.40 } else {
1656     $self->{onerror}->(node => $child_el,
1657     type => 'element not allowed:metadata',
1658 wakaba 1.104 level => $self->{level}->{must});
1659 wakaba 1.40 }
1660     $element_state->{in_head_original} = $self->{flag}->{in_head};
1661     $self->{flag}->{in_head} = 1;
1662     },
1663     check_child_text => sub {
1664     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
1665     if ($has_significant) {
1666 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
1667     level => $self->{level}->{must});
1668 wakaba 1.1 }
1669 wakaba 1.40 },
1670     check_end => sub {
1671     my ($self, $item, $element_state) = @_;
1672     unless ($element_state->{has_title}) {
1673     $self->{onerror}->(node => $item->{node},
1674 wakaba 1.104 type => 'child element missing',
1675     text => 'title',
1676 wakaba 1.105 level => $self->{level}->{must});
1677 wakaba 1.1 }
1678 wakaba 1.40 $self->{flag}->{in_head} = $element_state->{in_head_original};
1679 wakaba 1.1
1680 wakaba 1.40 $HTMLChecker{check_end}->(@_);
1681 wakaba 1.1 },
1682     };
1683    
1684 wakaba 1.40 $Element->{$HTML_NS}->{title} = {
1685     %HTMLTextChecker,
1686 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1687 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
1688     %HTMLAttrStatus,
1689 wakaba 1.82 %XHTML2CommonAttrStatus,
1690     class => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_HTML2X_RFC,
1691     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1692     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
1693 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1694 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
1695 wakaba 1.49 }),
1696 wakaba 1.40 };
1697 wakaba 1.1
1698 wakaba 1.40 $Element->{$HTML_NS}->{base} = {
1699 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1700 wakaba 1.40 %HTMLEmptyChecker,
1701     check_attrs => sub {
1702     my ($self, $item, $element_state) = @_;
1703 wakaba 1.1
1704 wakaba 1.40 if ($self->{has_base}) {
1705     $self->{onerror}->(node => $item->{node},
1706 wakaba 1.104 type => 'element not allowed:base',
1707     level => $self->{level}->{must});
1708 wakaba 1.40 } else {
1709     $self->{has_base} = 1;
1710 wakaba 1.29 }
1711    
1712 wakaba 1.40 my $has_href = $item->{node}->has_attribute_ns (undef, 'href');
1713     my $has_target = $item->{node}->has_attribute_ns (undef, 'target');
1714 wakaba 1.14
1715     if ($self->{has_uri_attr} and $has_href) {
1716 wakaba 1.4 ## ISSUE: Are these examples conforming?
1717     ## <head profile="a b c"><base href> (except for |profile|'s
1718     ## non-conformance)
1719     ## <title xml:base="relative"/><base href/> (maybe it should be)
1720     ## <unknown xmlns="relative"/><base href/> (assuming that
1721     ## |{relative}:unknown| is allowed before XHTML |base| (unlikely, though))
1722     ## <style>@import 'relative';</style><base href>
1723     ## <script>location.href = 'relative';</script><base href>
1724 wakaba 1.14 ## NOTE: <html manifest=".."><head><base href=""/> is conforming as
1725     ## an exception.
1726 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1727 wakaba 1.104 type => 'basehref after URL attribute',
1728     level => $self->{level}->{must});
1729 wakaba 1.4 }
1730 wakaba 1.14 if ($self->{has_hyperlink_element} and $has_target) {
1731 wakaba 1.4 ## ISSUE: Are these examples conforming?
1732     ## <head><title xlink:href=""/><base target="name"/></head>
1733     ## <xbl:xbl>...<svg:a href=""/>...</xbl:xbl><base target="name"/>
1734     ## (assuming that |xbl:xbl| is allowed before |base|)
1735     ## NOTE: These are non-conformant anyway because of |head|'s content model:
1736     ## <link href=""/><base target="name"/>
1737     ## <link rel=unknown href=""><base target=name>
1738 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1739 wakaba 1.104 type => 'basetarget after hyperlink',
1740     level => $self->{level}->{must});
1741 wakaba 1.4 }
1742    
1743 wakaba 1.14 if (not $has_href and not $has_target) {
1744 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1745 wakaba 1.104 type => 'attribute missing:href|target',
1746     level => $self->{level}->{must});
1747 wakaba 1.14 }
1748    
1749 wakaba 1.66 $element_state->{uri_info}->{href}->{type}->{base} = 1;
1750    
1751 wakaba 1.4 return $GetHTMLAttrsChecker->({
1752     href => $HTMLURIAttrChecker,
1753     target => $HTMLTargetAttrChecker,
1754 wakaba 1.49 }, {
1755     %HTMLAttrStatus,
1756 wakaba 1.50 href => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1757     id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1758     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1759 wakaba 1.40 })->($self, $item, $element_state);
1760 wakaba 1.4 },
1761 wakaba 1.1 };
1762    
1763     $Element->{$HTML_NS}->{link} = {
1764 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1765 wakaba 1.40 %HTMLEmptyChecker,
1766     check_attrs => sub {
1767     my ($self, $item, $element_state) = @_;
1768 wakaba 1.96 my $sizes_attr;
1769 wakaba 1.1 $GetHTMLAttrsChecker->({
1770 wakaba 1.91 charset => sub {
1771     my ($self, $attr) = @_;
1772     $HTMLCharsetChecker->($attr->value, @_);
1773     },
1774 wakaba 1.1 href => $HTMLURIAttrChecker,
1775 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(0, $item, @_) },
1776 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
1777 wakaba 1.1 media => $HTMLMQAttrChecker,
1778     hreflang => $HTMLLanguageTagAttrChecker,
1779 wakaba 1.96 sizes => sub {
1780     my ($self, $attr) = @_;
1781     $sizes_attr = $attr;
1782     my %word;
1783     for my $word (grep {length $_}
1784     split /[\x09-\x0D\x20]+/, $attr->value) {
1785     unless ($word{$word}) {
1786     $word{$word} = 1;
1787     if ($word eq 'any' or $word =~ /\A[1-9][0-9]*x[1-9][0-9]*\z/) {
1788     #
1789     } else {
1790     $self->{onerror}->(node => $attr,
1791 wakaba 1.104 type => 'sizes:syntax error',
1792 wakaba 1.96 value => $word,
1793 wakaba 1.104 level => $self->{level}->{must});
1794 wakaba 1.96 }
1795     } else {
1796     $self->{onerror}->(node => $attr, type => 'duplicate token',
1797     value => $word,
1798 wakaba 1.104 level => $self->{level}->{must});
1799 wakaba 1.96 }
1800     }
1801     },
1802 wakaba 1.70 target => $HTMLTargetAttrChecker,
1803 wakaba 1.1 type => $HTMLIMTAttrChecker,
1804     ## NOTE: Though |title| has special semantics,
1805     ## syntactically same as the |title| as global attribute.
1806 wakaba 1.49 }, {
1807     %HTMLAttrStatus,
1808 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
1809 wakaba 1.91 charset => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
1810     ## NOTE: |charset| attribute had been part of HTML5 spec though
1811     ## it had been commented out.
1812 wakaba 1.121 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1813 wakaba 1.82 hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1814     FEATURE_M12N10_REC,
1815 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1816 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1817 wakaba 1.61 methods => FEATURE_HTML20_RFC,
1818 wakaba 1.120 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1819     rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1820 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
1821 wakaba 1.96 sizes => FEATURE_HTML5_DEFAULT,
1822 wakaba 1.82 target => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1823 wakaba 1.50 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1824 wakaba 1.61 urn => FEATURE_HTML20_RFC,
1825 wakaba 1.40 })->($self, $item, $element_state);
1826 wakaba 1.96
1827 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'href')) {
1828     $self->{has_hyperlink_element} = 1 if $item->{has_hyperlink_link_type};
1829 wakaba 1.4 } else {
1830 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1831 wakaba 1.104 type => 'attribute missing',
1832     text => 'href',
1833     level => $self->{level}->{must});
1834 wakaba 1.1 }
1835 wakaba 1.96
1836 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'rel')) {
1837     $self->{onerror}->(node => $item->{node},
1838 wakaba 1.104 type => 'attribute missing',
1839     text => 'rel',
1840     level => $self->{level}->{must});
1841 wakaba 1.96 }
1842    
1843     if ($sizes_attr and not $element_state->{link_rel}->{icon}) {
1844     $self->{onerror}->(node => $sizes_attr,
1845     type => 'attribute not allowed',
1846 wakaba 1.104 level => $self->{level}->{must});
1847 wakaba 1.1 }
1848 wakaba 1.116
1849     if ($element_state->{link_rel}->{alternate} and
1850     $element_state->{link_rel}->{stylesheet}) {
1851     my $title_attr = $item->{node}->get_attribute_node_ns (undef, 'title');
1852     unless ($title_attr) {
1853     $self->{onerror}->(node => $item->{node},
1854     type => 'attribute missing',
1855     text => 'title',
1856     level => $self->{level}->{must});
1857     } elsif ($title_attr->value eq '') {
1858     $self->{onerror}->(node => $title_attr,
1859     type => 'empty style sheet title',
1860     level => $self->{level}->{must});
1861     }
1862     }
1863 wakaba 1.1 },
1864     };
1865    
1866     $Element->{$HTML_NS}->{meta} = {
1867 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
1868 wakaba 1.40 %HTMLEmptyChecker,
1869     check_attrs => sub {
1870     my ($self, $item, $element_state) = @_;
1871 wakaba 1.1 my $name_attr;
1872     my $http_equiv_attr;
1873     my $charset_attr;
1874     my $content_attr;
1875 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
1876 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
1877     $attr_ns = '' unless defined $attr_ns;
1878     my $attr_ln = $attr->manakai_local_name;
1879     my $checker;
1880 wakaba 1.73 my $status;
1881 wakaba 1.1 if ($attr_ns eq '') {
1882 wakaba 1.73 $status = {
1883     %HTMLAttrStatus,
1884 wakaba 1.82 %XHTML2CommonAttrStatus,
1885 wakaba 1.73 charset => FEATURE_HTML5_DEFAULT,
1886 wakaba 1.82 content => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1887     FEATURE_M12N10_REC,
1888     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1889     FEATURE_M12N10_REC,
1890 wakaba 1.73 'http-equiv' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1891 wakaba 1.82 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
1892     FEATURE_XHTML10_REC,
1893 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
1894     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
1895     scheme => FEATURE_M12N10_REC,
1896     }->{$attr_ln};
1897    
1898 wakaba 1.1 if ($attr_ln eq 'content') {
1899     $content_attr = $attr;
1900     $checker = 1;
1901     } elsif ($attr_ln eq 'name') {
1902     $name_attr = $attr;
1903     $checker = 1;
1904     } elsif ($attr_ln eq 'http-equiv') {
1905     $http_equiv_attr = $attr;
1906     $checker = 1;
1907     } elsif ($attr_ln eq 'charset') {
1908     $charset_attr = $attr;
1909     $checker = 1;
1910 wakaba 1.67 } elsif ($attr_ln eq 'scheme') {
1911 wakaba 1.71 ## NOTE: <http://suika.fam.cx/2007/html/standards#html-meta-scheme>
1912 wakaba 1.67 $checker = sub {};
1913 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
1914     $attr_ln !~ /[A-Z]/) {
1915 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
1916     $status = $HTMLDatasetAttrStatus;
1917 wakaba 1.1 } else {
1918     $checker = $HTMLAttrChecker->{$attr_ln}
1919 wakaba 1.67 || $AttrChecker->{$attr_ns}->{$attr_ln}
1920 wakaba 1.1 || $AttrChecker->{$attr_ns}->{''};
1921     }
1922     } else {
1923     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
1924 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
1925     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
1926     || $AttrStatus->{$attr_ns}->{''};
1927     $status = FEATURE_ALLOWED if not defined $status;
1928 wakaba 1.1 }
1929 wakaba 1.62
1930 wakaba 1.1 if ($checker) {
1931 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
1932 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
1933 wakaba 1.54 #
1934 wakaba 1.1 } else {
1935 wakaba 1.104 $self->{onerror}->(node => $attr,
1936     type => 'unknown attribute',
1937     level => $self->{level}->{uncertain});
1938 wakaba 1.49 ## ISSUE: No conformance createria for unknown attributes in the spec
1939     }
1940    
1941 wakaba 1.82 $self->_attr_status_info ($attr, $status);
1942 wakaba 1.1 }
1943    
1944     if (defined $name_attr) {
1945     if (defined $http_equiv_attr) {
1946     $self->{onerror}->(node => $http_equiv_attr,
1947 wakaba 1.104 type => 'attribute not allowed',
1948     level => $self->{level}->{must});
1949 wakaba 1.1 } elsif (defined $charset_attr) {
1950     $self->{onerror}->(node => $charset_attr,
1951 wakaba 1.104 type => 'attribute not allowed',
1952     level => $self->{level}->{must});
1953 wakaba 1.1 }
1954     my $metadata_name = $name_attr->value;
1955     my $metadata_value;
1956     if (defined $content_attr) {
1957     $metadata_value = $content_attr->value;
1958     } else {
1959 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1960 wakaba 1.104 type => 'attribute missing',
1961     text => 'content',
1962     level => $self->{level}->{must});
1963 wakaba 1.1 $metadata_value = '';
1964     }
1965     } elsif (defined $http_equiv_attr) {
1966     if (defined $charset_attr) {
1967     $self->{onerror}->(node => $charset_attr,
1968 wakaba 1.104 type => 'attribute not allowed',
1969     level => $self->{level}->{must});
1970 wakaba 1.1 }
1971     unless (defined $content_attr) {
1972 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1973 wakaba 1.104 type => 'attribute missing',
1974     text => 'content',
1975     level => $self->{level}->{must});
1976 wakaba 1.1 }
1977     } elsif (defined $charset_attr) {
1978     if (defined $content_attr) {
1979     $self->{onerror}->(node => $content_attr,
1980 wakaba 1.104 type => 'attribute not allowed',
1981     level => $self->{level}->{must});
1982 wakaba 1.1 }
1983     } else {
1984     if (defined $content_attr) {
1985     $self->{onerror}->(node => $content_attr,
1986 wakaba 1.104 type => 'attribute not allowed',
1987     level => $self->{level}->{must});
1988 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1989 wakaba 1.104 type => 'attribute missing:name|http-equiv',
1990     level => $self->{level}->{must});
1991 wakaba 1.1 } else {
1992 wakaba 1.40 $self->{onerror}->(node => $item->{node},
1993 wakaba 1.104 type => 'attribute missing:name|http-equiv|charset',
1994     level => $self->{level}->{must});
1995 wakaba 1.1 }
1996     }
1997    
1998 wakaba 1.32 my $check_charset_decl = sub () {
1999 wakaba 1.40 my $parent = $item->{node}->manakai_parent_element;
2000 wakaba 1.29 if ($parent and $parent eq $parent->owner_document->manakai_head) {
2001     for my $el (@{$parent->child_nodes}) {
2002     next unless $el->node_type == 1; # ELEMENT_NODE
2003 wakaba 1.40 unless ($el eq $item->{node}) {
2004 wakaba 1.29 ## NOTE: Not the first child element.
2005 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2006 wakaba 1.32 type => 'element not allowed:meta charset',
2007 wakaba 1.104 level => $self->{level}->{must});
2008 wakaba 1.29 }
2009     last;
2010     ## NOTE: Entity references are not supported.
2011     }
2012     } else {
2013 wakaba 1.40 $self->{onerror}->(node => $item->{node},
2014 wakaba 1.32 type => 'element not allowed:meta charset',
2015 wakaba 1.104 level => $self->{level}->{must});
2016 wakaba 1.29 }
2017    
2018 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
2019     $self->{onerror}->(node => $item->{node},
2020 wakaba 1.32 type => 'in XML:charset',
2021 wakaba 1.104 level => $self->{level}->{must});
2022 wakaba 1.1 }
2023 wakaba 1.32 }; # $check_charset_decl
2024 wakaba 1.21
2025 wakaba 1.32 my $check_charset = sub ($$) {
2026     my ($attr, $charset_value) = @_;
2027 wakaba 1.21
2028 wakaba 1.91 my $charset;
2029     ($charset, $charset_value)
2030     = $HTMLCharsetChecker->($charset_value, $self, $attr);
2031    
2032 wakaba 1.40 my $ic = $item->{node}->owner_document->input_encoding;
2033 wakaba 1.21 if (defined $ic) {
2034     ## TODO: Test for this case
2035     my $ic_charset = $Message::Charset::Info::IANACharset->{$ic};
2036     if ($charset ne $ic_charset) {
2037 wakaba 1.32 $self->{onerror}->(node => $attr,
2038 wakaba 1.104 type => 'mismatched charset name',
2039 wakaba 1.106 text => $ic,
2040 wakaba 1.104 value => $charset_value,
2041     level => $self->{level}->{must});
2042 wakaba 1.21 }
2043     } else {
2044     ## NOTE: MUST, but not checkable, since the document is not originally
2045     ## in serialized form (or the parser does not preserve the input
2046     ## encoding information).
2047 wakaba 1.32 $self->{onerror}->(node => $attr,
2048 wakaba 1.104 type => 'mismatched charset name not checked',
2049     value => $charset_value,
2050     level => $self->{level}->{uncertain});
2051 wakaba 1.21 }
2052    
2053 wakaba 1.32 if ($attr->get_user_data ('manakai_has_reference')) {
2054     $self->{onerror}->(node => $attr,
2055 wakaba 1.104 type => 'charref in charset',
2056     level => $self->{level}->{must},
2057     layer => 'syntax');
2058 wakaba 1.22 }
2059 wakaba 1.32 }; # $check_charset
2060    
2061     ## TODO: metadata conformance
2062    
2063     ## TODO: pragma conformance
2064     if (defined $http_equiv_attr) { ## An enumerated attribute
2065     my $keyword = lc $http_equiv_attr->value; ## TODO: ascii case?
2066 wakaba 1.33
2067 wakaba 1.85 if ($self->{has_http_equiv}->{$keyword}) {
2068     $self->{onerror}->(type => 'duplicate http-equiv', value => $keyword,
2069     node => $http_equiv_attr,
2070 wakaba 1.104 level => $self->{level}->{must});
2071 wakaba 1.85 } else {
2072     $self->{has_http_equiv}->{$keyword} = 1;
2073     }
2074    
2075     if ($keyword eq 'content-type') {
2076 wakaba 1.58 ## TODO: refs in "text/html; charset=" are not disallowed since rev.1275.
2077 wakaba 1.33
2078 wakaba 1.32 $check_charset_decl->();
2079     if ($content_attr) {
2080     my $content = $content_attr->value;
2081 wakaba 1.58 if ($content =~ m!^[Tt][Ee][Xx][Tt]/[Hh][Tt][Mm][Ll];
2082     [\x09-\x0D\x20]*[Cc][Hh][Aa][Rr][Ss][Ee][Tt]
2083     =(.+)\z!sx) {
2084 wakaba 1.32 $check_charset->($content_attr, $1);
2085     } else {
2086     $self->{onerror}->(node => $content_attr,
2087     type => 'meta content-type syntax error',
2088 wakaba 1.104 level => $self->{level}->{must});
2089 wakaba 1.85 }
2090     }
2091     } elsif ($keyword eq 'default-style') {
2092     ## ISSUE: Not defined yet in the spec.
2093     } elsif ($keyword eq 'refresh') {
2094     if ($content_attr) {
2095     my $content = $content_attr->value;
2096     if ($content =~ /\A[0-9]+\z/) {
2097     ## NOTE: Valid non-negative integer.
2098     #
2099     } elsif ($content =~ s/\A[0-9]+;[\x09-\x0D\x20]+[Uu][Rr][Ll]=//) {
2100     ## ISSUE: Relative references are allowed? (RFC 3987 "IRI" is an absolute reference with optional fragment identifier.)
2101     Whatpm::URIChecker->check_iri_reference ($content, sub {
2102 wakaba 1.104 $self->{onerror}->(value => $content, @_, node => $content_attr);
2103 wakaba 1.106 }, $self->{level});
2104 wakaba 1.85 $self->{has_uri_attr} = 1; ## NOTE: One of "attributes with URIs".
2105    
2106     $element_state->{uri_info}->{content}->{node} = $content_attr;
2107     $element_state->{uri_info}->{content}->{type}->{hyperlink} = 1;
2108     ## TODO: absolute
2109     push @{$self->{return}->{uri}->{$content} ||= []},
2110     $element_state->{uri_info}->{content};
2111     } else {
2112     $self->{onerror}->(node => $content_attr,
2113     type => 'refresh:syntax error',
2114 wakaba 1.104 level => $self->{level}->{must});
2115 wakaba 1.32 }
2116     }
2117     } else {
2118     $self->{onerror}->(node => $http_equiv_attr,
2119 wakaba 1.104 type => 'enumerated:invalid',
2120     level => $self->{level}->{must});
2121 wakaba 1.32 }
2122     }
2123    
2124     if (defined $charset_attr) {
2125     $check_charset_decl->();
2126     $check_charset->($charset_attr, $charset_attr->value);
2127 wakaba 1.1 }
2128     },
2129     };
2130    
2131     $Element->{$HTML_NS}->{style} = {
2132 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2133 wakaba 1.40 %HTMLChecker,
2134     check_attrs => $GetHTMLAttrsChecker->({
2135 wakaba 1.1 type => $HTMLIMTAttrChecker, ## TODO: MUST be a styling language
2136     media => $HTMLMQAttrChecker,
2137     scoped => $GetHTMLBooleanAttrChecker->('scoped'),
2138     ## NOTE: |title| has special semantics for |style|s, but is syntactically
2139     ## not different
2140 wakaba 1.49 }, {
2141     %HTMLAttrStatus,
2142 wakaba 1.82 %XHTML2CommonAttrStatus,
2143     dir => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2144     disabled => FEATURE_XHTML2_ED,
2145 wakaba 1.121 href => FEATURE_RDFA_PR | FEATURE_XHTML2_ED,
2146 wakaba 1.82 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_XHTML10_REC,
2147 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2148 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2149 wakaba 1.50 scoped => FEATURE_HTML5_DEFAULT,
2150 wakaba 1.82 title => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2151     type => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2152 wakaba 1.1 }),
2153 wakaba 1.40 check_start => sub {
2154     my ($self, $item, $element_state) = @_;
2155    
2156 wakaba 1.27 ## NOTE: |html:style| itself has no conformance creteria on content model.
2157 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
2158 wakaba 1.93 $type = 'text/css' unless defined $type;
2159     if ($type =~ m[\A(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*/(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*\z]) {
2160     $type = "$1/$2";
2161     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
2162     } else {
2163     ## NOTE: We don't know how parameters are handled by UAs. According to
2164     ## HTML5 specification, <style> with unknown parameters in |type=""|
2165     ## must be ignored.
2166     undef $type;
2167     }
2168     if (not defined $type) {
2169     $element_state->{allow_element} = 1; # invalid type=""
2170     } elsif ($type eq 'text/css') {
2171 wakaba 1.40 $element_state->{allow_element} = 0;
2172 wakaba 1.93 #} elsif ($type =~ m![/+][Xx][Mm][Ll]\z!) {
2173     # ## NOTE: There is no definition for "XML-based styling language" in HTML5
2174     # $element_state->{allow_element} = 1;
2175 wakaba 1.40 } else {
2176     $element_state->{allow_element} = 1; # unknown
2177     }
2178 wakaba 1.93 $element_state->{style_type} = $type;
2179 wakaba 1.79
2180     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2181     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2182 wakaba 1.107
2183     $element_state->{text} = '';
2184 wakaba 1.40 },
2185     check_child_element => sub {
2186     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2187     $child_is_transparent, $element_state) = @_;
2188     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2189     $self->{onerror}->(node => $child_el,
2190     type => 'element not allowed:minus',
2191 wakaba 1.104 level => $self->{level}->{must});
2192 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2193     #
2194     } elsif ($element_state->{allow_element}) {
2195     #
2196     } else {
2197 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2198     level => $self->{level}->{must});
2199 wakaba 1.40 }
2200     },
2201     check_child_text => sub {
2202     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2203 wakaba 1.115 $element_state->{text} .= $child_node->data;
2204 wakaba 1.40 },
2205     check_end => sub {
2206     my ($self, $item, $element_state) = @_;
2207 wakaba 1.93 if (not defined $element_state->{style_type}) {
2208     ## NOTE: Invalid type=""
2209     #
2210     } elsif ($element_state->{style_type} eq 'text/css') {
2211 wakaba 1.40 $self->{onsubdoc}->({s => $element_state->{text},
2212     container_node => $item->{node},
2213 wakaba 1.28 media_type => 'text/css', is_char_string => 1});
2214 wakaba 1.93 } elsif ($element_state->{style_type} =~ m![+/][Xx][Mm][Ll]\z!) {
2215     ## NOTE: XML content should be checked by THIS instance of checker
2216     ## as part of normal tree validation. However, we don't know of any
2217     ## XML-based styling language that can be used in HTML <style> element,
2218     ## such that we throw a "style language not supported" error.
2219 wakaba 1.104 $self->{onerror}->(node => $item->{node},
2220     type => 'XML style lang',
2221     text => $element_state->{style_type},
2222     level => $self->{level}->{uncertain});
2223 wakaba 1.93 } else {
2224     ## NOTE: Should we raise some kind of error for,
2225     ## say, <style type="text/plaion">?
2226     $self->{onsubdoc}->({s => $element_state->{text},
2227     container_node => $item->{node},
2228     media_type => $element_state->{style_type},
2229     is_char_string => 1});
2230 wakaba 1.27 }
2231 wakaba 1.40
2232     $HTMLChecker{check_end}->(@_);
2233 wakaba 1.1 },
2234     };
2235 wakaba 1.25 ## ISSUE: Relationship to significant content check?
2236 wakaba 1.1
2237     $Element->{$HTML_NS}->{body} = {
2238 wakaba 1.72 %HTMLFlowContentChecker,
2239 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2240 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2241     alink => $HTMLColorAttrChecker,
2242     background => $HTMLURIAttrChecker,
2243     bgcolor => $HTMLColorAttrChecker,
2244     link => $HTMLColorAttrChecker,
2245     text => $HTMLColorAttrChecker,
2246     vlink => $HTMLColorAttrChecker,
2247     }, {
2248 wakaba 1.49 %HTMLAttrStatus,
2249 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2250 wakaba 1.49 alink => FEATURE_M12N10_REC_DEPRECATED,
2251     background => FEATURE_M12N10_REC_DEPRECATED,
2252     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
2253 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2254 wakaba 1.49 link => FEATURE_M12N10_REC_DEPRECATED,
2255 wakaba 1.50 onload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2256     onunload => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2257 wakaba 1.49 text => FEATURE_M12N10_REC_DEPRECATED,
2258     vlink => FEATURE_M12N10_REC_DEPRECATED,
2259     }),
2260 wakaba 1.68 check_start => sub {
2261     my ($self, $item, $element_state) = @_;
2262    
2263     $element_state->{uri_info}->{background}->{type}->{embedded} = 1;
2264 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2265     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2266 wakaba 1.68 },
2267 wakaba 1.1 };
2268    
2269     $Element->{$HTML_NS}->{section} = {
2270 wakaba 1.72 %HTMLFlowContentChecker,
2271 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2272     check_attrs => $GetHTMLAttrsChecker->({
2273     }, {
2274     %HTMLAttrStatus,
2275     %XHTML2CommonAttrStatus,
2276     }),
2277 wakaba 1.1 };
2278    
2279     $Element->{$HTML_NS}->{nav} = {
2280 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2281 wakaba 1.72 %HTMLFlowContentChecker,
2282 wakaba 1.1 };
2283    
2284     $Element->{$HTML_NS}->{article} = {
2285 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2286 wakaba 1.72 %HTMLFlowContentChecker,
2287 wakaba 1.1 };
2288    
2289     $Element->{$HTML_NS}->{blockquote} = {
2290 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2291 wakaba 1.72 %HTMLFlowContentChecker,
2292 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2293 wakaba 1.1 cite => $HTMLURIAttrChecker,
2294 wakaba 1.49 }, {
2295     %HTMLAttrStatus,
2296 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2297 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2298 wakaba 1.82 cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2299 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2300 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2301 wakaba 1.1 }),
2302 wakaba 1.66 check_start => sub {
2303     my ($self, $item, $element_state) = @_;
2304    
2305     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2306 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2307     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2308 wakaba 1.66 },
2309 wakaba 1.1 };
2310    
2311     $Element->{$HTML_NS}->{aside} = {
2312 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2313 wakaba 1.72 %HTMLFlowContentChecker,
2314 wakaba 1.1 };
2315    
2316     $Element->{$HTML_NS}->{h1} = {
2317 wakaba 1.40 %HTMLPhrasingContentChecker,
2318 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2319 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2320     align => $GetHTMLEnumeratedAttrChecker->({
2321     left => 1, center => 1, right => 1, justify => 1,
2322     }),
2323     }, {
2324 wakaba 1.49 %HTMLAttrStatus,
2325 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2326 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2327 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2328 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2329 wakaba 1.49 }),
2330 wakaba 1.40 check_start => sub {
2331     my ($self, $item, $element_state) = @_;
2332     $self->{flag}->{has_hn} = 1;
2333 wakaba 1.79
2334     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2335     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2336 wakaba 1.1 },
2337     };
2338    
2339 wakaba 1.40 $Element->{$HTML_NS}->{h2} = {%{$Element->{$HTML_NS}->{h1}}};
2340 wakaba 1.1
2341 wakaba 1.40 $Element->{$HTML_NS}->{h3} = {%{$Element->{$HTML_NS}->{h1}}};
2342 wakaba 1.1
2343 wakaba 1.40 $Element->{$HTML_NS}->{h4} = {%{$Element->{$HTML_NS}->{h1}}};
2344 wakaba 1.1
2345 wakaba 1.40 $Element->{$HTML_NS}->{h5} = {%{$Element->{$HTML_NS}->{h1}}};
2346 wakaba 1.1
2347 wakaba 1.40 $Element->{$HTML_NS}->{h6} = {%{$Element->{$HTML_NS}->{h1}}};
2348 wakaba 1.1
2349 wakaba 1.29 ## TODO: Explicit sectioning is "encouraged".
2350    
2351 wakaba 1.1 $Element->{$HTML_NS}->{header} = {
2352 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2353 wakaba 1.72 %HTMLFlowContentChecker,
2354 wakaba 1.40 check_start => sub {
2355     my ($self, $item, $element_state) = @_;
2356     $self->_add_minus_elements ($element_state,
2357     {$HTML_NS => {qw/header 1 footer 1/}},
2358 wakaba 1.58 $HTMLSectioningContent);
2359 wakaba 1.40 $element_state->{has_hn_original} = $self->{flag}->{has_hn};
2360     $self->{flag}->{has_hn} = 0;
2361 wakaba 1.79
2362     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2363     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2364 wakaba 1.40 },
2365     check_end => sub {
2366     my ($self, $item, $element_state) = @_;
2367     $self->_remove_minus_elements ($element_state);
2368     unless ($self->{flag}->{has_hn}) {
2369     $self->{onerror}->(node => $item->{node},
2370 wakaba 1.104 type => 'element missing:hn',
2371     level => $self->{level}->{must});
2372 wakaba 1.40 }
2373     $self->{flag}->{has_hn} ||= $element_state->{has_hn_original};
2374 wakaba 1.1
2375 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2376 wakaba 1.1 },
2377 wakaba 1.40 ## ISSUE: <header><del><h1>...</h1></del></header> is conforming?
2378 wakaba 1.1 };
2379    
2380     $Element->{$HTML_NS}->{footer} = {
2381 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2382 wakaba 1.72 %HTMLFlowContentChecker,
2383 wakaba 1.40 check_start => sub {
2384     my ($self, $item, $element_state) = @_;
2385     $self->_add_minus_elements ($element_state,
2386     {$HTML_NS => {footer => 1}},
2387 wakaba 1.58 $HTMLSectioningContent,
2388 wakaba 1.57 $HTMLHeadingContent);
2389 wakaba 1.79
2390     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2391     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2392 wakaba 1.40 },
2393     check_end => sub {
2394     my ($self, $item, $element_state) = @_;
2395     $self->_remove_minus_elements ($element_state);
2396 wakaba 1.1
2397 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2398 wakaba 1.1 },
2399     };
2400    
2401     $Element->{$HTML_NS}->{address} = {
2402 wakaba 1.72 %HTMLFlowContentChecker,
2403 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2404 wakaba 1.110 check_attrs => $GetHTMLAttrsChecker->({
2405     ## TODO: add test
2406     #align => $GetHTMLEnumeratedAttrChecker->({
2407     # left => 1, center => 1, right => 1, justify => 1,
2408     #}),
2409     }, {
2410 wakaba 1.49 %HTMLAttrStatus,
2411 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2412 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2413 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2414 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2415     sdapref => FEATURE_HTML20_RFC,
2416 wakaba 1.49 }),
2417 wakaba 1.40 check_start => sub {
2418     my ($self, $item, $element_state) = @_;
2419     $self->_add_minus_elements ($element_state,
2420     {$HTML_NS => {footer => 1, address => 1}},
2421     $HTMLSectioningContent, $HTMLHeadingContent);
2422 wakaba 1.79
2423     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2424     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2425 wakaba 1.40 },
2426     check_end => sub {
2427     my ($self, $item, $element_state) = @_;
2428     $self->_remove_minus_elements ($element_state);
2429 wakaba 1.29
2430 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
2431 wakaba 1.29 },
2432 wakaba 1.1 };
2433    
2434     $Element->{$HTML_NS}->{p} = {
2435 wakaba 1.40 %HTMLPhrasingContentChecker,
2436 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2437 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2438     align => $GetHTMLEnumeratedAttrChecker->({
2439     left => 1, center => 1, right => 1, justify => 1,
2440     }),
2441     }, {
2442 wakaba 1.49 %HTMLAttrStatus,
2443 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2444 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
2445 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2446 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2447 wakaba 1.49 }),
2448 wakaba 1.1 };
2449    
2450     $Element->{$HTML_NS}->{hr} = {
2451 wakaba 1.40 %HTMLEmptyChecker,
2452 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2453 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
2454     ## TODO: HTML4 |align|, |noshade|, |size|, |width|
2455     }, {
2456 wakaba 1.49 %HTMLAttrStatus,
2457     %HTMLM12NCommonAttrStatus,
2458     align => FEATURE_M12N10_REC_DEPRECATED,
2459 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2460 wakaba 1.49 noshade => FEATURE_M12N10_REC_DEPRECATED,
2461 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2462 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
2463     width => FEATURE_M12N10_REC_DEPRECATED,
2464     }),
2465 wakaba 1.1 };
2466    
2467     $Element->{$HTML_NS}->{br} = {
2468 wakaba 1.40 %HTMLEmptyChecker,
2469 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2470 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2471     clear => $GetHTMLEnumeratedAttrChecker->({
2472     left => 1, all => 1, right => 1, none => 1,
2473     }),
2474     }, {
2475 wakaba 1.49 %HTMLAttrStatus,
2476 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2477 wakaba 1.49 clear => FEATURE_M12N10_REC_DEPRECATED,
2478 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2479 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
2480 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2481 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2482 wakaba 1.49 }),
2483 wakaba 1.29 ## NOTE: Blank line MUST NOT be used for presentation purpose.
2484     ## (This requirement is semantic so that we cannot check.)
2485 wakaba 1.1 };
2486    
2487     $Element->{$HTML_NS}->{dialog} = {
2488 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2489 wakaba 1.40 %HTMLChecker,
2490     check_start => sub {
2491     my ($self, $item, $element_state) = @_;
2492     $element_state->{phase} = 'before dt';
2493 wakaba 1.79
2494     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2495     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2496 wakaba 1.40 },
2497     check_child_element => sub {
2498     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2499     $child_is_transparent, $element_state) = @_;
2500     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2501     $self->{onerror}->(node => $child_el,
2502     type => 'element not allowed:minus',
2503 wakaba 1.104 level => $self->{level}->{must});
2504 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2505     #
2506     } elsif ($element_state->{phase} eq 'before dt') {
2507     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2508     $element_state->{phase} = 'before dd';
2509     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2510     $self->{onerror}
2511 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2512     text => 'dt',
2513     level => $self->{level}->{must});
2514 wakaba 1.40 $element_state->{phase} = 'before dt';
2515     } else {
2516 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2517     level => $self->{level}->{must});
2518 wakaba 1.40 }
2519     } elsif ($element_state->{phase} eq 'before dd') {
2520     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2521     $element_state->{phase} = 'before dt';
2522     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2523     $self->{onerror}
2524 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2525     text => 'dd',
2526     level => $self->{level}->{must});
2527 wakaba 1.40 $element_state->{phase} = 'before dd';
2528     } else {
2529 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2530     level => $self->{level}->{must});
2531 wakaba 1.1 }
2532 wakaba 1.40 } else {
2533     die "check_child_element: Bad |dialog| phase: $element_state->{phase}";
2534     }
2535     },
2536     check_child_text => sub {
2537     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2538     if ($has_significant) {
2539 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2540     level => $self->{level}->{must});
2541 wakaba 1.1 }
2542 wakaba 1.40 },
2543     check_end => sub {
2544     my ($self, $item, $element_state) = @_;
2545     if ($element_state->{phase} eq 'before dd') {
2546     $self->{onerror}->(node => $item->{node},
2547 wakaba 1.104 type => 'child element missing',
2548     text => 'dd',
2549     level => $self->{level}->{must});
2550 wakaba 1.1 }
2551 wakaba 1.40
2552     $HTMLChecker{check_end}->(@_);
2553 wakaba 1.1 },
2554     };
2555    
2556     $Element->{$HTML_NS}->{pre} = {
2557 wakaba 1.40 %HTMLPhrasingContentChecker,
2558 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2559 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2560     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
2561     }, {
2562 wakaba 1.49 %HTMLAttrStatus,
2563 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2564 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2565 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2566 wakaba 1.49 width => FEATURE_M12N10_REC_DEPRECATED,
2567     }),
2568 wakaba 1.101 check_end => sub {
2569     my ($self, $item, $element_state) = @_;
2570    
2571     ## TODO: Flag to enable/disable IDL checking?
2572     my $class = $item->{node}->get_attribute ('class');
2573 wakaba 1.102 if ($class =~ /\bidl(?>-code)?\b/) { ## TODO: use classList.has
2574     ## NOTE: pre.idl: WHATWG, XHR, Selectors API, CSSOM specs
2575     ## NOTE: pre.code > code.idl-code: WebIDL spec
2576     ## NOTE: pre.idl-code: DOM1 spec
2577     ## NOTE: div.idl-code > pre: DOM, ProgressEvent specs
2578     ## NOTE: pre.schema: ReSpec-generated specs
2579 wakaba 1.101 $self->{onsubdoc}->({s => $item->{node}->text_content,
2580     container_node => $item->{node},
2581     media_type => 'text/x-webidl',
2582     is_char_string => 1});
2583     }
2584    
2585 wakaba 1.110 $HTMLPhrasingContentChecker{check_end}->(@_);
2586 wakaba 1.101 },
2587 wakaba 1.1 };
2588    
2589     $Element->{$HTML_NS}->{ol} = {
2590 wakaba 1.40 %HTMLChecker,
2591 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2592 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2593 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2594 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2595 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2596 wakaba 1.69 ## TODO: HTML4 |type|
2597 wakaba 1.49 }, {
2598     %HTMLAttrStatus,
2599 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2600 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2601 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2602 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2603 wakaba 1.53 reversed => FEATURE_HTML5_DEFAULT,
2604 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2605 wakaba 1.54 #start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
2606     start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2607 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2608 wakaba 1.1 }),
2609 wakaba 1.40 check_child_element => sub {
2610     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2611     $child_is_transparent, $element_state) = @_;
2612     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2613     $self->{onerror}->(node => $child_el,
2614     type => 'element not allowed:minus',
2615 wakaba 1.104 level => $self->{level}->{must});
2616 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2617     #
2618     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2619     #
2620     } else {
2621 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2622     level => $self->{level}->{must});
2623 wakaba 1.1 }
2624 wakaba 1.40 },
2625     check_child_text => sub {
2626     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2627     if ($has_significant) {
2628 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2629     level => $self->{level}->{must});
2630 wakaba 1.1 }
2631     },
2632     };
2633    
2634     $Element->{$HTML_NS}->{ul} = {
2635 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2636 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2637 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2638     compact => $GetHTMLBooleanAttrChecker->('compact'),
2639 wakaba 1.69 ## TODO: HTML4 |type|
2640     ## TODO: sdaform, align
2641 wakaba 1.68 }, {
2642 wakaba 1.49 %HTMLAttrStatus,
2643 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2644 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2645 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2646 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2647 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2648 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2649     }),
2650 wakaba 1.1 };
2651    
2652 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2653     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2654     %{$Element->{$HTML_NS}->{ul}},
2655     status => FEATURE_M12N10_REC_DEPRECATED,
2656 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2657     compact => $GetHTMLBooleanAttrChecker->('compact'),
2658     }, {
2659 wakaba 1.64 %HTMLAttrStatus,
2660     %HTMLM12NCommonAttrStatus,
2661     align => FEATURE_HTML2X_RFC,
2662     compact => FEATURE_M12N10_REC_DEPRECATED,
2663     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2664     sdaform => FEATURE_HTML20_RFC,
2665     sdapref => FEATURE_HTML20_RFC,
2666     }),
2667     };
2668    
2669 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2670 wakaba 1.72 %HTMLFlowContentChecker,
2671 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2672 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2673 wakaba 1.69 ## TODO: HTML4 |type|
2674 wakaba 1.49 value => sub {
2675 wakaba 1.1 my ($self, $attr) = @_;
2676     my $parent = $attr->owner_element->manakai_parent_element;
2677     if (defined $parent) {
2678     my $parent_ns = $parent->namespace_uri;
2679     $parent_ns = '' unless defined $parent_ns;
2680     my $parent_ln = $parent->manakai_local_name;
2681     unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') {
2682 wakaba 1.104 $self->{onerror}->(node => $attr,
2683     type => 'unknown attribute',
2684     level => $self->{level}->{uncertain});
2685 wakaba 1.1 }
2686     }
2687     $HTMLIntegerAttrChecker->($self, $attr);
2688 wakaba 1.49 }, ## TODO: test
2689     }, {
2690     %HTMLAttrStatus,
2691 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2692 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2693 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2694 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2695 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2696 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2697     # FEATURE_M12N10_REC_DEPRECATED,
2698 wakaba 1.82 value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
2699     FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2700 wakaba 1.1 }),
2701 wakaba 1.40 check_child_element => sub {
2702     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2703     $child_is_transparent, $element_state) = @_;
2704     if ($self->{flag}->{in_menu}) {
2705     $HTMLPhrasingContentChecker{check_child_element}->(@_);
2706     } else {
2707 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2708 wakaba 1.40 }
2709     },
2710     check_child_text => sub {
2711     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2712     if ($self->{flag}->{in_menu}) {
2713     $HTMLPhrasingContentChecker{check_child_text}->(@_);
2714 wakaba 1.1 } else {
2715 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2716 wakaba 1.1 }
2717     },
2718     };
2719    
2720     $Element->{$HTML_NS}->{dl} = {
2721 wakaba 1.40 %HTMLChecker,
2722 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2723 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2724     compact => $GetHTMLBooleanAttrChecker->('compact'),
2725     }, {
2726 wakaba 1.49 %HTMLAttrStatus,
2727 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2728 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2729 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2730 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2731     sdapref => FEATURE_HTML20_RFC,
2732 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2733     }),
2734 wakaba 1.40 check_start => sub {
2735     my ($self, $item, $element_state) = @_;
2736     $element_state->{phase} = 'before dt';
2737 wakaba 1.79
2738     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2739     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2740 wakaba 1.40 },
2741     check_child_element => sub {
2742     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2743     $child_is_transparent, $element_state) = @_;
2744     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2745     $self->{onerror}->(node => $child_el,
2746     type => 'element not allowed:minus',
2747 wakaba 1.104 level => $self->{level}->{must});
2748 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2749     #
2750     } elsif ($element_state->{phase} eq 'in dds') {
2751     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2752     #$element_state->{phase} = 'in dds';
2753     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2754     $element_state->{phase} = 'in dts';
2755     } else {
2756 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2757     level => $self->{level}->{must});
2758 wakaba 1.40 }
2759     } elsif ($element_state->{phase} eq 'in dts') {
2760     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2761     #$element_state->{phase} = 'in dts';
2762     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2763     $element_state->{phase} = 'in dds';
2764     } else {
2765 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2766     level => $self->{level}->{must});
2767 wakaba 1.40 }
2768     } elsif ($element_state->{phase} eq 'before dt') {
2769     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2770     $element_state->{phase} = 'in dts';
2771     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2772     $self->{onerror}
2773 wakaba 1.104 ->(node => $child_el, type => 'ps element missing',
2774     text => 'dt',
2775     level => $self->{level}->{must});
2776 wakaba 1.40 $element_state->{phase} = 'in dds';
2777     } else {
2778 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
2779     level => $self->{level}->{must});
2780 wakaba 1.1 }
2781 wakaba 1.40 } else {
2782     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2783 wakaba 1.1 }
2784 wakaba 1.40 },
2785     check_child_text => sub {
2786     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2787     if ($has_significant) {
2788 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
2789     level => $self->{level}->{must});
2790 wakaba 1.40 }
2791     },
2792     check_end => sub {
2793     my ($self, $item, $element_state) = @_;
2794     if ($element_state->{phase} eq 'in dts') {
2795     $self->{onerror}->(node => $item->{node},
2796 wakaba 1.104 type => 'child element missing',
2797     text => 'dd',
2798     level => $self->{level}->{must});
2799 wakaba 1.1 }
2800    
2801 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2802 wakaba 1.1 },
2803     };
2804    
2805     $Element->{$HTML_NS}->{dt} = {
2806 wakaba 1.40 %HTMLPhrasingContentChecker,
2807 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2808 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2809     %HTMLAttrStatus,
2810 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2811 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2812 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2813 wakaba 1.49 }),
2814 wakaba 1.1 };
2815    
2816     $Element->{$HTML_NS}->{dd} = {
2817 wakaba 1.72 %HTMLFlowContentChecker,
2818 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2819 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2820     %HTMLAttrStatus,
2821 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2822 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2823 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2824 wakaba 1.49 }),
2825 wakaba 1.1 };
2826    
2827     $Element->{$HTML_NS}->{a} = {
2828 wakaba 1.123 %HTMLTransparentChecker,
2829 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2830 wakaba 1.40 check_attrs => sub {
2831     my ($self, $item, $element_state) = @_;
2832 wakaba 1.1 my %attr;
2833 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2834 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2835     $attr_ns = '' unless defined $attr_ns;
2836     my $attr_ln = $attr->manakai_local_name;
2837     my $checker;
2838 wakaba 1.73 my $status;
2839 wakaba 1.1 if ($attr_ns eq '') {
2840 wakaba 1.73 $status = {
2841     %HTMLAttrStatus,
2842 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2843 wakaba 1.73 accesskey => FEATURE_M12N10_REC,
2844     charset => FEATURE_M12N10_REC,
2845 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2846 wakaba 1.73 cryptopts => FEATURE_RFC2659,
2847     dn => FEATURE_RFC2659,
2848 wakaba 1.121 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2849 wakaba 1.82 hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2850 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2851 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2852 wakaba 1.73 methods => FEATURE_HTML20_RFC,
2853     name => FEATURE_M12N10_REC_DEPRECATED,
2854     nonce => FEATURE_RFC2659,
2855     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2856     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2857     ping => FEATURE_HTML5_DEFAULT,
2858 wakaba 1.120 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2859     rev => FEATURE_RDFA_CR | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2860 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
2861 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2862 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2863 wakaba 1.82 target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2864 wakaba 1.73 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2865     urn => FEATURE_HTML20_RFC,
2866     }->{$attr_ln};
2867    
2868 wakaba 1.1 $checker = {
2869 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
2870 wakaba 1.91 charset => sub {
2871     my ($self, $attr) = @_;
2872     $HTMLCharsetChecker->($attr->value, @_);
2873     },
2874 wakaba 1.70 ## TODO: HTML4 |coords|
2875 wakaba 1.1 target => $HTMLTargetAttrChecker,
2876     href => $HTMLURIAttrChecker,
2877     ping => $HTMLSpaceURIsAttrChecker,
2878 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
2879 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
2880 wakaba 1.70 ## TODO: HTML4 |shape|
2881 wakaba 1.1 media => $HTMLMQAttrChecker,
2882 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
2883 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
2884     type => $HTMLIMTAttrChecker,
2885     }->{$attr_ln};
2886     if ($checker) {
2887     $attr{$attr_ln} = $attr;
2888 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
2889     $attr_ln !~ /[A-Z]/) {
2890 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
2891     $status = $HTMLDatasetAttrStatus;
2892 wakaba 1.1 } else {
2893     $checker = $HTMLAttrChecker->{$attr_ln};
2894     }
2895     }
2896     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2897     || $AttrChecker->{$attr_ns}->{''};
2898 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2899     || $AttrStatus->{$attr_ns}->{''};
2900     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
2901 wakaba 1.62
2902 wakaba 1.1 if ($checker) {
2903 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2904 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2905 wakaba 1.54 #
2906 wakaba 1.1 } else {
2907 wakaba 1.104 $self->{onerror}->(node => $attr,
2908     type => 'unknown attribute',
2909     level => $self->{level}->{uncertain});
2910 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
2911 wakaba 1.1 }
2912 wakaba 1.49
2913 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2914 wakaba 1.1 }
2915    
2916 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
2917 wakaba 1.4 if (defined $attr{href}) {
2918     $self->{has_hyperlink_element} = 1;
2919 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
2920 wakaba 1.4 } else {
2921 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
2922     if (defined $attr{$_}) {
2923     $self->{onerror}->(node => $attr{$_},
2924 wakaba 1.104 type => 'attribute not allowed',
2925     level => $self->{level}->{must});
2926 wakaba 1.1 }
2927     }
2928     }
2929 wakaba 1.66
2930     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
2931 wakaba 1.1 },
2932 wakaba 1.40 check_start => sub {
2933     my ($self, $item, $element_state) = @_;
2934     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
2935 wakaba 1.79
2936     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2937     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2938 wakaba 1.40 },
2939     check_end => sub {
2940     my ($self, $item, $element_state) = @_;
2941     $self->_remove_minus_elements ($element_state);
2942 wakaba 1.59 delete $self->{flag}->{in_a_href}
2943     unless $element_state->{in_a_href_original};
2944 wakaba 1.1
2945 wakaba 1.123 $HTMLTransparentChecker{check_end}->(@_);
2946 wakaba 1.1 },
2947     };
2948    
2949     $Element->{$HTML_NS}->{q} = {
2950 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2951 wakaba 1.40 %HTMLPhrasingContentChecker,
2952     check_attrs => $GetHTMLAttrsChecker->({
2953 wakaba 1.50 cite => $HTMLURIAttrChecker,
2954     }, {
2955 wakaba 1.49 %HTMLAttrStatus,
2956 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2957     cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2958 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2959 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2960     sdasuff => FEATURE_HTML2X_RFC,
2961 wakaba 1.1 }),
2962 wakaba 1.66 check_start => sub {
2963     my ($self, $item, $element_state) = @_;
2964    
2965     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2966 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2967     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2968 wakaba 1.66 },
2969 wakaba 1.1 };
2970 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
2971     ## placed inside the <code>q</code> element." Though we cannot test the
2972     ## element against this requirement since it incluides a semantic bit,
2973     ## it might be possible to inform of the existence of quotation marks OUTSIDE
2974     ## the |q| element.
2975 wakaba 1.1
2976     $Element->{$HTML_NS}->{cite} = {
2977 wakaba 1.40 %HTMLPhrasingContentChecker,
2978 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2979 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2980     %HTMLAttrStatus,
2981 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2982 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2983 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2984 wakaba 1.49 }),
2985 wakaba 1.1 };
2986    
2987     $Element->{$HTML_NS}->{em} = {
2988 wakaba 1.40 %HTMLPhrasingContentChecker,
2989 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2990 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2991     %HTMLAttrStatus,
2992 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2993 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2994 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2995 wakaba 1.49 }),
2996 wakaba 1.1 };
2997    
2998     $Element->{$HTML_NS}->{strong} = {
2999 wakaba 1.40 %HTMLPhrasingContentChecker,
3000 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3001 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3002     %HTMLAttrStatus,
3003 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3004 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3005 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3006 wakaba 1.49 }),
3007 wakaba 1.1 };
3008    
3009     $Element->{$HTML_NS}->{small} = {
3010 wakaba 1.40 %HTMLPhrasingContentChecker,
3011 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3012     check_attrs => $GetHTMLAttrsChecker->({}, {
3013     %HTMLAttrStatus,
3014     %HTMLM12NCommonAttrStatus,
3015 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3016 wakaba 1.49 }),
3017 wakaba 1.1 };
3018    
3019 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
3020     %HTMLPhrasingContentChecker,
3021     status => FEATURE_M12N10_REC,
3022     check_attrs => $GetHTMLAttrsChecker->({}, {
3023     %HTMLAttrStatus,
3024     %HTMLM12NCommonAttrStatus,
3025     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3026     }),
3027     };
3028    
3029 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
3030 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3031 wakaba 1.40 %HTMLPhrasingContentChecker,
3032 wakaba 1.1 };
3033    
3034     $Element->{$HTML_NS}->{dfn} = {
3035 wakaba 1.40 %HTMLPhrasingContentChecker,
3036 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3037 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3038     %HTMLAttrStatus,
3039 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3040 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3041 wakaba 1.49 }),
3042 wakaba 1.40 check_start => sub {
3043     my ($self, $item, $element_state) = @_;
3044     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
3045 wakaba 1.1
3046 wakaba 1.40 my $node = $item->{node};
3047 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
3048     unless (defined $term) {
3049     for my $child (@{$node->child_nodes}) {
3050     if ($child->node_type == 1) { # ELEMENT_NODE
3051     if (defined $term) {
3052     undef $term;
3053     last;
3054     } elsif ($child->manakai_local_name eq 'abbr') {
3055     my $nsuri = $child->namespace_uri;
3056     if (defined $nsuri and $nsuri eq $HTML_NS) {
3057     my $attr = $child->get_attribute_node_ns (undef, 'title');
3058     if ($attr) {
3059     $term = $attr->value;
3060     }
3061     }
3062     }
3063     } elsif ($child->node_type == 3 or $child->node_type == 4) {
3064     ## TEXT_NODE or CDATA_SECTION_NODE
3065     if ($child->data =~ /\A[\x09-\x0D\x20]+\z/) { # Inter-element whitespace
3066     next;
3067     }
3068     undef $term;
3069     last;
3070     }
3071     }
3072     unless (defined $term) {
3073     $term = $node->text_content;
3074     }
3075     }
3076     if ($self->{term}->{$term}) {
3077     push @{$self->{term}->{$term}}, $node;
3078     } else {
3079     $self->{term}->{$term} = [$node];
3080     }
3081 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
3082     ## |ruby| unless |dfn| has |title|.
3083 wakaba 1.79
3084     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3085     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3086 wakaba 1.40 },
3087     check_end => sub {
3088     my ($self, $item, $element_state) = @_;
3089     $self->_remove_minus_elements ($element_state);
3090 wakaba 1.1
3091 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3092 wakaba 1.1 },
3093     };
3094    
3095     $Element->{$HTML_NS}->{abbr} = {
3096 wakaba 1.40 %HTMLPhrasingContentChecker,
3097 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3098 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3099     %HTMLAttrStatus,
3100 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3101     full => FEATURE_XHTML2_ED,
3102 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3103 wakaba 1.49 }),
3104 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
3105     ## number (plural vs singular) must match the grammatical number of the
3106     ## contents of the element." Though this can be checked by machine,
3107     ## it requires language-specific knowledge and dictionary, such that
3108     ## we don't support the check of the requirement.
3109     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
3110 wakaba 1.49 };
3111    
3112     $Element->{$HTML_NS}->{acronym} = {
3113     %HTMLPhrasingContentChecker,
3114     status => FEATURE_M12N10_REC,
3115     check_attrs => $GetHTMLAttrsChecker->({}, {
3116     %HTMLAttrStatus,
3117     %HTMLM12NCommonAttrStatus,
3118 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3119 wakaba 1.49 }),
3120 wakaba 1.1 };
3121    
3122     $Element->{$HTML_NS}->{time} = {
3123 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3124 wakaba 1.40 %HTMLPhrasingContentChecker,
3125     check_attrs => $GetHTMLAttrsChecker->({
3126 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
3127 wakaba 1.49 }, {
3128     %HTMLAttrStatus,
3129     %HTMLM12NCommonAttrStatus,
3130 wakaba 1.72 datetime => FEATURE_HTML5_FD,
3131 wakaba 1.1 }),
3132     ## TODO: Write tests
3133 wakaba 1.40 check_end => sub {
3134     my ($self, $item, $element_state) = @_;
3135 wakaba 1.1
3136 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
3137 wakaba 1.1 my $input;
3138     my $reg_sp;
3139     my $input_node;
3140     if ($attr) {
3141     $input = $attr->value;
3142     $reg_sp = qr/[\x09-\x0D\x20]*/;
3143     $input_node = $attr;
3144     } else {
3145 wakaba 1.40 $input = $item->{node}->text_content;
3146 wakaba 1.112 $reg_sp = qr/\p{WhiteSpace}*/;
3147 wakaba 1.40 $input_node = $item->{node};
3148 wakaba 1.1
3149     ## ISSUE: What is the definition for "successfully extracts a date
3150     ## or time"? If the algorithm says the string is invalid but
3151     ## return some date or time, is it "successfully"?
3152     }
3153    
3154     my $hour;
3155     my $minute;
3156     my $second;
3157     if ($input =~ /
3158     \A
3159 wakaba 1.112 $reg_sp
3160 wakaba 1.1 ([0-9]+) # 1
3161     (?>
3162     -([0-9]+) # 2
3163 wakaba 1.112 -((?>[0-9]+)) # 3 # Use (?>) such that yyyy-mm-ddhh:mm does not match
3164     $reg_sp
3165 wakaba 1.1 (?>
3166     T
3167 wakaba 1.112 $reg_sp
3168 wakaba 1.1 )?
3169     ([0-9]+) # 4
3170     :([0-9]+) # 5
3171     (?>
3172     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
3173     )?
3174 wakaba 1.112 $reg_sp
3175 wakaba 1.1 (?>
3176     Z
3177 wakaba 1.112 $reg_sp
3178 wakaba 1.1 |
3179     [+-]([0-9]+):([0-9]+) # 7, 8
3180 wakaba 1.112 $reg_sp
3181 wakaba 1.1 )?
3182     \z
3183     |
3184     :([0-9]+) # 9
3185     (?>
3186     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
3187     )?
3188 wakaba 1.112 $reg_sp
3189     \z
3190 wakaba 1.1 )
3191     /x) {
3192     if (defined $2) { ## YYYY-MM-DD T? hh:mm
3193     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
3194     length $4 != 2 or length $5 != 2) {
3195     $self->{onerror}->(node => $input_node,
3196 wakaba 1.104 type => 'dateortime:syntax error',
3197     level => $self->{level}->{must});
3198 wakaba 1.1 }
3199    
3200     if (1 <= $2 and $2 <= 12) {
3201 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3202     level => $self->{level}->{must})
3203 wakaba 1.1 if $3 < 1 or
3204     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
3205 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad day',
3206     level => $self->{level}->{must})
3207 wakaba 1.1 if $2 == 2 and $3 == 29 and
3208     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
3209     } else {
3210     $self->{onerror}->(node => $input_node,
3211 wakaba 1.104 type => 'datetime:bad month',
3212     level => $self->{level}->{must});
3213 wakaba 1.1 }
3214    
3215     ($hour, $minute, $second) = ($4, $5, $6);
3216    
3217     if (defined $7) { ## [+-]hh:mm
3218     if (length $7 != 2 or length $8 != 2) {
3219     $self->{onerror}->(node => $input_node,
3220 wakaba 1.104 type => 'dateortime:syntax error',
3221     level => $self->{level}->{must});
3222 wakaba 1.1 }
3223    
3224     $self->{onerror}->(node => $input_node,
3225 wakaba 1.104 type => 'datetime:bad timezone hour',
3226     level => $self->{level}->{must})
3227 wakaba 1.1 if $7 > 23;
3228     $self->{onerror}->(node => $input_node,
3229 wakaba 1.104 type => 'datetime:bad timezone minute',
3230     level => $self->{level}->{must})
3231 wakaba 1.1 if $8 > 59;
3232     }
3233     } else { ## hh:mm
3234     if (length $1 != 2 or length $9 != 2) {
3235     $self->{onerror}->(node => $input_node,
3236 wakaba 1.104 type => qq'dateortime:syntax error',
3237     level => $self->{level}->{must});
3238 wakaba 1.1 }
3239    
3240     ($hour, $minute, $second) = ($1, $9, $10);
3241     }
3242    
3243 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad hour',
3244     level => $self->{level}->{must}) if $hour > 23;
3245     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute',
3246     level => $self->{level}->{must}) if $minute > 59;
3247 wakaba 1.1
3248     if (defined $second) { ## s
3249     ## NOTE: Integer part of second don't have to have length of two.
3250    
3251     if (substr ($second, 0, 1) eq '.') {
3252     $self->{onerror}->(node => $input_node,
3253 wakaba 1.104 type => 'dateortime:syntax error',
3254     level => $self->{level}->{must});
3255 wakaba 1.1 }
3256    
3257 wakaba 1.104 $self->{onerror}->(node => $input_node, type => 'datetime:bad second',
3258     level => $self->{level}->{must}) if $second >= 60;
3259 wakaba 1.1 }
3260     } else {
3261     $self->{onerror}->(node => $input_node,
3262 wakaba 1.104 type => 'dateortime:syntax error',
3263     level => $self->{level}->{must});
3264 wakaba 1.1 }
3265    
3266 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3267 wakaba 1.1 },
3268     };
3269    
3270     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3271 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3272 wakaba 1.113 ## TODO: content checking
3273     ## TODO: content or value must contain number (rev 2053)
3274 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3275 wakaba 1.40 %HTMLPhrasingContentChecker,
3276     check_attrs => $GetHTMLAttrsChecker->({
3277 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3278     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3279     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3280     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3281     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3282     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3283 wakaba 1.50 }, {
3284     %HTMLAttrStatus,
3285     high => FEATURE_HTML5_DEFAULT,
3286     low => FEATURE_HTML5_DEFAULT,
3287     max => FEATURE_HTML5_DEFAULT,
3288     min => FEATURE_HTML5_DEFAULT,
3289     optimum => FEATURE_HTML5_DEFAULT,
3290     value => FEATURE_HTML5_DEFAULT,
3291 wakaba 1.1 }),
3292     };
3293    
3294     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3295 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3296 wakaba 1.40 %HTMLPhrasingContentChecker,
3297     check_attrs => $GetHTMLAttrsChecker->({
3298 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3299     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3300 wakaba 1.50 }, {
3301     %HTMLAttrStatus,
3302     max => FEATURE_HTML5_DEFAULT,
3303     value => FEATURE_HTML5_DEFAULT,
3304 wakaba 1.1 }),
3305     };
3306    
3307     $Element->{$HTML_NS}->{code} = {
3308 wakaba 1.40 %HTMLPhrasingContentChecker,
3309 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3310 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3311     %HTMLAttrStatus,
3312 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3313 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3314 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3315 wakaba 1.49 }),
3316 wakaba 1.1 };
3317    
3318     $Element->{$HTML_NS}->{var} = {
3319 wakaba 1.40 %HTMLPhrasingContentChecker,
3320 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3321 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3322     %HTMLAttrStatus,
3323 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3324 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3325 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3326 wakaba 1.49 }),
3327 wakaba 1.1 };
3328    
3329     $Element->{$HTML_NS}->{samp} = {
3330 wakaba 1.40 %HTMLPhrasingContentChecker,
3331 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3332 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3333     %HTMLAttrStatus,
3334 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3335 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3336 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3337 wakaba 1.49 }),
3338 wakaba 1.1 };
3339    
3340     $Element->{$HTML_NS}->{kbd} = {
3341 wakaba 1.40 %HTMLPhrasingContentChecker,
3342 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3343 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3344     %HTMLAttrStatus,
3345 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3346 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3347 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3348 wakaba 1.49 }),
3349 wakaba 1.1 };
3350    
3351     $Element->{$HTML_NS}->{sub} = {
3352 wakaba 1.40 %HTMLPhrasingContentChecker,
3353 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3354 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3355     %HTMLAttrStatus,
3356 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3357 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3358 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3359 wakaba 1.49 }),
3360 wakaba 1.1 };
3361    
3362 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3363 wakaba 1.1
3364     $Element->{$HTML_NS}->{span} = {
3365 wakaba 1.40 %HTMLPhrasingContentChecker,
3366 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3367 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3368     %HTMLAttrStatus,
3369 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3370 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3371     dataformatas => FEATURE_HTML4_REC_RESERVED,
3372     datasrc => FEATURE_HTML4_REC_RESERVED,
3373 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3374 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3375 wakaba 1.49 }),
3376 wakaba 1.1 };
3377    
3378     $Element->{$HTML_NS}->{i} = {
3379 wakaba 1.40 %HTMLPhrasingContentChecker,
3380 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3381     check_attrs => $GetHTMLAttrsChecker->({}, {
3382     %HTMLAttrStatus,
3383     %HTMLM12NCommonAttrStatus,
3384 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3385 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3386 wakaba 1.49 }),
3387 wakaba 1.1 };
3388    
3389 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3390    
3391 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3392     %HTMLPhrasingContentChecker,
3393     status => FEATURE_M12N10_REC,
3394     check_attrs => $GetHTMLAttrsChecker->({}, {
3395     %HTMLAttrStatus,
3396     %HTMLM12NCommonAttrStatus,
3397     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3398     sdaform => FEATURE_HTML20_RFC,
3399     }),
3400     };
3401 wakaba 1.51
3402     $Element->{$HTML_NS}->{s} = {
3403 wakaba 1.40 %HTMLPhrasingContentChecker,
3404 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3405 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3406     %HTMLAttrStatus,
3407     %HTMLM12NCommonAttrStatus,
3408 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3409 wakaba 1.49 }),
3410 wakaba 1.1 };
3411    
3412 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3413    
3414     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3415    
3416 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3417 wakaba 1.40 %HTMLPhrasingContentChecker,
3418 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3419 wakaba 1.40 check_attrs => sub {
3420     my ($self, $item, $element_state) = @_;
3421 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3422     %HTMLAttrStatus,
3423 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3424     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3425     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3426 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3427 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3428     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3429 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3430     sdasuff => FEATURE_HTML2X_RFC,
3431 wakaba 1.49 })->($self, $item, $element_state);
3432 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3433     $self->{onerror}->(node => $item->{node},
3434 wakaba 1.104 type => 'attribute missing',
3435     text => 'dir',
3436     level => $self->{level}->{must});
3437 wakaba 1.1 }
3438     },
3439     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3440     };
3441    
3442 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3443     %HTMLPhrasingContentChecker,
3444     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3445     check_attrs => $GetHTMLAttrsChecker->({}, {
3446     %HTMLAttrStatus,
3447     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3448     lang => FEATURE_HTML5_DEFAULT,
3449     }),
3450     check_start => sub {
3451     my ($self, $item, $element_state) = @_;
3452    
3453     $element_state->{phase} = 'before-rb';
3454     #$element_state->{has_sig}
3455 wakaba 1.100
3456     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3457     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3458 wakaba 1.99 },
3459     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3460     check_child_element => sub {
3461     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3462     $child_is_transparent, $element_state) = @_;
3463     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3464     $self->{onerror}->(node => $child_el,
3465     type => 'element not allowed:minus',
3466 wakaba 1.104 level => $self->{level}->{must});
3467 wakaba 1.99 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3468     #
3469     } elsif ($element_state->{phase} eq 'before-rb') {
3470     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3471     $element_state->{phase} = 'in-rb';
3472     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3473     $self->{onerror}->(node => $child_el,
3474 wakaba 1.104 level => $self->{level}->{should},
3475     type => 'no significant content before');
3476 wakaba 1.99 $element_state->{phase} = 'after-rt';
3477     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3478     $self->{onerror}->(node => $child_el,
3479 wakaba 1.104 level => $self->{level}->{should},
3480     type => 'no significant content before');
3481 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3482     } else {
3483     $self->{onerror}->(node => $child_el,
3484 wakaba 1.104 type => 'element not allowed:ruby base',
3485     level => $self->{level}->{must});
3486 wakaba 1.99 $element_state->{phase} = 'in-rb';
3487     }
3488     } elsif ($element_state->{phase} eq 'in-rb') {
3489     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3490     #$element_state->{phase} = 'in-rb';
3491     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3492     unless ($element_state->{has_significant}) {
3493     $self->{onerror}->(node => $child_el,
3494 wakaba 1.104 level => $self->{level}->{should},
3495     type => 'no significant content before');
3496 wakaba 1.99 }
3497     $element_state->{phase} = 'after-rt';
3498     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3499     unless ($element_state->{has_significant}) {
3500     $self->{onerror}->(node => $child_el,
3501 wakaba 1.104 level => $self->{level}->{should},
3502     type => 'no significant content before');
3503 wakaba 1.99 }
3504     $element_state->{phase} = 'after-rp1';
3505     } else {
3506     $self->{onerror}->(node => $child_el,
3507 wakaba 1.104 type => 'element not allowed:ruby base',
3508     level => $self->{level}->{must});
3509 wakaba 1.99 #$element_state->{phase} = 'in-rb';
3510     }
3511     } elsif ($element_state->{phase} eq 'after-rt') {
3512     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3513     if ($element_state->{has_significant}) {
3514     $element_state->{has_sig} = 1;
3515     delete $element_state->{has_significant};
3516     }
3517     $element_state->{phase} = 'in-rb';
3518     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3519     $self->{onerror}->(node => $child_el,
3520 wakaba 1.104 level => $self->{level}->{should},
3521     type => 'no significant content before');
3522 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3523     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3524     $self->{onerror}->(node => $child_el,
3525 wakaba 1.104 level => $self->{level}->{should},
3526     type => 'no significant content before');
3527 wakaba 1.99 #$element_state->{phase} = 'after-rt';
3528     } else {
3529     $self->{onerror}->(node => $child_el,
3530 wakaba 1.104 type => 'element not allowed:ruby base',
3531     level => $self->{level}->{must});
3532 wakaba 1.99 if ($element_state->{has_significant}) {
3533     $element_state->{has_sig} = 1;
3534     delete $element_state->{has_significant};
3535     }
3536     $element_state->{phase} = 'in-rb';
3537     }
3538     } elsif ($element_state->{phase} eq 'after-rp1') {
3539     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3540     $element_state->{phase} = 'after-rp-rt';
3541     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3542     $self->{onerror}->(node => $child_el,
3543 wakaba 1.104 type => 'ps element missing',
3544     text => 'rt',
3545     level => $self->{level}->{must});
3546 wakaba 1.99 $element_state->{phase} = 'after-rp2';
3547     } else {
3548     $self->{onerror}->(node => $child_el,
3549 wakaba 1.104 type => 'ps element missing',
3550     text => 'rt',
3551     level => $self->{level}->{must});
3552 wakaba 1.99 $self->{onerror}->(node => $child_el,
3553 wakaba 1.104 type => 'ps element missing',
3554     text => 'rp',
3555     level => $self->{level}->{must});
3556 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3557     $self->{onerror}->(node => $child_el,
3558 wakaba 1.104 type => 'element not allowed:ruby base',
3559     level => $self->{level}->{must});
3560 wakaba 1.99 }
3561     if ($element_state->{has_significant}) {
3562     $element_state->{has_sig} = 1;
3563     delete $element_state->{has_significant};
3564     }
3565     $element_state->{phase} = 'in-rb';
3566     }
3567     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3568     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3569     $element_state->{phase} = 'after-rp2';
3570     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3571     $self->{onerror}->(node => $child_el,
3572 wakaba 1.104 type => 'ps element missing',
3573     text => 'rp',
3574     level => $self->{level}->{must});
3575 wakaba 1.99 $self->{onerror}->(node => $child_el,
3576 wakaba 1.104 level => $self->{level}->{should},
3577     type => 'no significant content before');
3578 wakaba 1.99 $element_state->{phase} = 'after-rt';
3579     } else {
3580     $self->{onerror}->(node => $child_el,
3581 wakaba 1.104 type => 'ps element missing',
3582     text => 'rp',
3583     level => $self->{level}->{must});
3584 wakaba 1.99 unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3585     $self->{onerror}->(node => $child_el,
3586 wakaba 1.104 type => 'element not allowed:ruby base',
3587     level => $self->{level}->{must});
3588 wakaba 1.99 }
3589     if ($element_state->{has_significant}) {
3590     $element_state->{has_sig} = 1;
3591     delete $element_state->{has_significant};
3592     }
3593     $element_state->{phase} = 'in-rb';
3594     }
3595     } elsif ($element_state->{phase} eq 'after-rp2') {
3596     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3597     if ($element_state->{has_significant}) {
3598     $element_state->{has_sig} = 1;
3599     delete $element_state->{has_significant};
3600     }
3601     $element_state->{phase} = 'in-rb';
3602     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3603     $self->{onerror}->(node => $child_el,
3604 wakaba 1.104 level => $self->{level}->{should},
3605     type => 'no significant content before');
3606 wakaba 1.99 $element_state->{phase} = 'after-rt';
3607     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3608     $self->{onerror}->(node => $child_el,
3609 wakaba 1.104 level => $self->{level}->{should},
3610     type => 'no significant content before');
3611 wakaba 1.99 $element_state->{phase} = 'after-rp1';
3612     } else {
3613     $self->{onerror}->(node => $child_el,
3614 wakaba 1.104 type => 'element not allowed:ruby base',
3615     level => $self->{level}->{must});
3616 wakaba 1.99 if ($element_state->{has_significant}) {
3617     $element_state->{has_sig} = 1;
3618     delete $element_state->{has_significant};
3619     }
3620     $element_state->{phase} = 'in-rb';
3621     }
3622     } else {
3623     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3624     }
3625     },
3626     check_child_text => sub {
3627     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3628     if ($has_significant) {
3629     if ($element_state->{phase} eq 'before-rb') {
3630     $element_state->{phase} = 'in-rb';
3631     } elsif ($element_state->{phase} eq 'in-rb') {
3632     #
3633     } elsif ($element_state->{phase} eq 'after-rt' or
3634     $element_state->{phase} eq 'after-rp2') {
3635     $element_state->{phase} = 'in-rb';
3636     } elsif ($element_state->{phase} eq 'after-rp1') {
3637     $self->{onerror}->(node => $child_node,
3638 wakaba 1.104 type => 'ps element missing',
3639     text => 'rt',
3640     level => $self->{level}->{must});
3641 wakaba 1.99 $self->{onerror}->(node => $child_node,
3642 wakaba 1.104 type => 'ps element missing',
3643     text => 'rp',
3644     level => $self->{level}->{must});
3645 wakaba 1.99 $element_state->{phase} = 'in-rb';
3646     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3647     $self->{onerror}->(node => $child_node,
3648 wakaba 1.104 type => 'ps element missing',
3649     text => 'rp',
3650     level => $self->{level}->{must});
3651 wakaba 1.99 $element_state->{phase} = 'in-rb';
3652     } else {
3653     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3654     }
3655     }
3656     },
3657     check_end => sub {
3658     my ($self, $item, $element_state) = @_;
3659     $self->_remove_minus_elements ($element_state);
3660    
3661     if ($element_state->{phase} eq 'before-rb') {
3662     $self->{onerror}->(node => $item->{node},
3663 wakaba 1.104 level => $self->{level}->{should},
3664 wakaba 1.99 type => 'no significant content');
3665     $self->{onerror}->(node => $item->{node},
3666 wakaba 1.104 type => 'element missing',
3667     text => 'rt',
3668     level => $self->{level}->{must});
3669 wakaba 1.99 } elsif ($element_state->{phase} eq 'in-rb') {
3670     unless ($element_state->{has_significant}) {
3671     $self->{onerror}->(node => $item->{node},
3672 wakaba 1.104 level => $self->{level}->{should},
3673     type => 'no significant content at the end');
3674 wakaba 1.99 }
3675     $self->{onerror}->(node => $item->{node},
3676 wakaba 1.104 type => 'element missing',
3677     text => 'rt',
3678     level => $self->{level}->{must});
3679 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rt' or
3680     $element_state->{phase} eq 'after-rp2') {
3681     #
3682     } elsif ($element_state->{phase} eq 'after-rp1') {
3683     $self->{onerror}->(node => $item->{node},
3684 wakaba 1.104 type => 'element missing',
3685     text => 'rt',
3686     level => $self->{level}->{must});
3687 wakaba 1.99 $self->{onerror}->(node => $item->{node},
3688 wakaba 1.104 type => 'element missing',
3689     text => 'rp',
3690     level => $self->{level}->{must});
3691 wakaba 1.99 } elsif ($element_state->{phase} eq 'after-rp-rt') {
3692     $self->{onerror}->(node => $item->{node},
3693 wakaba 1.104 type => 'element missing',
3694     text => 'rp',
3695     level => $self->{level}->{must});
3696 wakaba 1.99 } else {
3697     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3698     }
3699    
3700     ## NOTE: A modified version of |check_end| of %AnyChecker.
3701     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3702     $item->{real_parent_state}->{has_significant} = 1;
3703     }
3704     },
3705     };
3706    
3707     $Element->{$HTML_NS}->{rt} = {
3708     %HTMLPhrasingContentChecker,
3709     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3710     check_attrs => $GetHTMLAttrsChecker->({}, {
3711     %HTMLAttrStatus,
3712     %HTMLM12NXHTML2CommonAttrStatus,
3713     lang => FEATURE_HTML5_DEFAULT,
3714     }),
3715     };
3716    
3717     $Element->{$HTML_NS}->{rp} = {
3718     %HTMLTextChecker,
3719     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3720     check_attrs => $GetHTMLAttrsChecker->({}, {
3721     %HTMLAttrStatus,
3722     %HTMLM12NXHTML2CommonAttrStatus,
3723     lang => FEATURE_HTML5_DEFAULT,
3724     }),
3725 wakaba 1.100 check_start => sub {
3726 wakaba 1.99 my ($self, $item, $element_state) = @_;
3727     $element_state->{text} = '';
3728 wakaba 1.100
3729     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3730     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3731 wakaba 1.99 },
3732     check_child_text => sub {
3733     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3734     if ($has_significant) {
3735     $element_state->{text} .= $child_node->data;
3736     ## NOTE: |<rp> <!---->(</rp>| is allowed.
3737     }
3738     },
3739     check_end => sub {
3740     my ($self, $item, $element_state) = @_;
3741    
3742     my $p_class = ($item->{parent_state} and
3743     $item->{parent_state}->{phase} and
3744     $item->{parent_state}->{phase} eq 'after-rp2')
3745     ? qr/\p{Pe}/ : qr/\p{Ps}/;
3746     if ($element_state->{text} =~ /\A$p_class\z/) {
3747     #=~ /\A[\x09-\x0D\x20]*${p_class}[\x09-\x0D\x20]*\z/) {
3748     #
3749     } else {
3750     $self->{onerror}->(node => $item->{node},
3751 wakaba 1.104 type => 'rp:syntax error',
3752     level => $self->{level}->{must});
3753 wakaba 1.99 }
3754    
3755     $HTMLTextChecker{check_end}->(@_);
3756     },
3757     };
3758    
3759 wakaba 1.29 =pod
3760    
3761     ## TODO:
3762    
3763     +
3764     + <p>Partly because of the confusion described above, authors are
3765     + strongly recommended to always mark up all paragraphs with the
3766     + <code>p</code> element, and to not have any <code>ins</code> or
3767     + <code>del</code> elements that cross across any <span
3768     + title="paragraph">implied paragraphs</span>.</p>
3769     +
3770     (An informative note)
3771    
3772     <p><code>ins</code> elements should not cross <span
3773     + title="paragraph">implied paragraph</span> boundaries.</p>
3774     (normative)
3775    
3776     + <p><code>del</code> elements should not cross <span
3777     + title="paragraph">implied paragraph</span> boundaries.</p>
3778     (normative)
3779    
3780     =cut
3781    
3782 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3783 wakaba 1.40 %HTMLTransparentChecker,
3784 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3785 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3786 wakaba 1.1 cite => $HTMLURIAttrChecker,
3787     datetime => $HTMLDatetimeAttrChecker,
3788 wakaba 1.49 }, {
3789     %HTMLAttrStatus,
3790     %HTMLM12NCommonAttrStatus,
3791 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3792 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3793 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3794 wakaba 1.1 }),
3795 wakaba 1.66 check_start => sub {
3796     my ($self, $item, $element_state) = @_;
3797    
3798     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3799 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3800     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3801 wakaba 1.66 },
3802 wakaba 1.1 };
3803    
3804     $Element->{$HTML_NS}->{del} = {
3805 wakaba 1.40 %HTMLTransparentChecker,
3806 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3807 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3808 wakaba 1.1 cite => $HTMLURIAttrChecker,
3809     datetime => $HTMLDatetimeAttrChecker,
3810 wakaba 1.49 }, {
3811     %HTMLAttrStatus,
3812     %HTMLM12NCommonAttrStatus,
3813 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3814 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3815 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3816 wakaba 1.1 }),
3817 wakaba 1.40 check_end => sub {
3818     my ($self, $item, $element_state) = @_;
3819     if ($element_state->{has_significant}) {
3820     ## NOTE: Significantness flag does not propagate.
3821     } elsif ($item->{transparent}) {
3822     #
3823     } else {
3824     $self->{onerror}->(node => $item->{node},
3825 wakaba 1.104 level => $self->{level}->{should},
3826 wakaba 1.40 type => 'no significant content');
3827     }
3828 wakaba 1.1 },
3829 wakaba 1.66 check_start => sub {
3830     my ($self, $item, $element_state) = @_;
3831    
3832     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3833 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3834     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3835 wakaba 1.66 },
3836 wakaba 1.1 };
3837    
3838 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3839 wakaba 1.72 %HTMLFlowContentChecker,
3840 wakaba 1.48 status => FEATURE_HTML5_FD,
3841 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3842 wakaba 1.41 check_child_element => sub {
3843     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3844     $child_is_transparent, $element_state) = @_;
3845     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3846     $self->{onerror}->(node => $child_el,
3847     type => 'element not allowed:minus',
3848 wakaba 1.104 level => $self->{level}->{must});
3849 wakaba 1.41 $element_state->{has_non_legend} = 1;
3850     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3851     #
3852     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3853     if ($element_state->{has_legend_at_first}) {
3854     $self->{onerror}->(node => $child_el,
3855     type => 'element not allowed:figure legend',
3856 wakaba 1.104 level => $self->{level}->{must});
3857 wakaba 1.41 } elsif ($element_state->{has_legend}) {
3858     $self->{onerror}->(node => $element_state->{has_legend},
3859     type => 'element not allowed:figure legend',
3860 wakaba 1.104 level => $self->{level}->{must});
3861 wakaba 1.41 $element_state->{has_legend} = $child_el;
3862     } elsif ($element_state->{has_non_legend}) {
3863     $element_state->{has_legend} = $child_el;
3864     } else {
3865     $element_state->{has_legend_at_first} = 1;
3866 wakaba 1.35 }
3867 wakaba 1.41 delete $element_state->{has_non_legend};
3868     } else {
3869 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3870 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
3871 wakaba 1.41 }
3872     },
3873     check_child_text => sub {
3874     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3875     if ($has_significant) {
3876     $element_state->{has_non_legend} = 1;
3877 wakaba 1.35 }
3878 wakaba 1.41 },
3879     check_end => sub {
3880     my ($self, $item, $element_state) = @_;
3881 wakaba 1.35
3882 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
3883     #
3884     } elsif ($element_state->{has_legend}) {
3885     if ($element_state->{has_non_legend}) {
3886     $self->{onerror}->(node => $element_state->{has_legend},
3887 wakaba 1.35 type => 'element not allowed:figure legend',
3888 wakaba 1.104 level => $self->{level}->{must});
3889 wakaba 1.35 }
3890     }
3891 wakaba 1.41
3892 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3893 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
3894 wakaba 1.35 },
3895     };
3896 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
3897 wakaba 1.1
3898 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
3899     my ($self, $attr) = @_;
3900 wakaba 1.104 $self->{onerror}->(node => $attr,
3901     type => 'unknown attribute',
3902     level => $self->{level}->{uncertain});
3903 wakaba 1.92 };
3904    
3905 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
3906 wakaba 1.40 %HTMLEmptyChecker,
3907 wakaba 1.98 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3908 wakaba 1.40 check_attrs => sub {
3909     my ($self, $item, $element_state) = @_;
3910 wakaba 1.1 $GetHTMLAttrsChecker->({
3911 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3912     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3913     }),
3914 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
3915 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3916 wakaba 1.1 src => $HTMLURIAttrChecker,
3917     usemap => $HTMLUsemapAttrChecker,
3918 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3919 wakaba 1.1 ismap => sub {
3920 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
3921     if (not $self->{flag}->{in_a_href}) {
3922 wakaba 1.15 $self->{onerror}->(node => $attr,
3923 wakaba 1.59 type => 'attribute not allowed:ismap',
3924 wakaba 1.104 level => $self->{level}->{must});
3925 wakaba 1.1 }
3926 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
3927 wakaba 1.1 },
3928 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
3929     ## TODO: HTML4 |name|
3930 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3931 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3932 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3933 wakaba 1.49 }, {
3934     %HTMLAttrStatus,
3935 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3936 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3937 wakaba 1.98 alt => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3938 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
3939 wakaba 1.98 height => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3940 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
3941 wakaba 1.98 ismap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3942 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3943 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3944     name => FEATURE_M12N10_REC_DEPRECATED,
3945 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3946 wakaba 1.98 src => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3947     usemap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3948 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
3949 wakaba 1.98 width => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3950 wakaba 1.66 })->($self, $item, $element_state);
3951 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
3952     $self->{onerror}->(node => $item->{node},
3953 wakaba 1.104 type => 'attribute missing',
3954     text => 'alt',
3955     level => $self->{level}->{should});
3956 wakaba 1.114 ## TODO: ...
3957 wakaba 1.1 }
3958 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3959     $self->{onerror}->(node => $item->{node},
3960 wakaba 1.104 type => 'attribute missing',
3961     text => 'src',
3962     level => $self->{level}->{must});
3963 wakaba 1.1 }
3964 wakaba 1.66
3965 wakaba 1.114 ## TODO: external resource check
3966    
3967 wakaba 1.66 $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3968     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
3969     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
3970     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
3971 wakaba 1.1 },
3972     };
3973    
3974     $Element->{$HTML_NS}->{iframe} = {
3975 wakaba 1.40 %HTMLTextChecker,
3976 wakaba 1.114 status => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3977 wakaba 1.49 ## NOTE: Not part of M12N10 Strict
3978 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3979 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3980 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3981 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
3982     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
3983     }),
3984     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
3985 wakaba 1.1 src => $HTMLURIAttrChecker,
3986 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3987 wakaba 1.49 }, {
3988     %HTMLAttrStatus,
3989     %HTMLM12NCommonAttrStatus,
3990     align => FEATURE_XHTML10_REC,
3991 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3992 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
3993 wakaba 1.92 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3994 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3995 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3996     marginheight => FEATURE_M12N10_REC,
3997     marginwidth => FEATURE_M12N10_REC,
3998 wakaba 1.114 #name => FEATURE_HTML5_WD | FEATURE_M12N10_REC_DEPRECATED,
3999     name => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4000     sandbox => FEATURE_HTML5_WD,
4001 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
4002 wakaba 1.114 seemless => FEATURE_HTML5_WD,
4003     src => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
4004 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4005 wakaba 1.92 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4006 wakaba 1.1 }),
4007 wakaba 1.66 check_start => sub {
4008     my ($self, $item, $element_state) = @_;
4009    
4010     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4011 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4012     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4013 wakaba 1.66 },
4014 wakaba 1.40 };
4015    
4016 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
4017 wakaba 1.40 %HTMLEmptyChecker,
4018 wakaba 1.98 status => FEATURE_HTML5_WD,
4019 wakaba 1.40 check_attrs => sub {
4020     my ($self, $item, $element_state) = @_;
4021 wakaba 1.1 my $has_src;
4022 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4023 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4024     $attr_ns = '' unless defined $attr_ns;
4025     my $attr_ln = $attr->manakai_local_name;
4026     my $checker;
4027 wakaba 1.73
4028     my $status = {
4029     %HTMLAttrStatus,
4030     height => FEATURE_HTML5_DEFAULT,
4031 wakaba 1.98 src => FEATURE_HTML5_WD,
4032     type => FEATURE_HTML5_WD,
4033 wakaba 1.73 width => FEATURE_HTML5_DEFAULT,
4034     }->{$attr_ln};
4035    
4036 wakaba 1.1 if ($attr_ns eq '') {
4037     if ($attr_ln eq 'src') {
4038     $checker = $HTMLURIAttrChecker;
4039     $has_src = 1;
4040     } elsif ($attr_ln eq 'type') {
4041     $checker = $HTMLIMTAttrChecker;
4042 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
4043     $checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet.
4044 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4045     $attr_ln !~ /[A-Z]/) {
4046 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4047     $status = $HTMLDatasetAttrStatus;
4048 wakaba 1.117 } elsif ($attr_ln !~ /^[Xx][Mm][Ll]/ and
4049 wakaba 1.118 $attr_ln !~ /[A-Z]/ and
4050 wakaba 1.117 $attr_ln =~ /\A\p{InXML_NCNameStartChar10}\p{InXMLNCNameChar10}*\z/) {
4051 wakaba 1.1 $checker = $HTMLAttrChecker->{$attr_ln}
4052     || sub { }; ## NOTE: Any local attribute is ok.
4053 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
4054 wakaba 1.117 } else {
4055     $checker = $HTMLAttrChecker->{$attr_ln};
4056 wakaba 1.1 }
4057     }
4058     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4059 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4060     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4061     || $AttrStatus->{$attr_ns}->{''};
4062     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4063 wakaba 1.62
4064 wakaba 1.1 if ($checker) {
4065 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
4066 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4067 wakaba 1.54 #
4068 wakaba 1.1 } else {
4069 wakaba 1.104 $self->{onerror}->(node => $attr,
4070     type => 'unknown attribute',
4071     level => $self->{level}->{uncertain});
4072 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
4073     }
4074    
4075 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4076 wakaba 1.1 }
4077    
4078     unless ($has_src) {
4079 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4080 wakaba 1.104 type => 'attribute missing',
4081     text => 'src',
4082 wakaba 1.114 level => $self->{level}->{info});
4083     ## NOTE: <embed> without src="" is allowed since revision 1929.
4084     ## We issues an informational message since <embed> w/o src=""
4085     ## is likely an authoring error.
4086 wakaba 1.1 }
4087 wakaba 1.114
4088     ## TODO: external resource check
4089 wakaba 1.66
4090     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4091 wakaba 1.1 },
4092     };
4093    
4094 wakaba 1.49 ## TODO:
4095     ## {applet} FEATURE_M12N10_REC_DEPRECATED
4096     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
4097    
4098 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
4099 wakaba 1.40 %HTMLTransparentChecker,
4100 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4101 wakaba 1.40 check_attrs => sub {
4102     my ($self, $item, $element_state) = @_;
4103 wakaba 1.1 $GetHTMLAttrsChecker->({
4104 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4105     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
4106     }),
4107     archive => $HTMLSpaceURIsAttrChecker,
4108     ## TODO: Relative to @codebase
4109     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4110     classid => $HTMLURIAttrChecker,
4111     codebase => $HTMLURIAttrChecker,
4112     codetype => $HTMLIMTAttrChecker,
4113     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
4114 wakaba 1.1 data => $HTMLURIAttrChecker,
4115 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
4116     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
4117     ## [HTML4] but we don't know how to test this.
4118     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4119 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
4120 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
4121 wakaba 1.1 type => $HTMLIMTAttrChecker,
4122     usemap => $HTMLUsemapAttrChecker,
4123 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4124 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4125 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4126 wakaba 1.49 }, {
4127     %HTMLAttrStatus,
4128 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4129 wakaba 1.49 align => FEATURE_XHTML10_REC,
4130 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4131 wakaba 1.49 border => FEATURE_XHTML10_REC,
4132     classid => FEATURE_M12N10_REC,
4133     codebase => FEATURE_M12N10_REC,
4134     codetype => FEATURE_M12N10_REC,
4135 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
4136 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4137 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4138     dataformatas => FEATURE_HTML4_REC_RESERVED,
4139     datasrc => FEATURE_HTML4_REC_RESERVED,
4140 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4141 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4142 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
4143 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4144 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4145 wakaba 1.49 standby => FEATURE_M12N10_REC,
4146 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4147     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4148     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4149 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
4150 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4151 wakaba 1.66 })->($self, $item, $element_state);
4152 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
4153     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
4154     $self->{onerror}->(node => $item->{node},
4155 wakaba 1.104 type => 'attribute missing:data|type',
4156     level => $self->{level}->{must});
4157 wakaba 1.1 }
4158     }
4159 wakaba 1.66
4160     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
4161     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
4162     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
4163     ## TODO: archive
4164     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4165 wakaba 1.1 },
4166 wakaba 1.72 ## NOTE: param*, transparent (Flow)
4167 wakaba 1.41 check_child_element => sub {
4168     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4169     $child_is_transparent, $element_state) = @_;
4170     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4171     $self->{onerror}->(node => $child_el,
4172     type => 'element not allowed:minus',
4173 wakaba 1.104 level => $self->{level}->{must});
4174 wakaba 1.41 $element_state->{has_non_legend} = 1;
4175     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4176     #
4177     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
4178     if ($element_state->{has_non_param}) {
4179 wakaba 1.104 $self->{onerror}->(node => $child_el,
4180 wakaba 1.72 type => 'element not allowed:flow',
4181 wakaba 1.104 level => $self->{level}->{must});
4182 wakaba 1.39 }
4183 wakaba 1.41 } else {
4184 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4185 wakaba 1.41 $element_state->{has_non_param} = 1;
4186 wakaba 1.39 }
4187 wakaba 1.25 },
4188 wakaba 1.41 check_child_text => sub {
4189     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4190     if ($has_significant) {
4191     $element_state->{has_non_param} = 1;
4192     }
4193 wakaba 1.42 },
4194     check_end => sub {
4195     my ($self, $item, $element_state) = @_;
4196     if ($element_state->{has_significant}) {
4197 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
4198 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
4199     ## NOTE: Transparent.
4200     } else {
4201     $self->{onerror}->(node => $item->{node},
4202 wakaba 1.104 level => $self->{level}->{should},
4203 wakaba 1.42 type => 'no significant content');
4204     }
4205     },
4206 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
4207 wakaba 1.1 };
4208 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4209     ## What about |<section><object data><style scoped></style>x</object></section>|?
4210     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4211 wakaba 1.1
4212     $Element->{$HTML_NS}->{param} = {
4213 wakaba 1.40 %HTMLEmptyChecker,
4214 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4215 wakaba 1.40 check_attrs => sub {
4216     my ($self, $item, $element_state) = @_;
4217 wakaba 1.1 $GetHTMLAttrsChecker->({
4218     name => sub { },
4219 wakaba 1.70 type => $HTMLIMTAttrChecker,
4220 wakaba 1.1 value => sub { },
4221 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4222     data => 1, ref => 1, object => 1,
4223     }),
4224 wakaba 1.49 }, {
4225     %HTMLAttrStatus,
4226 wakaba 1.121 href => FEATURE_RDFA_PR,
4227 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4228 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4229 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4230 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4231 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4232 wakaba 1.66 })->(@_);
4233 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4234     $self->{onerror}->(node => $item->{node},
4235 wakaba 1.104 type => 'attribute missing',
4236     text => 'name',
4237     level => $self->{level}->{must});
4238 wakaba 1.1 }
4239 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4240     $self->{onerror}->(node => $item->{node},
4241 wakaba 1.104 type => 'attribute missing',
4242     text => 'value',
4243     level => $self->{level}->{must});
4244 wakaba 1.1 }
4245     },
4246     };
4247    
4248     $Element->{$HTML_NS}->{video} = {
4249 wakaba 1.40 %HTMLTransparentChecker,
4250 wakaba 1.48 status => FEATURE_HTML5_LC,
4251 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4252 wakaba 1.1 src => $HTMLURIAttrChecker,
4253     ## TODO: start, loopstart, loopend, end
4254     ## ISSUE: they MUST be "value time offset"s. Value?
4255 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4256 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4257     controls => $GetHTMLBooleanAttrChecker->('controls'),
4258 wakaba 1.59 poster => $HTMLURIAttrChecker,
4259 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4260     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4261 wakaba 1.50 }, {
4262     %HTMLAttrStatus,
4263     autoplay => FEATURE_HTML5_LC,
4264     controls => FEATURE_HTML5_LC,
4265     end => FEATURE_HTML5_LC,
4266     height => FEATURE_HTML5_LC,
4267     loopend => FEATURE_HTML5_LC,
4268     loopstart => FEATURE_HTML5_LC,
4269     playcount => FEATURE_HTML5_LC,
4270     poster => FEATURE_HTML5_LC,
4271     src => FEATURE_HTML5_LC,
4272     start => FEATURE_HTML5_LC,
4273     width => FEATURE_HTML5_LC,
4274 wakaba 1.1 }),
4275 wakaba 1.42 check_start => sub {
4276     my ($self, $item, $element_state) = @_;
4277     $element_state->{allow_source}
4278     = not $item->{node}->has_attribute_ns (undef, 'src');
4279     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4280     ## NOTE: It might be set true by |check_element|.
4281 wakaba 1.66
4282     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4283     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4284 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4285     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4286 wakaba 1.42 },
4287     check_child_element => sub {
4288     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4289     $child_is_transparent, $element_state) = @_;
4290     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4291     $self->{onerror}->(node => $child_el,
4292     type => 'element not allowed:minus',
4293 wakaba 1.104 level => $self->{level}->{must});
4294 wakaba 1.42 delete $element_state->{allow_source};
4295     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4296     #
4297     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4298 wakaba 1.45 unless ($element_state->{allow_source}) {
4299 wakaba 1.104 $self->{onerror}->(node => $child_el,
4300 wakaba 1.72 type => 'element not allowed:flow',
4301 wakaba 1.104 level => $self->{level}->{must});
4302 wakaba 1.42 }
4303 wakaba 1.45 $element_state->{has_source} = 1;
4304 wakaba 1.1 } else {
4305 wakaba 1.42 delete $element_state->{allow_source};
4306 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4307 wakaba 1.42 }
4308     },
4309     check_child_text => sub {
4310     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4311     if ($has_significant) {
4312     delete $element_state->{allow_source};
4313     }
4314 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4315 wakaba 1.42 },
4316     check_end => sub {
4317     my ($self, $item, $element_state) = @_;
4318     if ($element_state->{has_source} == -1) {
4319     $self->{onerror}->(node => $item->{node},
4320 wakaba 1.104 type => 'child element missing',
4321     text => 'source',
4322     level => $self->{level}->{must});
4323 wakaba 1.1 }
4324 wakaba 1.42
4325     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4326 wakaba 1.1 },
4327     };
4328    
4329     $Element->{$HTML_NS}->{audio} = {
4330 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4331 wakaba 1.48 status => FEATURE_HTML5_LC,
4332 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4333     src => $HTMLURIAttrChecker,
4334     ## TODO: start, loopstart, loopend, end
4335     ## ISSUE: they MUST be "value time offset"s. Value?
4336     ## ISSUE: playcount has no conformance creteria
4337     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4338     controls => $GetHTMLBooleanAttrChecker->('controls'),
4339 wakaba 1.50 }, {
4340     %HTMLAttrStatus,
4341     autoplay => FEATURE_HTML5_LC,
4342     controls => FEATURE_HTML5_LC,
4343     end => FEATURE_HTML5_LC,
4344     loopend => FEATURE_HTML5_LC,
4345     loopstart => FEATURE_HTML5_LC,
4346     playcount => FEATURE_HTML5_LC,
4347     src => FEATURE_HTML5_LC,
4348     start => FEATURE_HTML5_LC,
4349 wakaba 1.42 }),
4350 wakaba 1.1 };
4351    
4352     $Element->{$HTML_NS}->{source} = {
4353 wakaba 1.40 %HTMLEmptyChecker,
4354 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
4355 wakaba 1.40 check_attrs => sub {
4356     my ($self, $item, $element_state) = @_;
4357 wakaba 1.1 $GetHTMLAttrsChecker->({
4358 wakaba 1.90 media => $HTMLMQAttrChecker,
4359     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4360     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4361 wakaba 1.1 type => $HTMLIMTAttrChecker,
4362 wakaba 1.50 }, {
4363     %HTMLAttrStatus,
4364     media => FEATURE_HTML5_DEFAULT,
4365 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
4366 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
4367     type => FEATURE_HTML5_DEFAULT,
4368 wakaba 1.66 })->(@_);
4369 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4370     $self->{onerror}->(node => $item->{node},
4371 wakaba 1.104 type => 'attribute missing',
4372     text => 'src',
4373     level => $self->{level}->{must});
4374 wakaba 1.1 }
4375 wakaba 1.66
4376     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4377 wakaba 1.1 },
4378     };
4379    
4380     $Element->{$HTML_NS}->{canvas} = {
4381 wakaba 1.40 %HTMLTransparentChecker,
4382 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4383 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4384 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4385     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4386 wakaba 1.50 }, {
4387     %HTMLAttrStatus,
4388 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4389     width => FEATURE_HTML5_COMPLETE,
4390 wakaba 1.1 }),
4391     };
4392    
4393     $Element->{$HTML_NS}->{map} = {
4394 wakaba 1.72 %HTMLFlowContentChecker,
4395 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4396 wakaba 1.40 check_attrs => sub {
4397     my ($self, $item, $element_state) = @_;
4398 wakaba 1.4 my $has_id;
4399 wakaba 1.100 my $has_name;
4400 wakaba 1.4 $GetHTMLAttrsChecker->({
4401     id => sub {
4402 wakaba 1.100 ## NOTE: Same as the global |id=""|, with |$self->{map}| registration.
4403 wakaba 1.4 my ($self, $attr) = @_;
4404     my $value = $attr->value;
4405 wakaba 1.100 if (length $value) {
4406 wakaba 1.4 if ($self->{id}->{$value}) {
4407 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
4408     level => $self->{level}->{must});
4409 wakaba 1.4 push @{$self->{id}->{$value}}, $attr;
4410     } else {
4411     $self->{id}->{$value} = [$attr];
4412     }
4413 wakaba 1.1 } else {
4414 wakaba 1.4 ## NOTE: MUST contain at least one character
4415 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
4416     level => $self->{level}->{must});
4417 wakaba 1.1 }
4418 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
4419 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
4420     level => $self->{level}->{must});
4421 wakaba 1.4 }
4422 wakaba 1.100 #$self->{map}->{$value} ||= $attr;
4423     $has_id = [$value, $attr];
4424     },
4425     name => sub {
4426     my ($self, $attr) = @_;
4427     my $value = $attr->value;
4428     if (length $value) {
4429     ## NOTE: Duplication is not non-conforming.
4430     ## NOTE: Space characters are not non-conforming.
4431     #
4432     } else {
4433     $self->{onerror}->(node => $attr,
4434     type => 'empty attribute value',
4435 wakaba 1.104 level => $self->{level}->{must});
4436 wakaba 1.100 }
4437 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4438 wakaba 1.100 $has_name = [$value, $attr];
4439 wakaba 1.4 },
4440 wakaba 1.49 }, {
4441     %HTMLAttrStatus,
4442 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4443     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4444     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4445     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4446 wakaba 1.100 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
4447     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4448 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4449     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4450     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4451     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4452     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4453     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4454     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4455     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4456     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4457     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4458     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4459 wakaba 1.66 })->(@_);
4460 wakaba 1.100
4461     if ($has_name and $has_id) {
4462     if ($has_name->[0] ne $has_id->[0]) {
4463     $self->{onerror}->(node => $has_id->[1],
4464 wakaba 1.104 type => 'id ne name',
4465     level => $self->{level}->{must});
4466 wakaba 1.100 }
4467     } elsif (not $has_name) {
4468     $self->{onerror}->(node => $item->{node},
4469 wakaba 1.104 type => 'attribute missing',
4470     text => 'name',
4471     level => $self->{level}->{must});
4472 wakaba 1.100 }
4473 wakaba 1.4 },
4474 wakaba 1.59 check_start => sub {
4475     my ($self, $item, $element_state) = @_;
4476     $element_state->{in_map_original} = $self->{flag}->{in_map};
4477     $self->{flag}->{in_map} = 1;
4478 wakaba 1.79
4479     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4480     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4481 wakaba 1.59 },
4482     check_end => sub {
4483     my ($self, $item, $element_state) = @_;
4484     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
4485 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4486 wakaba 1.59 },
4487 wakaba 1.1 };
4488    
4489     $Element->{$HTML_NS}->{area} = {
4490 wakaba 1.40 %HTMLEmptyChecker,
4491 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4492 wakaba 1.40 check_attrs => sub {
4493     my ($self, $item, $element_state) = @_;
4494 wakaba 1.1 my %attr;
4495     my $coords;
4496 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4497 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4498     $attr_ns = '' unless defined $attr_ns;
4499     my $attr_ln = $attr->manakai_local_name;
4500     my $checker;
4501 wakaba 1.73 my $status;
4502 wakaba 1.1 if ($attr_ns eq '') {
4503 wakaba 1.73 $status = {
4504     %HTMLAttrStatus,
4505     %HTMLM12NCommonAttrStatus,
4506     accesskey => FEATURE_M12N10_REC,
4507     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4508     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4509 wakaba 1.121 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR | FEATURE_M12N10_REC,
4510 wakaba 1.73 hreflang => FEATURE_HTML5_DEFAULT,
4511     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4512     media => FEATURE_HTML5_DEFAULT,
4513     nohref => FEATURE_M12N10_REC,
4514     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4515     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4516     ping => FEATURE_HTML5_DEFAULT,
4517 wakaba 1.120 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_CR,
4518 wakaba 1.73 shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4519     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4520     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4521     type => FEATURE_HTML5_DEFAULT,
4522     }->{$attr_ln};
4523    
4524 wakaba 1.1 $checker = {
4525 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4526 wakaba 1.1 alt => sub { },
4527     ## NOTE: |alt| value has no conformance creteria.
4528     shape => $GetHTMLEnumeratedAttrChecker->({
4529     circ => -1, circle => 1,
4530     default => 1,
4531     poly => 1, polygon => -1,
4532     rect => 1, rectangle => -1,
4533     }),
4534     coords => sub {
4535     my ($self, $attr) = @_;
4536     my $value = $attr->value;
4537     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4538     $coords = [split /,/, $value];
4539     } else {
4540     $self->{onerror}->(node => $attr,
4541 wakaba 1.104 type => 'coords:syntax error',
4542     level => $self->{level}->{must});
4543 wakaba 1.1 }
4544     },
4545 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4546     target => $HTMLTargetAttrChecker,
4547 wakaba 1.1 href => $HTMLURIAttrChecker,
4548     ping => $HTMLSpaceURIsAttrChecker,
4549 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4550 wakaba 1.1 media => $HTMLMQAttrChecker,
4551     hreflang => $HTMLLanguageTagAttrChecker,
4552     type => $HTMLIMTAttrChecker,
4553     }->{$attr_ln};
4554     if ($checker) {
4555     $attr{$attr_ln} = $attr;
4556 wakaba 1.122 } elsif ($attr_ln =~ /^data-\p{InXMLNCNameChar10}+\z/ and
4557     $attr_ln !~ /[A-Z]/) {
4558 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4559     $status = $HTMLDatasetAttrStatus;
4560 wakaba 1.1 } else {
4561     $checker = $HTMLAttrChecker->{$attr_ln};
4562     }
4563     }
4564     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4565 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4566     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4567     || $AttrStatus->{$attr_ns}->{''};
4568     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4569 wakaba 1.62
4570 wakaba 1.1 if ($checker) {
4571 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4572 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4573 wakaba 1.54 #
4574 wakaba 1.1 } else {
4575 wakaba 1.104 $self->{onerror}->(node => $attr,
4576     type => 'unknown attribute',
4577     level => $self->{level}->{uncertain});
4578 wakaba 1.1 ## ISSUE: No comformance createria for unknown attributes in the spec
4579     }
4580 wakaba 1.49
4581 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4582 wakaba 1.1 }
4583    
4584     if (defined $attr{href}) {
4585 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4586 wakaba 1.1 unless (defined $attr{alt}) {
4587 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4588 wakaba 1.104 type => 'attribute missing',
4589     text => 'alt',
4590     level => $self->{level}->{must});
4591 wakaba 1.1 }
4592     } else {
4593     for (qw/target ping rel media hreflang type alt/) {
4594     if (defined $attr{$_}) {
4595     $self->{onerror}->(node => $attr{$_},
4596 wakaba 1.104 type => 'attribute not allowed',
4597     level => $self->{level}->{must});
4598 wakaba 1.1 }
4599     }
4600     }
4601    
4602     my $shape = 'rectangle';
4603     if (defined $attr{shape}) {
4604     $shape = {
4605     circ => 'circle', circle => 'circle',
4606     default => 'default',
4607     poly => 'polygon', polygon => 'polygon',
4608     rect => 'rectangle', rectangle => 'rectangle',
4609     }->{lc $attr{shape}->value} || 'rectangle';
4610     ## TODO: ASCII lowercase?
4611     }
4612    
4613     if ($shape eq 'circle') {
4614     if (defined $attr{coords}) {
4615     if (defined $coords) {
4616     if (@$coords == 3) {
4617     if ($coords->[2] < 0) {
4618     $self->{onerror}->(node => $attr{coords},
4619 wakaba 1.104 type => 'coords:out of range',
4620     index => 2,
4621     value => $coords->[2],
4622     level => $self->{level}->{must});
4623 wakaba 1.1 }
4624     } else {
4625     $self->{onerror}->(node => $attr{coords},
4626 wakaba 1.104 type => 'coords:number not 3',
4627     text => 0+@$coords,
4628     level => $self->{level}->{must});
4629 wakaba 1.1 }
4630     } else {
4631     ## NOTE: A syntax error has been reported.
4632     }
4633     } else {
4634 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4635 wakaba 1.104 type => 'attribute missing',
4636     text => 'coords',
4637     level => $self->{level}->{must});
4638 wakaba 1.1 }
4639     } elsif ($shape eq 'default') {
4640     if (defined $attr{coords}) {
4641     $self->{onerror}->(node => $attr{coords},
4642 wakaba 1.104 type => 'attribute not allowed',
4643     level => $self->{level}->{must});
4644 wakaba 1.1 }
4645     } elsif ($shape eq 'polygon') {
4646     if (defined $attr{coords}) {
4647     if (defined $coords) {
4648     if (@$coords >= 6) {
4649     unless (@$coords % 2 == 0) {
4650     $self->{onerror}->(node => $attr{coords},
4651 wakaba 1.104 type => 'coords:number not even',
4652     text => 0+@$coords,
4653     level => $self->{level}->{must});
4654 wakaba 1.1 }
4655     } else {
4656     $self->{onerror}->(node => $attr{coords},
4657 wakaba 1.104 type => 'coords:number lt 6',
4658     text => 0+@$coords,
4659     level => $self->{level}->{must});
4660 wakaba 1.1 }
4661     } else {
4662     ## NOTE: A syntax error has been reported.
4663     }
4664     } else {
4665 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4666 wakaba 1.104 type => 'attribute missing',
4667     text => 'coords',
4668     level => $self->{level}->{must});
4669 wakaba 1.1 }
4670     } elsif ($shape eq 'rectangle') {
4671     if (defined $attr{coords}) {
4672     if (defined $coords) {
4673     if (@$coords == 4) {
4674     unless ($coords->[0] < $coords->[2]) {
4675     $self->{onerror}->(node => $attr{coords},
4676 wakaba 1.104 type => 'coords:out of range',
4677     index => 0,
4678     value => $coords->[0],
4679     level => $self->{level}->{must});
4680 wakaba 1.1 }
4681     unless ($coords->[1] < $coords->[3]) {
4682     $self->{onerror}->(node => $attr{coords},
4683 wakaba 1.104 type => 'coords:out of range',
4684     index => 1,
4685     value => $coords->[1],
4686     level => $self->{level}->{must});
4687 wakaba 1.1 }
4688     } else {
4689     $self->{onerror}->(node => $attr{coords},
4690 wakaba 1.104 type => 'coords:number not 4',
4691     text => 0+@$coords,
4692     level => $self->{level}->{must});
4693 wakaba 1.1 }
4694     } else {
4695     ## NOTE: A syntax error has been reported.
4696     }
4697     } else {
4698 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4699 wakaba 1.104 type => 'attribute missing',
4700     text => 'coords',
4701     level => $self->{level}->{must});
4702 wakaba 1.1 }
4703     }
4704 wakaba 1.66
4705     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4706 wakaba 1.1 },
4707 wakaba 1.59 check_start => sub {
4708     my ($self, $item, $element_state) = @_;
4709     unless ($self->{flag}->{in_map} or
4710     not $item->{node}->manakai_parent_element) {
4711     $self->{onerror}->(node => $item->{node},
4712     type => 'element not allowed:area',
4713 wakaba 1.104 level => $self->{level}->{must});
4714 wakaba 1.59 }
4715 wakaba 1.79
4716     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4717     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4718 wakaba 1.59 },
4719 wakaba 1.1 };
4720    
4721     $Element->{$HTML_NS}->{table} = {
4722 wakaba 1.40 %HTMLChecker,
4723 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4724 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4725 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4726     cellspacing => $HTMLLengthAttrChecker,
4727 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4728     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4729     lhs => 1, rhs => 1, box => 1, border => 1,
4730     }),
4731     rules => $GetHTMLEnumeratedAttrChecker->({
4732     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4733     }),
4734     summary => sub {}, ## NOTE: %Text; in HTML4.
4735     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4736     }, {
4737 wakaba 1.49 %HTMLAttrStatus,
4738 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4739 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4740     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4741     border => FEATURE_M12N10_REC,
4742     cellpadding => FEATURE_M12N10_REC,
4743     cellspacing => FEATURE_M12N10_REC,
4744 wakaba 1.61 cols => FEATURE_RFC1942,
4745 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4746     dataformatas => FEATURE_HTML4_REC_RESERVED,
4747     datapagesize => FEATURE_M12N10_REC,
4748     datasrc => FEATURE_HTML4_REC_RESERVED,
4749     frame => FEATURE_M12N10_REC,
4750 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4751 wakaba 1.49 rules => FEATURE_M12N10_REC,
4752     summary => FEATURE_M12N10_REC,
4753     width => FEATURE_M12N10_REC,
4754     }),
4755 wakaba 1.40 check_start => sub {
4756     my ($self, $item, $element_state) = @_;
4757     $element_state->{phase} = 'before caption';
4758 wakaba 1.66
4759     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4760 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4761     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4762 wakaba 1.40 },
4763     check_child_element => sub {
4764     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4765     $child_is_transparent, $element_state) = @_;
4766     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4767     $self->{onerror}->(node => $child_el,
4768     type => 'element not allowed:minus',
4769 wakaba 1.104 level => $self->{level}->{must});
4770 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4771     #
4772     } elsif ($element_state->{phase} eq 'in tbodys') {
4773     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4774     #$element_state->{phase} = 'in tbodys';
4775     } elsif (not $element_state->{has_tfoot} and
4776     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4777     $element_state->{phase} = 'after tfoot';
4778     $element_state->{has_tfoot} = 1;
4779     } else {
4780 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4781     level => $self->{level}->{must});
4782 wakaba 1.40 }
4783     } elsif ($element_state->{phase} eq 'in trs') {
4784     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4785     #$element_state->{phase} = 'in trs';
4786     } elsif (not $element_state->{has_tfoot} and
4787     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4788     $element_state->{phase} = 'after tfoot';
4789     $element_state->{has_tfoot} = 1;
4790     } else {
4791 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4792     level => $self->{level}->{must});
4793 wakaba 1.40 }
4794     } elsif ($element_state->{phase} eq 'after thead') {
4795     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4796     $element_state->{phase} = 'in tbodys';
4797     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4798     $element_state->{phase} = 'in trs';
4799     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4800     $element_state->{phase} = 'in tbodys';
4801     $element_state->{has_tfoot} = 1;
4802     } else {
4803 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4804     level => $self->{level}->{must});
4805 wakaba 1.40 }
4806     } elsif ($element_state->{phase} eq 'in colgroup') {
4807     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4808     $element_state->{phase} = 'in colgroup';
4809     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4810     $element_state->{phase} = 'after thead';
4811     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4812     $element_state->{phase} = 'in tbodys';
4813     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4814     $element_state->{phase} = 'in trs';
4815     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4816     $element_state->{phase} = 'in tbodys';
4817     $element_state->{has_tfoot} = 1;
4818     } else {
4819 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4820     level => $self->{level}->{must});
4821 wakaba 1.40 }
4822     } elsif ($element_state->{phase} eq 'before caption') {
4823     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4824     $element_state->{phase} = 'in colgroup';
4825     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4826     $element_state->{phase} = 'in colgroup';
4827     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4828     $element_state->{phase} = 'after thead';
4829     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4830     $element_state->{phase} = 'in tbodys';
4831     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4832     $element_state->{phase} = 'in trs';
4833     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4834     $element_state->{phase} = 'in tbodys';
4835     $element_state->{has_tfoot} = 1;
4836     } else {
4837 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4838     level => $self->{level}->{must});
4839 wakaba 1.40 }
4840     } elsif ($element_state->{phase} eq 'after tfoot') {
4841 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4842     level => $self->{level}->{must});
4843 wakaba 1.40 } else {
4844     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4845     }
4846     },
4847     check_child_text => sub {
4848     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4849     if ($has_significant) {
4850 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4851     level => $self->{level}->{must});
4852 wakaba 1.1 }
4853 wakaba 1.40 },
4854     check_end => sub {
4855     my ($self, $item, $element_state) = @_;
4856 wakaba 1.1
4857     ## Table model errors
4858     require Whatpm::HTMLTable;
4859 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
4860 wakaba 1.104 $self->{onerror}->(@_);
4861     }, $self->{level});
4862 wakaba 1.87 Whatpm::HTMLTable->assign_header
4863 wakaba 1.104 ($table, $self->{onerror}, $self->{level});
4864 wakaba 1.87 push @{$self->{return}->{table}}, $table;
4865 wakaba 1.1
4866 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4867 wakaba 1.1 },
4868     };
4869    
4870     $Element->{$HTML_NS}->{caption} = {
4871 wakaba 1.40 %HTMLPhrasingContentChecker,
4872 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4873 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4874     align => $GetHTMLEnumeratedAttrChecker->({
4875     top => 1, bottom => 1, left => 1, right => 1,
4876     }),
4877     }, {
4878 wakaba 1.49 %HTMLAttrStatus,
4879 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4880 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4881 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4882 wakaba 1.49 }),
4883 wakaba 1.1 };
4884    
4885 wakaba 1.69 my %cellalign = (
4886     ## HTML4 %cellhalign;
4887 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4888     left => 1, center => 1, right => 1, justify => 1, char => 1,
4889     }),
4890     char => sub {
4891     my ($self, $attr) = @_;
4892 wakaba 1.69
4893 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4894    
4895     my $value = $attr->value;
4896     if (length $value != 1) {
4897     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4898 wakaba 1.105 level => $self->{level}->{html4_fact});
4899 wakaba 1.70 }
4900     },
4901 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
4902    
4903 wakaba 1.69 ## HTML4 %cellvalign;
4904 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4905     top => 1, middle => 1, bottom => 1, baseline => 1,
4906     }),
4907 wakaba 1.69 );
4908    
4909 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4910 wakaba 1.40 %HTMLEmptyChecker,
4911 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4912 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4913 wakaba 1.69 %cellalign,
4914 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4915     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4916     ## TODO: "attribute not supported" if |col|.
4917     ## ISSUE: MUST NOT if any |col|?
4918     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4919 wakaba 1.49 }, {
4920     %HTMLAttrStatus,
4921 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4922 wakaba 1.49 align => FEATURE_M12N10_REC,
4923     char => FEATURE_M12N10_REC,
4924     charoff => FEATURE_M12N10_REC,
4925 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4926 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4927 wakaba 1.49 valign => FEATURE_M12N10_REC,
4928     width => FEATURE_M12N10_REC,
4929 wakaba 1.1 }),
4930 wakaba 1.40 check_child_element => sub {
4931     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4932     $child_is_transparent, $element_state) = @_;
4933     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4934     $self->{onerror}->(node => $child_el,
4935     type => 'element not allowed:minus',
4936 wakaba 1.104 level => $self->{level}->{must});
4937 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4938     #
4939     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4940     #
4941     } else {
4942 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
4943     level => $self->{level}->{must});
4944 wakaba 1.40 }
4945     },
4946     check_child_text => sub {
4947     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4948     if ($has_significant) {
4949 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
4950     level => $self->{level}->{must});
4951 wakaba 1.1 }
4952     },
4953     };
4954    
4955     $Element->{$HTML_NS}->{col} = {
4956 wakaba 1.40 %HTMLEmptyChecker,
4957 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4958 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4959 wakaba 1.69 %cellalign,
4960 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4961 wakaba 1.49 }, {
4962     %HTMLAttrStatus,
4963 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4964 wakaba 1.49 align => FEATURE_M12N10_REC,
4965     char => FEATURE_M12N10_REC,
4966     charoff => FEATURE_M12N10_REC,
4967 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4968 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4969 wakaba 1.49 valign => FEATURE_M12N10_REC,
4970     width => FEATURE_M12N10_REC,
4971 wakaba 1.1 }),
4972     };
4973    
4974     $Element->{$HTML_NS}->{tbody} = {
4975 wakaba 1.40 %HTMLChecker,
4976 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4977 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4978     %cellalign,
4979     }, {
4980 wakaba 1.49 %HTMLAttrStatus,
4981 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4982 wakaba 1.49 align => FEATURE_M12N10_REC,
4983     char => FEATURE_M12N10_REC,
4984     charoff => FEATURE_M12N10_REC,
4985 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4986 wakaba 1.49 valign => FEATURE_M12N10_REC,
4987     }),
4988 wakaba 1.40 check_child_element => sub {
4989     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4990     $child_is_transparent, $element_state) = @_;
4991     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4992     $self->{onerror}->(node => $child_el,
4993     type => 'element not allowed:minus',
4994 wakaba 1.104 level => $self->{level}->{must});
4995 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4996     #
4997     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4998 wakaba 1.84 #
4999 wakaba 1.40 } else {
5000 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5001     level => $self->{level}->{must});
5002 wakaba 1.40 }
5003     },
5004     check_child_text => sub {
5005     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5006     if ($has_significant) {
5007 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5008     level => $self->{level}->{must});
5009 wakaba 1.1 }
5010 wakaba 1.40 },
5011 wakaba 1.1 };
5012    
5013     $Element->{$HTML_NS}->{thead} = {
5014 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5015 wakaba 1.1 };
5016    
5017     $Element->{$HTML_NS}->{tfoot} = {
5018 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
5019 wakaba 1.1 };
5020    
5021     $Element->{$HTML_NS}->{tr} = {
5022 wakaba 1.40 %HTMLChecker,
5023 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5024 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
5025     %cellalign,
5026     bgcolor => $HTMLColorAttrChecker,
5027     }, {
5028 wakaba 1.49 %HTMLAttrStatus,
5029 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5030 wakaba 1.49 align => FEATURE_M12N10_REC,
5031     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5032     char => FEATURE_M12N10_REC,
5033     charoff => FEATURE_M12N10_REC,
5034 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5035 wakaba 1.49 valign => FEATURE_M12N10_REC,
5036     }),
5037 wakaba 1.40 check_child_element => sub {
5038     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5039     $child_is_transparent, $element_state) = @_;
5040     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5041     $self->{onerror}->(node => $child_el,
5042     type => 'element not allowed:minus',
5043 wakaba 1.104 level => $self->{level}->{must});
5044 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5045     #
5046     } elsif ($child_nsuri eq $HTML_NS and
5047     ($child_ln eq 'td' or $child_ln eq 'th')) {
5048 wakaba 1.84 #
5049 wakaba 1.40 } else {
5050 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
5051     level => $self->{level}->{must});
5052 wakaba 1.40 }
5053     },
5054     check_child_text => sub {
5055     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5056     if ($has_significant) {
5057 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
5058     level => $self->{level}->{must});
5059 wakaba 1.1 }
5060     },
5061     };
5062    
5063     $Element->{$HTML_NS}->{td} = {
5064 wakaba 1.72 %HTMLFlowContentChecker,
5065 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5066 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5067 wakaba 1.69 %cellalign,
5068     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5069     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5070     bgcolor => $HTMLColorAttrChecker,
5071 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5072 wakaba 1.87 headers => sub {
5073     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
5074     ## Though that method does not check the |headers| attribute of a
5075     ## |td| element if the element does not form a table, in that case
5076     ## the |td| element is non-conforming anyway.
5077     },
5078 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5079 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5080 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
5081     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5082 wakaba 1.49 }, {
5083     %HTMLAttrStatus,
5084 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5085     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5086 wakaba 1.49 align => FEATURE_M12N10_REC,
5087 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5088 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5089     char => FEATURE_M12N10_REC,
5090     charoff => FEATURE_M12N10_REC,
5091 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5092 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5093 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5094 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5095 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5096 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5097     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5098 wakaba 1.49 valign => FEATURE_M12N10_REC,
5099     width => FEATURE_M12N10_REC_DEPRECATED,
5100 wakaba 1.1 }),
5101     };
5102    
5103     $Element->{$HTML_NS}->{th} = {
5104 wakaba 1.40 %HTMLPhrasingContentChecker,
5105 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5106 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5107 wakaba 1.69 %cellalign,
5108     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
5109     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
5110     bgcolor => $HTMLColorAttrChecker,
5111 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5112 wakaba 1.87 ## TODO: HTML4(?) |headers|
5113 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
5114 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
5115     scope => $GetHTMLEnumeratedAttrChecker
5116     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
5117 wakaba 1.49 }, {
5118     %HTMLAttrStatus,
5119 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5120     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5121 wakaba 1.49 align => FEATURE_M12N10_REC,
5122 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5123 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
5124     char => FEATURE_M12N10_REC,
5125     charoff => FEATURE_M12N10_REC,
5126 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5127     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5128 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
5129 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5130 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
5131 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5132     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5133 wakaba 1.49 valign => FEATURE_M12N10_REC,
5134     width => FEATURE_M12N10_REC_DEPRECATED,
5135 wakaba 1.1 }),
5136     };
5137    
5138 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
5139 wakaba 1.121 %HTMLFlowContentChecker,
5140 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5141 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5142 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5143 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
5144     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
5145 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5146     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
5147     method => $GetHTMLEnumeratedAttrChecker->({
5148     get => 1, post => 1, put => 1, delete => 1,
5149     }),
5150 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
5151     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
5152     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
5153 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
5154     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5155 wakaba 1.52 target => $HTMLTargetAttrChecker,
5156 wakaba 1.121 ## TODO: Warn for combination whose behavior is not defined in HTML4/WF2.
5157 wakaba 1.52 }, {
5158     %HTMLAttrStatus,
5159     %HTMLM12NCommonAttrStatus,
5160 wakaba 1.119 accept => FEATURE_WF2X | FEATURE_M12N10_REC,
5161     'accept-charset' => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5162     action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5163 wakaba 1.56 data => FEATURE_WF2,
5164 wakaba 1.119 enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5165 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5166 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5167     #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
5168     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5169 wakaba 1.56 onreceived => FEATURE_WF2,
5170 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5171     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5172 wakaba 1.56 replace => FEATURE_WF2,
5173 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5174     sdasuff => FEATURE_HTML20_RFC,
5175 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5176 wakaba 1.52 }),
5177 wakaba 1.66 check_start => sub {
5178     my ($self, $item, $element_state) = @_;
5179 wakaba 1.121 $self->_add_minus_elements ($element_state, {$HTML_NS => {form => 1}});
5180 wakaba 1.66
5181     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5182     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5183 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5184     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5185 wakaba 1.66 },
5186 wakaba 1.121 check_end => sub {
5187     my ($self, $item, $element_state) = @_;
5188     $self->_remove_minus_elements ($element_state);
5189    
5190     $HTMLFlowContentChecker{check_end}->(@_);
5191     },
5192 wakaba 1.52 };
5193    
5194     $Element->{$HTML_NS}->{fieldset} = {
5195 wakaba 1.119 %HTMLFlowContentChecker, ## ISSUE: No |legend| in the spec
5196     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5197 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5198     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5199     ## TODO: form [WF2]
5200 wakaba 1.125 ## TODO: name [HTML5]
5201 wakaba 1.56 }, {
5202 wakaba 1.52 %HTMLAttrStatus,
5203     %HTMLM12NCommonAttrStatus,
5204 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5205     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5206 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5207 wakaba 1.125 name => FEATURE_HTML5_DEFAULT,
5208 wakaba 1.52 }),
5209     ## TODO: Tests
5210     ## TODO: Tests for <nest/> in <fieldset>
5211     };
5212    
5213     $Element->{$HTML_NS}->{input} = {
5214 wakaba 1.119 %HTMLEmptyChecker,
5215     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5216 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5217 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
5218 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5219 wakaba 1.56 action => $HTMLURIAttrChecker,
5220 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
5221     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
5222     }),
5223     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
5224     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
5225     ## here.
5226 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
5227     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5228 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
5229     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5230 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
5231     ## TODO: form [WF2]
5232     ## TODO: inputmode [WF2]
5233 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
5234 wakaba 1.56 ## TODO: list [WF2]
5235     ## TODO: max [WF2]
5236 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5237 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
5238     get => 1, post => 1, put => 1, delete => 1,
5239     }),
5240     ## TODO: min [WF2]
5241 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5242 wakaba 1.119 ## TODO: pattern
5243 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5244 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5245     required => $GetHTMLBooleanAttrChecker->('required'),
5246 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5247     src => $HTMLURIAttrChecker,
5248 wakaba 1.56 ## TODO: step [WF2]
5249     target => $HTMLTargetAttrChecker,
5250 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
5251     ## attribute to support the |add| button type (as part of repetition
5252     ## template feature). It conflicts with the |template| global attribute
5253     ## introduced as part of the data template feature.
5254     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5255     ## author requirement.
5256 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5257     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
5258     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
5259 wakaba 1.56 ## [WF2]
5260     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
5261     time => 1, number => 1, range => 1, email => 1, url => 1,
5262     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5263 wakaba 1.52 }),
5264     usemap => $HTMLUsemapAttrChecker,
5265 wakaba 1.56 value => sub {}, ## NOTE: CDATA [M12N] ## TODO: "optional except when the type attribute has the value "radio" or "checkbox"" [HTML4] ## TODO: constraints [WF2]
5266     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
5267 wakaba 1.52 }, {
5268     %HTMLAttrStatus,
5269     %HTMLM12NCommonAttrStatus,
5270 wakaba 1.119 accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5271 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5272 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5273 wakaba 1.119 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5274 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
5275 wakaba 1.119 alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5276     autocomplete => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5277     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5278     checked => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5279 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5280     dataformatas => FEATURE_HTML4_REC_RESERVED,
5281     datasrc => FEATURE_HTML4_REC_RESERVED,
5282 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5283     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5284     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5285     inputmode => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR,
5286 wakaba 1.52 ismap => FEATURE_M12N10_REC,
5287     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5288 wakaba 1.119 list => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5289     max => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5290     maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5291     method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5292     min => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5293     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5294 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5295     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5296     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5297     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5298 wakaba 1.119 pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5299     readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5300 wakaba 1.65 replace => FEATURE_WF2,
5301 wakaba 1.119 required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5302 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5303 wakaba 1.119 size => FEATURE_HTML5_DEFAULT | FEATURE_WF2X_DEPRECATED | FEATURE_M12N10_REC,
5304     src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5305     step => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5306 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5307 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5308 wakaba 1.56 template => FEATURE_WF2,
5309 wakaba 1.119 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5310 wakaba 1.52 usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5311 wakaba 1.119 value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5312 wakaba 1.52 }),
5313     ## TODO: Tests
5314     ## TODO: Tests for <nest/> in <input>
5315 wakaba 1.66 check_start => sub {
5316     my ($self, $item, $element_state) = @_;
5317    
5318     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5319     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5320     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
5321 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5322     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5323 wakaba 1.66 },
5324 wakaba 1.52 };
5325    
5326 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
5327    
5328 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
5329     ## [repetition-block-related] buttons carefully to make clear which block a
5330     ## button applies to.": I have no idea how this can be tested.
5331    
5332 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
5333 wakaba 1.119 %HTMLPhrasingContentChecker, ## ISSUE: -interactive?
5334     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5335 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5336 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5337 wakaba 1.56 action => $HTMLURIAttrChecker,
5338     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5339 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5340 wakaba 1.56 ## TODO: form [WF2]
5341     method => $GetHTMLEnumeratedAttrChecker->({
5342     get => 1, post => 1, put => 1, delete => 1,
5343     }),
5344 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5345 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5346     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5347     target => $HTMLTargetAttrChecker,
5348 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
5349     ## attribute to support the |add| button type (as part of repetition
5350     ## template feature). It conflicts with the |template| global attribute
5351     ## introduced as part of the data template feature.
5352     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5353     ## author requirement.
5354 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5355     button => 1, submit => 1, reset => 1,
5356 wakaba 1.80 ## [WF2]
5357     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5358 wakaba 1.52 }),
5359     value => sub {}, ## NOTE: CDATA [M12N]
5360     }, {
5361     %HTMLAttrStatus,
5362     %HTMLM12NCommonAttrStatus,
5363     accesskey => FEATURE_M12N10_REC,
5364 wakaba 1.119 action => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5365     autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5366 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5367     dataformatas => FEATURE_HTML4_REC_RESERVED,
5368     datasrc => FEATURE_HTML4_REC_RESERVED,
5369 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5370     enctype => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5371     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5372 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5373 wakaba 1.119 method => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5374     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5375 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5376     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5377 wakaba 1.56 oninvalid => FEATURE_WF2,
5378     replace => FEATURE_WF2,
5379 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5380 wakaba 1.119 target => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5381 wakaba 1.56 template => FEATURE_WF2,
5382 wakaba 1.119 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5383     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5384 wakaba 1.52 }),
5385     ## TODO: Tests
5386     ## TODO: Tests for <nest/> in <button>
5387 wakaba 1.66 check_start => sub {
5388     my ($self, $item, $element_state) = @_;
5389    
5390     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5391     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5392 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5393     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5394 wakaba 1.66 },
5395 wakaba 1.52 };
5396    
5397     $Element->{$HTML_NS}->{label} = {
5398 wakaba 1.119 %HTMLPhrasingContentChecker, ## ISSUE: -label? -interactive? At most one form control [WF2]?
5399     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC
5400     | FEATURE_XHTML2_ED,
5401 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5402 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5403 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
5404 wakaba 1.119 ## TODO: form
5405 wakaba 1.52 }, {
5406     %HTMLAttrStatus,
5407 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5408 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
5409 wakaba 1.119 for => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5410     form => FEATURE_HTML5_DEFAULT,
5411 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5412     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5413     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5414     }),
5415     ## TODO: Tests
5416     ## TODO: Tests for <nest/> in <label>
5417     };
5418    
5419     $Element->{$HTML_NS}->{select} = {
5420 wakaba 1.121 %HTMLChecker,
5421 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
5422     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
5423 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5424 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
5425 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5426 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5427 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5428 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5429 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5430     ## TODO: form [WF2]
5431 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5432     name => sub {}, ## NOTE: CDATA [M12N]
5433 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5434 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5435     }, {
5436     %HTMLAttrStatus,
5437     %HTMLM12NCommonAttrStatus,
5438 wakaba 1.56 accesskey => FEATURE_WF2,
5439 wakaba 1.119 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5440 wakaba 1.56 data => FEATURE_WF2,
5441 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5442     dataformatas => FEATURE_HTML4_REC_RESERVED,
5443     datasrc => FEATURE_HTML4_REC_RESERVED,
5444 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5445     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5446 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5447 wakaba 1.119 multiple => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5448     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5449 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5450     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5451     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5452 wakaba 1.56 oninvalid => FEATURE_WF2,
5453 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5454     sdapref => FEATURE_HTML20_RFC,
5455 wakaba 1.119 size => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5456 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5457     }),
5458 wakaba 1.66 check_start => sub {
5459     my ($self, $item, $element_state) = @_;
5460    
5461     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5462     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5463 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5464     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5465 wakaba 1.66 },
5466 wakaba 1.121 check_child_element => sub {
5467     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5468     $child_is_transparent, $element_state) = @_;
5469     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5470     $self->{onerror}->(node => $child_el,
5471     type => 'element not allowed:minus',
5472     level => $self->{level}->{must});
5473     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5474     #
5475     } elsif ($child_nsuri eq $HTML_NS and
5476     {
5477     option => 1, optgroup => 1,
5478     }->{$child_ln}) {
5479     #
5480     } else {
5481     $self->{onerror}->(node => $child_el, type => 'element not allowed',
5482     level => $self->{level}->{must});
5483     }
5484     },
5485     check_child_text => sub {
5486     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5487     if ($has_significant) {
5488     $self->{onerror}->(node => $child_node, type => 'character not allowed',
5489     level => $self->{level}->{must});
5490     }
5491     },
5492 wakaba 1.52 };
5493 wakaba 1.1
5494 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
5495 wakaba 1.121 %HTMLPhrasingContentChecker,
5496 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
5497 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5498 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5499     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5500     }, {
5501 wakaba 1.52 %HTMLAttrStatus,
5502 wakaba 1.56 data => FEATURE_WF2,
5503 wakaba 1.52 }),
5504 wakaba 1.66 check_start => sub {
5505     my ($self, $item, $element_state) = @_;
5506    
5507 wakaba 1.121 $element_state->{phase} = 'any'; # any | phrasing | option
5508    
5509 wakaba 1.66 $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5510 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5511     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5512 wakaba 1.66 },
5513 wakaba 1.121 ## NOTE: phrasing | option*
5514     check_child_element => sub {
5515     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5516     $child_is_transparent, $element_state) = @_;
5517     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5518     $self->{onerror}->(node => $child_el,
5519     type => 'element not allowed:minus',
5520     level => $self->{level}->{must});
5521     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5522     #
5523     } elsif ($element_state->{phase} eq 'phrasing') {
5524     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
5525     #
5526     } else {
5527     $self->{onerror}->(node => $child_el,
5528     type => 'element not allowed:phrasing',
5529     level => $self->{level}->{must});
5530     }
5531     } elsif ($element_state->{phase} eq 'option') {
5532     if ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
5533     #
5534     } else {
5535     $self->{onerror}->(node => $child_el,
5536     type => 'element not allowed',
5537     level => $self->{level}->{must});
5538     }
5539     } elsif ($element_state->{phase} eq 'any') {
5540     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
5541     $element_state->{phase} = 'phrasing';
5542     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
5543     $element_state->{phase} = 'option';
5544     } else {
5545     $self->{onerror}->(node => $child_el,
5546     type => 'element not allowed',
5547     level => $self->{level}->{must});
5548     }
5549     } else {
5550     die "check_child_element: Bad |datalist| phase: $element_state->{phase}";
5551     }
5552     },
5553     check_child_text => sub {
5554     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5555     if ($has_significant) {
5556     if ($element_state->{phase} eq 'phrasing') {
5557     #
5558     } elsif ($element_state->{phase} eq 'any') {
5559     $element_state->{phase} = 'phrasing';
5560     } else {
5561     $self->{onerror}->(node => $child_node,
5562     type => 'character not allowed',
5563     level => $self->{level}->{must});
5564     }
5565     }
5566     },
5567     check_end => sub {
5568     my ($self, $item, $element_state) = @_;
5569     if ($element_state->{phase} eq 'phrasing') {
5570     if ($element_state->{has_significant}) {
5571     $item->{real_parent_state}->{has_significant} = 1;
5572     } elsif ($item->{transparent}) {
5573     #
5574     } else {
5575     $self->{onerror}->(node => $item->{node},
5576     type => 'no significant content',
5577     level => $self->{level}->{should});
5578     }
5579     } else {
5580     ## NOTE: Since the content model explicitly allows a |datalist| element
5581     ## being empty, we don't raise "no significant content" error for this
5582     ## element when there is no element. (We should raise an error for
5583     ## |<datalist><br></datalist>|, however.)
5584     ## NOTE: As a side-effect, when the |datalist| element only contains
5585     ## non-conforming content, then the |phase| flag has not changed from
5586     ## |any|, no "no significant content" error is raised neither.
5587     $HTMLChecker{check_end}->(@_);
5588     }
5589     },
5590 wakaba 1.52 };
5591 wakaba 1.49
5592 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
5593 wakaba 1.121 %HTMLChecker,
5594 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5595 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5596     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5597     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
5598     }, {
5599     %HTMLAttrStatus,
5600     %HTMLM12NCommonAttrStatus,
5601 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5602     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5603 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5604     }),
5605 wakaba 1.121 check_child_element => sub {
5606     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5607     $child_is_transparent, $element_state) = @_;
5608     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5609     $self->{onerror}->(node => $child_el,
5610     type => 'element not allowed:minus',
5611     level => $self->{level}->{must});
5612     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5613     #
5614     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'option') {
5615     #
5616     } else {
5617     $self->{onerror}->(node => $child_el, type => 'element not allowed',
5618     level => $self->{level}->{must});
5619     }
5620     },
5621     check_child_text => sub {
5622     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5623     if ($has_significant) {
5624     $self->{onerror}->(node => $child_node, type => 'character not allowed',
5625     level => $self->{level}->{must});
5626     }
5627     },
5628 wakaba 1.52 };
5629    
5630     $Element->{$HTML_NS}->{option} = {
5631     %HTMLTextChecker,
5632 wakaba 1.119 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5633 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5634     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5635     label => sub {}, ## NOTE: Text [M12N]
5636     selected => $GetHTMLBooleanAttrChecker->('selected'),
5637     value => sub {}, ## NOTE: CDATA [M12N]
5638     }, {
5639     %HTMLAttrStatus,
5640     %HTMLM12NCommonAttrStatus,
5641 wakaba 1.119 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5642     label => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5643 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5644 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5645     sdapref => FEATURE_HTML20_RFC,
5646 wakaba 1.119 selected => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5647     value => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5648 wakaba 1.52 }),
5649     };
5650 wakaba 1.49
5651 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
5652     %HTMLTextChecker,
5653 wakaba 1.121 status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5654 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5655 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
5656 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5657 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5658     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
5659 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5660 wakaba 1.56 ## TODO: form [WF2]
5661     ## TODO: inputmode [WF2]
5662     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5663 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5664 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
5665 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5666 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
5667     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5668     oninvalid => $HTMLEventHandlerAttrChecker,
5669     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
5670 wakaba 1.52 }, {
5671     %HTMLAttrStatus,
5672     %HTMLM12NCommonAttrStatus,
5673 wakaba 1.121 accept => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5674 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5675 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5676 wakaba 1.121 autofocus => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5677     cols => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5678 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5679 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
5680     datasrc => FEATURE_HTML4_REC_RESERVED,
5681 wakaba 1.121 disabled => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5682     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5683     inputmode => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_XHTMLBASIC11_CR,
5684 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5685 wakaba 1.121 maxlength => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5686     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5687 wakaba 1.52 onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5688     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5689     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5690 wakaba 1.56 oninvalid => FEATURE_WF2,
5691 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5692 wakaba 1.121 pattern => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5693     readonly => FEATURE_HTML5_DEFAULT | FEATURE_WF2X | FEATURE_M12N10_REC,
5694     required => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5695     rows => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5696 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5697     sdapref => FEATURE_HTML20_RFC,
5698 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5699 wakaba 1.121 wrap => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5700 wakaba 1.52 }),
5701 wakaba 1.66 check_start => sub {
5702     my ($self, $item, $element_state) = @_;
5703    
5704     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5705 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5706     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5707 wakaba 1.66 },
5708 wakaba 1.52 };
5709 wakaba 1.49
5710 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
5711 wakaba 1.121 %HTMLPhrasingContentChecker,
5712     status => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5713 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5714     ## TODO: for [WF2]
5715     ## TODO: form [WF2]
5716     ## TODO: name [WF2]
5717     ## onformchange[WF2]
5718     ## onforminput[WF2]
5719     }, {
5720 wakaba 1.52 %HTMLAttrStatus,
5721 wakaba 1.121 for => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5722     form => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5723     name => FEATURE_HTML5_DEFAULT | FEATURE_WF2X,
5724 wakaba 1.56 onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
5725     onformchange => FEATURE_WF2,
5726     onforminput => FEATURE_WF2,
5727 wakaba 1.52 }),
5728 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
5729 wakaba 1.52 };
5730    
5731     $Element->{$HTML_NS}->{isindex} = {
5732     %HTMLEmptyChecker,
5733 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
5734     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
5735 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5736     prompt => sub {}, ## NOTE: Text [M12N]
5737     }, {
5738     %HTMLAttrStatus,
5739     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5740     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5741     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5742     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5743     prompt => FEATURE_M12N10_REC_DEPRECATED,
5744 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5745 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5746 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5747     }),
5748     ## TODO: Tests
5749     ## TODO: Tests for <nest/> in <isindex>
5750 wakaba 1.66 check_start => sub {
5751     my ($self, $item, $element_state) = @_;
5752    
5753     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5754 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5755     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5756 wakaba 1.66 },
5757 wakaba 1.52 };
5758 wakaba 1.49
5759 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
5760 wakaba 1.40 %HTMLChecker,
5761 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5762 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5763 wakaba 1.91 charset => sub {
5764     my ($self, $attr) = @_;
5765    
5766     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
5767     $self->{onerror}->(type => 'attribute not allowed',
5768     node => $attr,
5769 wakaba 1.104 level => $self->{level}->{must});
5770 wakaba 1.91 }
5771    
5772     $HTMLCharsetChecker->($attr->value, @_);
5773     },
5774 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
5775 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
5776 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
5777     async => $GetHTMLBooleanAttrChecker->('async'),
5778 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
5779 wakaba 1.49 }, {
5780     %HTMLAttrStatus,
5781 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
5782 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5783 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5784 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
5785     for => FEATURE_HTML4_REC_RESERVED,
5786 wakaba 1.121 href => FEATURE_RDFA_PR,
5787 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5788 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
5789 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5790     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5791 wakaba 1.9 }),
5792 wakaba 1.40 check_start => sub {
5793     my ($self, $item, $element_state) = @_;
5794 wakaba 1.1
5795 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
5796     $element_state->{must_be_empty} = 1;
5797 wakaba 1.1 } else {
5798     ## NOTE: No content model conformance in HTML5 spec.
5799 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
5800     my $language = $item->{node}->get_attribute_ns (undef, 'language');
5801 wakaba 1.1 if ((defined $type and $type eq '') or
5802     (defined $language and $language eq '')) {
5803     $type = 'text/javascript';
5804     } elsif (defined $type) {
5805     #
5806     } elsif (defined $language) {
5807     $type = 'text/' . $language;
5808     } else {
5809     $type = 'text/javascript';
5810     }
5811 wakaba 1.93
5812     if ($type =~ m[\A(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*/(?>(?>\x0D\x0A)?[\x09\x20])*([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)(?>(?>\x0D\x0A)?[\x09\x20])*(?>;|\z)]) {
5813     $type = "$1/$2";
5814     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
5815     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
5816     }
5817     $element_state->{script_type} = $type;
5818 wakaba 1.40 }
5819 wakaba 1.66
5820     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5821 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5822     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5823 wakaba 1.107
5824     $element_state->{text} = '';
5825 wakaba 1.40 },
5826     check_child_element => sub {
5827     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5828     $child_is_transparent, $element_state) = @_;
5829     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5830     $self->{onerror}->(node => $child_el,
5831     type => 'element not allowed:minus',
5832 wakaba 1.104 level => $self->{level}->{must});
5833 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5834     #
5835     } else {
5836     if ($element_state->{must_be_empty}) {
5837     $self->{onerror}->(node => $child_el,
5838 wakaba 1.104 type => 'element not allowed:empty',
5839     level => $self->{level}->{must});
5840 wakaba 1.40 }
5841     }
5842     },
5843     check_child_text => sub {
5844     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5845     if ($has_significant and
5846     $element_state->{must_be_empty}) {
5847     $self->{onerror}->(node => $child_node,
5848 wakaba 1.104 type => 'character not allowed:empty',
5849     level => $self->{level}->{must});
5850 wakaba 1.40 }
5851 wakaba 1.115 $element_state->{text} .= $child_node->data;
5852 wakaba 1.40 },
5853     check_end => sub {
5854     my ($self, $item, $element_state) = @_;
5855     unless ($element_state->{must_be_empty}) {
5856 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
5857     ## NOTE: XML content should be checked by THIS instance of checker
5858     ## as part of normal tree validation.
5859 wakaba 1.104 $self->{onerror}->(node => $item->{node},
5860     type => 'XML script lang',
5861     text => $element_state->{script_type},
5862     level => $self->{level}->{uncertain});
5863     ## ISSUE: Should we raise some kind of error for
5864     ## <script type="text/xml">aaaaa</script>?
5865     ## NOTE: ^^^ This is why we throw an "uncertain" error.
5866 wakaba 1.93 } else {
5867     $self->{onsubdoc}->({s => $element_state->{text},
5868     container_node => $item->{node},
5869     media_type => $element_state->{script_type},
5870     is_char_string => 1});
5871     }
5872 wakaba 1.40
5873     $HTMLChecker{check_end}->(@_);
5874 wakaba 1.1 }
5875     },
5876 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
5877     ## NOTE: "When used to include script data, the script data must be embedded
5878     ## inline, the format of the data must be given using the type attribute,
5879     ## and the src attribute must not be specified." - not testable.
5880     ## TODO: It would be possible to err <script type=text/plain src=...>
5881 wakaba 1.1 };
5882 wakaba 1.25 ## ISSUE: Significant check and text child node
5883 wakaba 1.1
5884     ## NOTE: When script is disabled.
5885     $Element->{$HTML_NS}->{noscript} = {
5886 wakaba 1.40 %HTMLTransparentChecker,
5887 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5888     check_attrs => $GetHTMLAttrsChecker->({}, {
5889     %HTMLAttrStatus,
5890     %HTMLM12NCommonAttrStatus,
5891 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5892 wakaba 1.49 }),
5893 wakaba 1.40 check_start => sub {
5894     my ($self, $item, $element_state) = @_;
5895 wakaba 1.3
5896 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
5897 wakaba 1.104 $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript',
5898     level => $self->{level}->{must});
5899 wakaba 1.3 }
5900    
5901 wakaba 1.40 unless ($self->{flag}->{in_head}) {
5902     $self->_add_minus_elements ($element_state,
5903     {$HTML_NS => {noscript => 1}});
5904     }
5905 wakaba 1.79
5906     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5907     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5908 wakaba 1.3 },
5909 wakaba 1.40 check_child_element => sub {
5910     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5911     $child_is_transparent, $element_state) = @_;
5912     if ($self->{flag}->{in_head}) {
5913     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5914     $self->{onerror}->(node => $child_el,
5915     type => 'element not allowed:minus',
5916 wakaba 1.104 level => $self->{level}->{must});
5917 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5918     #
5919     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
5920     #
5921     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
5922     if ($child_el->has_attribute_ns (undef, 'scoped')) {
5923     $self->{onerror}->(node => $child_el,
5924     type => 'element not allowed:head noscript',
5925 wakaba 1.104 level => $self->{level}->{must});
5926 wakaba 1.40 }
5927     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
5928 wakaba 1.47 my $http_equiv_attr
5929     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
5930     if ($http_equiv_attr) {
5931     ## TODO: case
5932     if (lc $http_equiv_attr->value eq 'content-type') {
5933 wakaba 1.40 $self->{onerror}->(node => $child_el,
5934 wakaba 1.34 type => 'element not allowed:head noscript',
5935 wakaba 1.104 level => $self->{level}->{must});
5936 wakaba 1.47 } else {
5937     #
5938 wakaba 1.3 }
5939 wakaba 1.47 } else {
5940     $self->{onerror}->(node => $child_el,
5941     type => 'element not allowed:head noscript',
5942 wakaba 1.104 level => $self->{level}->{must});
5943 wakaba 1.3 }
5944 wakaba 1.40 } else {
5945     $self->{onerror}->(node => $child_el,
5946     type => 'element not allowed:head noscript',
5947 wakaba 1.104 level => $self->{level}->{must});
5948 wakaba 1.40 }
5949     } else {
5950     $HTMLTransparentChecker{check_child_element}->(@_);
5951     }
5952     },
5953     check_child_text => sub {
5954     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5955     if ($self->{flag}->{in_head}) {
5956     if ($has_significant) {
5957     $self->{onerror}->(node => $child_node,
5958 wakaba 1.104 type => 'character not allowed',
5959     level => $self->{level}->{must});
5960 wakaba 1.3 }
5961     } else {
5962 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5963     }
5964     },
5965     check_end => sub {
5966     my ($self, $item, $element_state) = @_;
5967     $self->_remove_minus_elements ($element_state);
5968     if ($self->{flag}->{in_head}) {
5969     $HTMLChecker{check_end}->(@_);
5970     } else {
5971     $HTMLPhrasingContentChecker{check_end}->(@_);
5972 wakaba 1.3 }
5973 wakaba 1.1 },
5974     };
5975 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5976 wakaba 1.1
5977     $Element->{$HTML_NS}->{'event-source'} = {
5978 wakaba 1.40 %HTMLEmptyChecker,
5979 wakaba 1.118 status => FEATURE_HTML5_LC_DROPPED,
5980     check_attrs => $GetHTMLAttrsChecker->({
5981     src => $HTMLURIAttrChecker,
5982     }, {
5983     %HTMLAttrStatus,
5984     src => FEATURE_HTML5_LC_DROPPED,
5985     }),
5986     check_start => sub {
5987     my ($self, $item, $element_state) = @_;
5988    
5989     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5990     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5991     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5992     },
5993     };
5994    
5995     $Element->{$HTML_NS}->{eventsource} = {
5996     %HTMLEmptyChecker,
5997     status => FEATURE_HTML5_DEFAULT,
5998 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5999 wakaba 1.1 src => $HTMLURIAttrChecker,
6000 wakaba 1.50 }, {
6001     %HTMLAttrStatus,
6002 wakaba 1.118 src => FEATURE_HTML5_DEFAULT,
6003 wakaba 1.1 }),
6004 wakaba 1.66 check_start => sub {
6005     my ($self, $item, $element_state) = @_;
6006    
6007     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
6008 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6009     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6010 wakaba 1.66 },
6011 wakaba 1.1 };
6012    
6013     $Element->{$HTML_NS}->{details} = {
6014 wakaba 1.72 %HTMLFlowContentChecker,
6015 wakaba 1.48 status => FEATURE_HTML5_WD,
6016 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6017 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
6018 wakaba 1.50 }, {
6019     %HTMLAttrStatus,
6020 wakaba 1.59 open => FEATURE_HTML5_WD,
6021 wakaba 1.1 }),
6022 wakaba 1.72 ## NOTE: legend, Flow
6023 wakaba 1.43 check_child_element => sub {
6024     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6025     $child_is_transparent, $element_state) = @_;
6026     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6027     $self->{onerror}->(node => $child_el,
6028     type => 'element not allowed:minus',
6029 wakaba 1.104 level => $self->{level}->{must});
6030 wakaba 1.43 $element_state->{has_non_legend} = 1;
6031     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6032     #
6033     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
6034     if ($element_state->{has_non_legend}) {
6035     $self->{onerror}->(node => $child_el,
6036     type => 'element not allowed:details legend',
6037 wakaba 1.104 level => $self->{level}->{must});
6038 wakaba 1.43 }
6039     $element_state->{has_legend} = 1;
6040     $element_state->{has_non_legend} = 1;
6041     } else {
6042 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
6043 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
6044     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
6045     ## is conforming?
6046     }
6047     },
6048     check_child_text => sub {
6049     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6050     if ($has_significant) {
6051     $element_state->{has_non_legend} = 1;
6052     }
6053     },
6054     check_end => sub {
6055     my ($self, $item, $element_state) = @_;
6056 wakaba 1.1
6057 wakaba 1.43 unless ($element_state->{has_legend}) {
6058     $self->{onerror}->(node => $item->{node},
6059 wakaba 1.110 type => 'child element missing',
6060 wakaba 1.104 text => 'legend',
6061     level => $self->{level}->{must});
6062 wakaba 1.43 }
6063    
6064 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
6065 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
6066 wakaba 1.1 },
6067     };
6068    
6069     $Element->{$HTML_NS}->{datagrid} = {
6070 wakaba 1.72 %HTMLFlowContentChecker,
6071 wakaba 1.48 status => FEATURE_HTML5_WD,
6072 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6073 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6074     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
6075 wakaba 1.50 }, {
6076     %HTMLAttrStatus,
6077     disabled => FEATURE_HTML5_WD,
6078     multiple => FEATURE_HTML5_WD,
6079 wakaba 1.1 }),
6080 wakaba 1.40 check_start => sub {
6081     my ($self, $item, $element_state) = @_;
6082 wakaba 1.1
6083 wakaba 1.40 $self->_add_minus_elements ($element_state,
6084     {$HTML_NS => {a => 1, datagrid => 1}});
6085     $element_state->{phase} = 'any';
6086 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6087     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6088 wakaba 1.40 },
6089 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
6090     ## datalist | Empty
6091 wakaba 1.40 check_child_element => sub {
6092     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6093     $child_is_transparent, $element_state) = @_;
6094     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6095     $self->{onerror}->(node => $child_el,
6096     type => 'element not allowed:minus',
6097 wakaba 1.104 level => $self->{level}->{must});
6098 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6099     #
6100 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
6101     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
6102 wakaba 1.44 if (not $element_state->{has_element} and
6103 wakaba 1.40 $child_nsuri eq $HTML_NS and
6104 wakaba 1.95 {
6105     table => 1, select => 1, datalist => 1,
6106     }->{$child_ln}) {
6107 wakaba 1.40 $self->{onerror}->(node => $child_el,
6108 wakaba 1.104 type => 'element not allowed',
6109     level => $self->{level}->{must});
6110 wakaba 1.40 } else {
6111 wakaba 1.8 #
6112 wakaba 1.1 }
6113 wakaba 1.40 } else {
6114     $self->{onerror}->(node => $child_el,
6115 wakaba 1.121 type => 'element not allowed', ## TODO: :flow
6116 wakaba 1.104 level => $self->{level}->{must});
6117 wakaba 1.40 }
6118 wakaba 1.43 $element_state->{has_element} = 1;
6119 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
6120     if ($child_nsuri eq $HTML_NS and
6121     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
6122     $element_state->{phase} = 'none';
6123 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
6124 wakaba 1.40 $element_state->{has_element} = 1;
6125 wakaba 1.72 $element_state->{phase} = 'flow';
6126 wakaba 1.40 } else {
6127     $self->{onerror}->(node => $child_el,
6128 wakaba 1.104 type => 'element not allowed',
6129     level => $self->{level}->{must});
6130 wakaba 1.40 }
6131     } elsif ($element_state->{phase} eq 'none') {
6132     $self->{onerror}->(node => $child_el,
6133 wakaba 1.104 type => 'element not allowed',
6134     level => $self->{level}->{must});
6135 wakaba 1.40 } else {
6136     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
6137     }
6138     },
6139     check_child_text => sub {
6140     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6141     if ($has_significant) {
6142 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
6143 wakaba 1.40 #
6144     } elsif ($element_state->{phase} eq 'any') {
6145 wakaba 1.72 $element_state->{phase} = 'flow';
6146 wakaba 1.40 } else {
6147     $self->{onerror}->(node => $child_node,
6148 wakaba 1.104 type => 'character not allowed',
6149     level => $self->{level}->{must});
6150 wakaba 1.1 }
6151     }
6152 wakaba 1.40 },
6153     check_end => sub {
6154     my ($self, $item, $element_state) = @_;
6155     $self->_remove_minus_elements ($element_state);
6156 wakaba 1.1
6157 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
6158     if ($element_state->{has_significant}) {
6159     $item->{real_parent_state}->{has_significant} = 1;
6160     } elsif ($item->{transparent}) {
6161     #
6162     } else {
6163     $self->{onerror}->(node => $item->{node},
6164 wakaba 1.104 type => 'no significant content',
6165 wakaba 1.110 level => $self->{level}->{should});
6166 wakaba 1.95 }
6167     } else {
6168     ## NOTE: Since the content model explicitly allows a |datagird| element
6169     ## being empty, we don't raise "no significant content" error for this
6170     ## element when there is no element. (We should raise an error for
6171     ## |<datagrid><br></datagrid>|, however.)
6172     ## NOTE: As a side-effect, when the |datagrid| element only contains
6173     ## non-conforming content, then the |phase| flag has not changed from
6174     ## |any|, no "no significant content" error is raised neither.
6175     ## NOTE: Another side-effect of the current implementation:
6176     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
6177     ## error at all.
6178 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6179     }
6180     },
6181 wakaba 1.1 };
6182    
6183     $Element->{$HTML_NS}->{command} = {
6184 wakaba 1.40 %HTMLEmptyChecker,
6185 wakaba 1.48 status => FEATURE_HTML5_WD,
6186 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6187 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
6188     default => $GetHTMLBooleanAttrChecker->('default'),
6189     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
6190     icon => $HTMLURIAttrChecker,
6191     label => sub { }, ## NOTE: No conformance creteria
6192     radiogroup => sub { }, ## NOTE: No conformance creteria
6193     type => sub {
6194     my ($self, $attr) = @_;
6195     my $value = $attr->value;
6196     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
6197 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'invalid attribute value',
6198     level => $self->{level}->{must});
6199 wakaba 1.1 }
6200     },
6201 wakaba 1.50 }, {
6202     %HTMLAttrStatus,
6203     checked => FEATURE_HTML5_WD,
6204     default => FEATURE_HTML5_WD,
6205     disabled => FEATURE_HTML5_WD,
6206     icon => FEATURE_HTML5_WD,
6207     label => FEATURE_HTML5_WD,
6208     radiogroup => FEATURE_HTML5_WD,
6209     type => FEATURE_HTML5_WD,
6210 wakaba 1.1 }),
6211 wakaba 1.66 check_start => sub {
6212     my ($self, $item, $element_state) = @_;
6213    
6214     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
6215 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6216     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6217 wakaba 1.66 },
6218 wakaba 1.115 };
6219    
6220     $Element->{$HTML_NS}->{bb} = {
6221     %HTMLPhrasingContentChecker,
6222     status => FEATURE_HTML5_DEFAULT,
6223     check_attrs => $GetHTMLAttrsChecker->({
6224     type => $GetHTMLEnumeratedAttrChecker->({makeapp => 1}),
6225     }, {
6226     %HTMLAttrStatus,
6227     type => FEATURE_HTML5_DEFAULT,
6228     }),
6229     ## TODO: no interactive content descendant
6230 wakaba 1.1 };
6231    
6232     $Element->{$HTML_NS}->{menu} = {
6233 wakaba 1.40 %HTMLPhrasingContentChecker,
6234 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
6235     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
6236     ## NOTE: We don't want any |menu| element warned as deprecated.
6237 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6238 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
6239 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
6240 wakaba 1.1 id => sub {
6241     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
6242     my ($self, $attr) = @_;
6243     my $value = $attr->value;
6244     if (length $value > 0) {
6245     if ($self->{id}->{$value}) {
6246 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'duplicate ID',
6247     level => $self->{level}->{must});
6248 wakaba 1.1 push @{$self->{id}->{$value}}, $attr;
6249     } else {
6250     $self->{id}->{$value} = [$attr];
6251     }
6252     } else {
6253     ## NOTE: MUST contain at least one character
6254 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'empty attribute value',
6255     level => $self->{level}->{must});
6256 wakaba 1.1 }
6257     if ($value =~ /[\x09-\x0D\x20]/) {
6258 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'space in ID',
6259     level => $self->{level}->{must});
6260 wakaba 1.1 }
6261     $self->{menu}->{$value} ||= $attr;
6262     ## ISSUE: <menu id=""><p contextmenu=""> match?
6263     },
6264     label => sub { }, ## NOTE: No conformance creteria
6265     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
6266 wakaba 1.49 }, {
6267     %HTMLAttrStatus,
6268     %HTMLM12NCommonAttrStatus,
6269 wakaba 1.61 align => FEATURE_HTML2X_RFC,
6270 wakaba 1.113 autosubmit => FEATURE_HTML5_DROPPED,
6271 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
6272 wakaba 1.50 label => FEATURE_HTML5_WD,
6273     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6274 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
6275     sdapref => FEATURE_HTML20_RFC,
6276 wakaba 1.50 type => FEATURE_HTML5_WD,
6277 wakaba 1.1 }),
6278 wakaba 1.40 check_start => sub {
6279     my ($self, $item, $element_state) = @_;
6280     $element_state->{phase} = 'li or phrasing';
6281     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
6282     $self->{flag}->{in_menu} = 1;
6283 wakaba 1.79
6284     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6285     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6286 wakaba 1.40 },
6287     check_child_element => sub {
6288     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6289     $child_is_transparent, $element_state) = @_;
6290     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6291     $self->{onerror}->(node => $child_el,
6292     type => 'element not allowed:minus',
6293 wakaba 1.104 level => $self->{level}->{must});
6294 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6295     #
6296     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
6297     if ($element_state->{phase} eq 'li') {
6298     #
6299     } elsif ($element_state->{phase} eq 'li or phrasing') {
6300     $element_state->{phase} = 'li';
6301     } else {
6302 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6303     level => $self->{level}->{must});
6304 wakaba 1.40 }
6305     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
6306     if ($element_state->{phase} eq 'phrasing') {
6307     #
6308     } elsif ($element_state->{phase} eq 'li or phrasing') {
6309     $element_state->{phase} = 'phrasing';
6310     } else {
6311 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6312     level => $self->{level}->{must});
6313 wakaba 1.40 }
6314     } else {
6315 wakaba 1.104 $self->{onerror}->(node => $child_el, type => 'element not allowed',
6316     level => $self->{level}->{must});
6317 wakaba 1.40 }
6318     },
6319     check_child_text => sub {
6320     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6321     if ($has_significant) {
6322     if ($element_state->{phase} eq 'phrasing') {
6323     #
6324     } elsif ($element_state->{phase} eq 'li or phrasing') {
6325     $element_state->{phase} = 'phrasing';
6326     } else {
6327     $self->{onerror}->(node => $child_node,
6328 wakaba 1.104 type => 'character not allowed',
6329     level => $self->{level}->{must});
6330 wakaba 1.1 }
6331     }
6332 wakaba 1.40 },
6333     check_end => sub {
6334     my ($self, $item, $element_state) = @_;
6335     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
6336    
6337     if ($element_state->{phase} eq 'li') {
6338     $HTMLChecker{check_end}->(@_);
6339     } else { # 'phrasing' or 'li or phrasing'
6340     $HTMLPhrasingContentChecker{check_end}->(@_);
6341 wakaba 1.1 }
6342     },
6343 wakaba 1.8 };
6344    
6345     $Element->{$HTML_NS}->{datatemplate} = {
6346 wakaba 1.40 %HTMLChecker,
6347 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6348 wakaba 1.40 check_child_element => sub {
6349     my ($self, $item, $child_el, $child_nsuri, $child_ln,
6350     $child_is_transparent, $element_state) = @_;
6351     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
6352     $self->{onerror}->(node => $child_el,
6353     type => 'element not allowed:minus',
6354 wakaba 1.104 level => $self->{level}->{must});
6355 wakaba 1.40 } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
6356     #
6357     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
6358     #
6359     } else {
6360     $self->{onerror}->(node => $child_el,
6361 wakaba 1.104 type => 'element not allowed:datatemplate',
6362     level => $self->{level}->{must});
6363 wakaba 1.40 }
6364     },
6365     check_child_text => sub {
6366     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
6367     if ($has_significant) {
6368 wakaba 1.104 $self->{onerror}->(node => $child_node, type => 'character not allowed',
6369     level => $self->{level}->{must});
6370 wakaba 1.8 }
6371     },
6372     is_xml_root => 1,
6373     };
6374    
6375     $Element->{$HTML_NS}->{rule} = {
6376 wakaba 1.40 %HTMLChecker,
6377 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6378 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6379 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
6380 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
6381 wakaba 1.50 }, {
6382     %HTMLAttrStatus,
6383     condition => FEATURE_HTML5_AT_RISK,
6384     mode => FEATURE_HTML5_AT_RISK,
6385 wakaba 1.8 }),
6386 wakaba 1.40 check_start => sub {
6387     my ($self, $item, $element_state) = @_;
6388 wakaba 1.79
6389 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
6390 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
6391     $self->{flag}->{in_rule} = 1;
6392    
6393     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6394     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6395 wakaba 1.40 },
6396     check_child_element => sub { },
6397     check_child_text => sub { },
6398     check_end => sub {
6399     my ($self, $item, $element_state) = @_;
6400 wakaba 1.79
6401 wakaba 1.40 $self->_remove_plus_elements ($element_state);
6402 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
6403    
6404 wakaba 1.40 $HTMLChecker{check_end}->(@_);
6405 wakaba 1.8 },
6406     ## NOTE: "MAY be anything that, when the parent |datatemplate|
6407     ## is applied to some conforming data, results in a conforming DOM tree.":
6408     ## We don't check against this.
6409     };
6410    
6411     $Element->{$HTML_NS}->{nest} = {
6412 wakaba 1.40 %HTMLEmptyChecker,
6413 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
6414 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
6415 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
6416     mode => sub {
6417     my ($self, $attr) = @_;
6418     my $value = $attr->value;
6419     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
6420 wakaba 1.104 $self->{onerror}->(node => $attr, type => 'mode:syntax error',
6421     level => $self->{level}->{must});
6422 wakaba 1.23 }
6423     },
6424 wakaba 1.50 }, {
6425     %HTMLAttrStatus,
6426     filter => FEATURE_HTML5_AT_RISK,
6427     mode => FEATURE_HTML5_AT_RISK,
6428 wakaba 1.8 }),
6429 wakaba 1.1 };
6430    
6431     $Element->{$HTML_NS}->{legend} = {
6432 wakaba 1.40 %HTMLPhrasingContentChecker,
6433 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6434 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
6435 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6436 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
6437     # top => 1, bottom => 1, left => 1, right => 1,
6438     # }),
6439     }, {
6440 wakaba 1.49 %HTMLAttrStatus,
6441     %HTMLM12NCommonAttrStatus,
6442     accesskey => FEATURE_M12N10_REC,
6443     align => FEATURE_M12N10_REC_DEPRECATED,
6444 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6445 wakaba 1.49 }),
6446 wakaba 1.1 };
6447    
6448     $Element->{$HTML_NS}->{div} = {
6449 wakaba 1.72 %HTMLFlowContentChecker,
6450 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
6451 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
6452     align => $GetHTMLEnumeratedAttrChecker->({
6453     left => 1, center => 1, right => 1, justify => 1,
6454     }),
6455     }, {
6456 wakaba 1.49 %HTMLAttrStatus,
6457 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6458 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
6459     datafld => FEATURE_HTML4_REC_RESERVED,
6460     dataformatas => FEATURE_HTML4_REC_RESERVED,
6461     datasrc => FEATURE_HTML4_REC_RESERVED,
6462 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6463 wakaba 1.49 }),
6464 wakaba 1.66 check_start => sub {
6465     my ($self, $item, $element_state) = @_;
6466    
6467     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6468 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6469     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6470 wakaba 1.66 },
6471 wakaba 1.1 };
6472    
6473 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
6474 wakaba 1.72 %HTMLFlowContentChecker,
6475 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
6476     check_attrs => $GetHTMLAttrsChecker->({}, {
6477     %HTMLAttrStatus,
6478     %HTMLM12NCommonAttrStatus,
6479     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6480     }),
6481     };
6482    
6483 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
6484 wakaba 1.40 %HTMLTransparentChecker,
6485 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
6486 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
6487     ## TODO: HTML4 |size|, |color|, |face|
6488 wakaba 1.49 }, {
6489     %HTMLAttrStatus,
6490 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6491 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
6492 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6493 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
6494 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6495     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6496 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
6497 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6498 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6499 wakaba 1.49 }),
6500 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
6501     ## it is allowed only in a document with the WYSIWYG signature. The
6502     ## checker does not check whether there is the signature, since the
6503     ## signature is dropped, too, and has never been implemented. (In addition,
6504     ## for any |font| element an "element not defined" error is raised anyway,
6505     ## such that we don't have to raise an additional error.)
6506 wakaba 1.1 };
6507 wakaba 1.49
6508 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
6509     %HTMLEmptyChecker,
6510     status => FEATURE_M12N10_REC_DEPRECATED,
6511     check_attrs => $GetHTMLAttrsChecker->({
6512     ## TODO: color, face, size
6513     }, {
6514     %HTMLAttrStatus,
6515     color => FEATURE_M12N10_REC_DEPRECATED,
6516     face => FEATURE_M12N10_REC_DEPRECATED,
6517     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
6518     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6519     size => FEATURE_M12N10_REC_DEPRECATED,
6520     }),
6521     };
6522    
6523 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
6524     ## class title id cols rows onload onunload style(x10)
6525     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
6526     ## noframes Common, lang(xhtml10)
6527    
6528 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
6529 wakaba 1.56
6530 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
6531     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
6532     ## xmp, listing sdapref[HTML2,0]
6533    
6534 wakaba 1.56 =pod
6535    
6536     WF2: Documents MUST comply to [CHARMOD].
6537     WF2: Vencor extensions MUST NOT be used.
6538    
6539 wakaba 1.61 HTML 2.0 nextid @n
6540    
6541     RFC 2659: CERTS CRYPTOPTS
6542    
6543     ISO-HTML: pre-html, divN
6544 wakaba 1.82
6545     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
6546     di (Common), nl (Common), handler (Common, type), standby (Common),
6547     summary (Common)
6548    
6549 wakaba 1.97 Access & XHTML2: access (LC)
6550 wakaba 1.82
6551     XML Events & XForms (for XHTML2 support; very, very low priority)
6552 wakaba 1.61
6553 wakaba 1.56 =cut
6554 wakaba 1.61
6555     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
6556     ## We added them only to |a|. |link| and |form| might also allow them
6557     ## in theory.
6558 wakaba 1.1
6559     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
6560    
6561     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24