/[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.55 - (hide annotations) (download)
Tue Feb 26 08:43:19 2008 UTC (16 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.54: +17 -3 lines
++ whatpm/Whatpm/ContentChecker/ChangeLog	26 Feb 2008 08:43:16 -0000
	* HTML.pm: Since even XHTML Basic 1.1 is a CR, diffs
	from XHTML M12N 1.0 are reflected in |status| field.

2008-02-26  Wakaba  <wakaba@suika.fam.cx>

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24