/[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.100 - (hide annotations) (download)
Sun Jun 8 13:29:58 2008 UTC (17 years, 1 month ago) by wakaba
Branch: MAIN
Changes since 1.99: +47 -12 lines
++ whatpm/t/ChangeLog	8 Jun 2008 13:04:13 -0000
	* content-model-1.dat, content-model-2.dat: Test data are
	updated and added for <map name=""> and <map id=""> (HTML5
	revisions 1722 and 1728).

2008-06-08  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	8 Jun 2008 13:04:07 -0000
	* HTML.pm: Support for |<map name="">| (HTML5 revisions
	1722 and 1728).

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

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24