/[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.54 - (hide annotations) (download)
Tue Feb 26 08:28:00 2008 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.53: +58 -38 lines
++ whatpm/Whatpm/ChangeLog	26 Feb 2008 08:22:33 -0000
2008-02-26  Wakaba  <wakaba@suika.fam.cx>

	* ContentChecker.pm: New status constants are added.
	($ElementDefault): |status| added.
	(check_element): Err for non-standard or deprecated elements.
	(_attr_status_info): For non-standard or deprecated attributes.

++ whatpm/Whatpm/ContentChecker/ChangeLog	26 Feb 2008 08:27:45 -0000
	* HTML.pm: HTML5 status constants are OR-ed with "allowed" status.
	Don't raise "not defined" errors; they are now raised according
	to status flags.  Status flags of li/@value, ol/@start, and menu
	are now non-deprecated, to avoid deprecated error message.
	area/@hreftype typo fixed.  |isindex| SHOULD NOT be used
	according to HTML4.

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24