/[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.53 - (hide annotations) (download)
Tue Feb 26 07:46:22 2008 UTC (17 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.52: +4 -5 lines
++ whatpm/t/ChangeLog	26 Feb 2008 07:46:15 -0000
2008-02-26  Wakaba  <wakaba@suika.fam.cx>

	* content-model-1.dat: Figure caption is now optional (HTML5
	revision 1240).  Add test data for |ol reversed| (HTML5
	revision 1248).

++ whatpm/Whatpm/ContentChecker/ChangeLog	26 Feb 2008 07:45:46 -0000
2008-02-26  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Make |figure| caption optional (HTML5 revision 1240).
	Add |reversed| to |ol| (HTML5 revision 1248).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24