/[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.92 - (hide annotations) (download)
Sat May 24 11:57:47 2008 UTC (17 years, 2 months ago) by wakaba
Branch: MAIN
Changes since 1.91: +48 -28 lines
++ whatpm/t/ChangeLog	24 May 2008 11:50:28 -0000
	* content-model-2.dat: Test data on sandbox="" and seemless=""
	are added (cf. HTML5 revision 1637 and 1643).

2008-05-24  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ChangeLog	24 May 2008 11:16:23 -0000
	* HTML.pm.src: ";" is not part of charset name (HTML5 revision 1665).

2008-05-24  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	24 May 2008 11:50:15 -0000
2008-05-24  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for seemless="" (HTML5 revision 1637) and
	sandbox="" (HTML5 revision 1643).

2008-05-24  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: |width| and |height| on |iframe| are now conforming (HTML5
	revision 1659).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24