/[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.82 - (hide annotations) (download)
Sat May 3 14:42:27 2008 UTC (16 years, 6 months ago) by wakaba
Branch: MAIN
Changes since 1.81: +318 -162 lines
++ whatpm/Whatpm/ChangeLog	3 May 2008 14:42:15 -0000
	* ContentChecker.pm: Support for global attributes.
	Status of XML specs are added.

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

++ whatpm/Whatpm/ContentChecker/ChangeLog	3 May 2008 14:41:46 -0000
	* HTML.pm: Support for global attributes.  Status
	for XHTML2 elements/attributes are added.

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

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24