/[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.81 - (hide annotations) (download)
Sat May 3 09:23:03 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.80: +1 -0 lines
++ whatpm/t/ChangeLog	3 May 2008 09:19:49 -0000
2008-05-03  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Test data for |repeat-max=""|
	and |repeat-start=""| attributes are added.

2008-05-03  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Test data for repetition template attributes
	are added.

2008-05-03  Wakaba  <wakaba@suika.fam.cx>

	* content-model-2.dat: Test data for data template attributes are
	added.

++ whatpm/Whatpm/ContentChecker/ChangeLog	3 May 2008 09:18:28 -0000
	* HTML.pm: Support for |repeat-start=""| attribute.

2008-05-03  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24