/[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.79 - (hide annotations) (download)
Sat May 3 08:00:17 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.78: +178 -3 lines
++ whatpm/Whatpm/ChangeLog	3 May 2008 07:58:56 -0000
2008-05-03  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.pm (check_element): Support for |template=""|
	and |ref=""| attribute (referent element type checking).

++ whatpm/Whatpm/ContentChecker/ChangeLog	3 May 2008 07:58:22 -0000
2008-05-03  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for |template=""|, |ref=""|, and
	|registrationmark=""| attributes.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24