/[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.63 - (hide annotations) (download)
Sun Mar 9 11:15:14 2008 UTC (16 years, 8 months ago) by wakaba
Branch: MAIN
Changes since 1.62: +3 -0 lines
++ whatpm/t/ChangeLog	9 Mar 2008 11:11:01 -0000
	* content-model-2.dat: |contenteditable=""| tests are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	9 Mar 2008 11:11:18 -0000
	* HTML.pm: |contenteditable| attribute support is now implemented.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24