/[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.119 - (hide annotations) (download)
Thu Sep 4 14:49:02 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.118: +90 -88 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	4 Sep 2008 14:46:07 -0000
2008-09-04  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: WF2 content model, contexts, attributes/elements
	status are updated. (HTML5 revision 2142-2152).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24