/[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.128 - (hide annotations) (download)
Wed Sep 17 03:55:15 2008 UTC (16 years, 10 months ago) by wakaba
Branch: MAIN
Changes since 1.127: +14 -4 lines
++ whatpm/t/ChangeLog	17 Sep 2008 03:45:14 -0000
2008-09-17  Wakaba  <wakaba@suika.fam.cx>

	* content-model-1.dat: Test results are updated.

	* content-model-2.dat: Tests for style="" are added.

++ whatpm/Whatpm/CSS/ChangeLog	17 Sep 2008 03:45:24 -0000
2008-09-17  Wakaba  <wakaba@suika.fam.cx>

	* Parser.pm (parse_char_string_as_inline): New method.

++ whatpm/Whatpm/ContentChecker/ChangeLog	17 Sep 2008 03:55:06 -0000
2008-09-17  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for style="" attributes.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24