/[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.101 - (hide annotations) (download)
Sat Jul 19 13:50:03 2008 UTC (17 years ago) by wakaba
Branch: MAIN
Changes since 1.100: +14 -0 lines
++ whatpm/Whatpm/ChangeLog	19 Jul 2008 13:49:53 -0000
	* WebIDL.pm (type_text): Better serializer.

2008-07-19  Wakaba  <wakaba@suika.fam.cx>

++ whatpm/Whatpm/ContentChecker/ChangeLog	19 Jul 2008 13:38:24 -0000
2008-07-19  Wakaba  <wakaba@suika.fam.cx>

	* HTML.pm: Support for class=idl WebIDL checking.

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.101 check_end => sub {
2429     my ($self, $item, $element_state) = @_;
2430    
2431     ## TODO: Flag to enable/disable IDL checking?
2432     my $class = $item->{node}->get_attribute ('class');
2433     if ($class =~ /\bidl\b/) { ## TODO: use classList.has
2434     $self->{onsubdoc}->({s => $item->{node}->text_content,
2435     container_node => $item->{node},
2436     media_type => 'text/x-webidl',
2437     is_char_string => 1});
2438     }
2439    
2440     $HTMLChecker{check_end}->(@_);
2441     },
2442 wakaba 1.1 };
2443    
2444     $Element->{$HTML_NS}->{ol} = {
2445 wakaba 1.40 %HTMLChecker,
2446 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2447 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2448 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
2449 wakaba 1.69 reversed => $GetHTMLBooleanAttrChecker->('reversed'),
2450 wakaba 1.1 start => $HTMLIntegerAttrChecker,
2451 wakaba 1.69 ## TODO: HTML4 |type|
2452 wakaba 1.49 }, {
2453     %HTMLAttrStatus,
2454 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2455 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2456 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2457 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2458 wakaba 1.53 reversed => FEATURE_HTML5_DEFAULT,
2459 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2460 wakaba 1.54 #start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
2461     start => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2462 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2463 wakaba 1.1 }),
2464 wakaba 1.40 check_child_element => sub {
2465     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2466     $child_is_transparent, $element_state) = @_;
2467     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2468     $self->{onerror}->(node => $child_el,
2469     type => 'element not allowed:minus',
2470     level => $self->{must_level});
2471     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2472     #
2473     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
2474     #
2475     } else {
2476     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2477 wakaba 1.1 }
2478 wakaba 1.40 },
2479     check_child_text => sub {
2480     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2481     if ($has_significant) {
2482     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2483 wakaba 1.1 }
2484     },
2485     };
2486    
2487     $Element->{$HTML_NS}->{ul} = {
2488 wakaba 1.40 %{$Element->{$HTML_NS}->{ol}},
2489 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2490 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2491     compact => $GetHTMLBooleanAttrChecker->('compact'),
2492 wakaba 1.69 ## TODO: HTML4 |type|
2493     ## TODO: sdaform, align
2494 wakaba 1.68 }, {
2495 wakaba 1.49 %HTMLAttrStatus,
2496 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2497 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2498 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2499 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2500 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2501 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2502     }),
2503 wakaba 1.1 };
2504    
2505 wakaba 1.64 $Element->{$HTML_NS}->{dir} = {
2506     ## TODO: %block; is not allowed [HTML4] ## TODO: Empty list allowed?
2507     %{$Element->{$HTML_NS}->{ul}},
2508     status => FEATURE_M12N10_REC_DEPRECATED,
2509 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2510     compact => $GetHTMLBooleanAttrChecker->('compact'),
2511     }, {
2512 wakaba 1.64 %HTMLAttrStatus,
2513     %HTMLM12NCommonAttrStatus,
2514     align => FEATURE_HTML2X_RFC,
2515     compact => FEATURE_M12N10_REC_DEPRECATED,
2516     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2517     sdaform => FEATURE_HTML20_RFC,
2518     sdapref => FEATURE_HTML20_RFC,
2519     }),
2520     };
2521    
2522 wakaba 1.1 $Element->{$HTML_NS}->{li} = {
2523 wakaba 1.72 %HTMLFlowContentChecker,
2524 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2525 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
2526 wakaba 1.69 ## TODO: HTML4 |type|
2527 wakaba 1.49 value => sub {
2528 wakaba 1.1 my ($self, $attr) = @_;
2529     my $parent = $attr->owner_element->manakai_parent_element;
2530     if (defined $parent) {
2531     my $parent_ns = $parent->namespace_uri;
2532     $parent_ns = '' unless defined $parent_ns;
2533     my $parent_ln = $parent->manakai_local_name;
2534     unless ($parent_ns eq $HTML_NS and $parent_ln eq 'ol') {
2535     $self->{onerror}->(node => $attr, level => 'unsupported',
2536     type => 'attribute');
2537     }
2538     }
2539     $HTMLIntegerAttrChecker->($self, $attr);
2540 wakaba 1.49 }, ## TODO: test
2541     }, {
2542     %HTMLAttrStatus,
2543 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2544 wakaba 1.61 align => FEATURE_HTML2X_RFC,
2545 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2546 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2547 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2548 wakaba 1.55 #value => FEATURE_HTML5_DEFAULT | FEATURE_XHTMLBASIC11_CR |
2549     # FEATURE_M12N10_REC_DEPRECATED,
2550 wakaba 1.82 value => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED |
2551     FEATURE_XHTMLBASIC11_CR | FEATURE_M12N10_REC,
2552 wakaba 1.1 }),
2553 wakaba 1.40 check_child_element => sub {
2554     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2555     $child_is_transparent, $element_state) = @_;
2556     if ($self->{flag}->{in_menu}) {
2557     $HTMLPhrasingContentChecker{check_child_element}->(@_);
2558     } else {
2559 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
2560 wakaba 1.40 }
2561     },
2562     check_child_text => sub {
2563     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2564     if ($self->{flag}->{in_menu}) {
2565     $HTMLPhrasingContentChecker{check_child_text}->(@_);
2566 wakaba 1.1 } else {
2567 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
2568 wakaba 1.1 }
2569     },
2570     };
2571    
2572     $Element->{$HTML_NS}->{dl} = {
2573 wakaba 1.40 %HTMLChecker,
2574 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2575 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
2576     compact => $GetHTMLBooleanAttrChecker->('compact'),
2577     }, {
2578 wakaba 1.49 %HTMLAttrStatus,
2579 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2580 wakaba 1.49 compact => FEATURE_M12N10_REC_DEPRECATED,
2581 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2582 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2583     sdapref => FEATURE_HTML20_RFC,
2584 wakaba 1.49 type => FEATURE_M12N10_REC_DEPRECATED,
2585     }),
2586 wakaba 1.40 check_start => sub {
2587     my ($self, $item, $element_state) = @_;
2588     $element_state->{phase} = 'before dt';
2589 wakaba 1.79
2590     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2591     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2592 wakaba 1.40 },
2593     check_child_element => sub {
2594     my ($self, $item, $child_el, $child_nsuri, $child_ln,
2595     $child_is_transparent, $element_state) = @_;
2596     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
2597     $self->{onerror}->(node => $child_el,
2598     type => 'element not allowed:minus',
2599     level => $self->{must_level});
2600     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
2601     #
2602     } elsif ($element_state->{phase} eq 'in dds') {
2603     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2604     #$element_state->{phase} = 'in dds';
2605     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2606     $element_state->{phase} = 'in dts';
2607     } else {
2608     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2609     }
2610     } elsif ($element_state->{phase} eq 'in dts') {
2611     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2612     #$element_state->{phase} = 'in dts';
2613     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2614     $element_state->{phase} = 'in dds';
2615     } else {
2616     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2617     }
2618     } elsif ($element_state->{phase} eq 'before dt') {
2619     if ($child_nsuri eq $HTML_NS and $child_ln eq 'dt') {
2620     $element_state->{phase} = 'in dts';
2621     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'dd') {
2622     $self->{onerror}
2623     ->(node => $child_el, type => 'ps element missing:dt');
2624     $element_state->{phase} = 'in dds';
2625     } else {
2626     $self->{onerror}->(node => $child_el, type => 'element not allowed');
2627 wakaba 1.1 }
2628 wakaba 1.40 } else {
2629     die "check_child_element: Bad |dl| phase: $element_state->{phase}";
2630 wakaba 1.1 }
2631 wakaba 1.40 },
2632     check_child_text => sub {
2633     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
2634     if ($has_significant) {
2635     $self->{onerror}->(node => $child_node, type => 'character not allowed');
2636     }
2637     },
2638     check_end => sub {
2639     my ($self, $item, $element_state) = @_;
2640     if ($element_state->{phase} eq 'in dts') {
2641     $self->{onerror}->(node => $item->{node},
2642     type => 'child element missing:dd');
2643 wakaba 1.1 }
2644    
2645 wakaba 1.40 $HTMLChecker{check_end}->(@_);
2646 wakaba 1.1 },
2647     };
2648    
2649     $Element->{$HTML_NS}->{dt} = {
2650 wakaba 1.40 %HTMLPhrasingContentChecker,
2651 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2652 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2653     %HTMLAttrStatus,
2654 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2655 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2656 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2657 wakaba 1.49 }),
2658 wakaba 1.1 };
2659    
2660     $Element->{$HTML_NS}->{dd} = {
2661 wakaba 1.72 %HTMLFlowContentChecker,
2662 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2663 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2664     %HTMLAttrStatus,
2665 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2666 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2667 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2668 wakaba 1.49 }),
2669 wakaba 1.1 };
2670    
2671     $Element->{$HTML_NS}->{a} = {
2672 wakaba 1.40 %HTMLPhrasingContentChecker,
2673 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2674 wakaba 1.40 check_attrs => sub {
2675     my ($self, $item, $element_state) = @_;
2676 wakaba 1.1 my %attr;
2677 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
2678 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
2679     $attr_ns = '' unless defined $attr_ns;
2680     my $attr_ln = $attr->manakai_local_name;
2681     my $checker;
2682 wakaba 1.73 my $status;
2683 wakaba 1.1 if ($attr_ns eq '') {
2684 wakaba 1.73 $status = {
2685     %HTMLAttrStatus,
2686 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2687 wakaba 1.73 accesskey => FEATURE_M12N10_REC,
2688     charset => FEATURE_M12N10_REC,
2689 wakaba 1.82 coords => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2690 wakaba 1.73 cryptopts => FEATURE_RFC2659,
2691     dn => FEATURE_RFC2659,
2692 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2693 wakaba 1.82 hreflang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2694 wakaba 1.73 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2695 wakaba 1.82 media => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED,
2696 wakaba 1.73 methods => FEATURE_HTML20_RFC,
2697     name => FEATURE_M12N10_REC_DEPRECATED,
2698     nonce => FEATURE_RFC2659,
2699     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2700     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2701     ping => FEATURE_HTML5_DEFAULT,
2702 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2703     rev => FEATURE_RDFA_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2704 wakaba 1.73 sdapref => FEATURE_HTML20_RFC,
2705 wakaba 1.82 shape => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2706 wakaba 1.73 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2707 wakaba 1.82 target => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2708 wakaba 1.73 type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2709     urn => FEATURE_HTML20_RFC,
2710     }->{$attr_ln};
2711    
2712 wakaba 1.1 $checker = {
2713 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
2714 wakaba 1.91 charset => sub {
2715     my ($self, $attr) = @_;
2716     $HTMLCharsetChecker->($attr->value, @_);
2717     },
2718 wakaba 1.70 ## TODO: HTML4 |coords|
2719 wakaba 1.1 target => $HTMLTargetAttrChecker,
2720     href => $HTMLURIAttrChecker,
2721     ping => $HTMLSpaceURIsAttrChecker,
2722 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
2723 wakaba 1.92 rev => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
2724 wakaba 1.70 ## TODO: HTML4 |shape|
2725 wakaba 1.1 media => $HTMLMQAttrChecker,
2726 wakaba 1.70 ## TODO: HTML4/XHTML1 |name|
2727 wakaba 1.1 hreflang => $HTMLLanguageTagAttrChecker,
2728     type => $HTMLIMTAttrChecker,
2729     }->{$attr_ln};
2730     if ($checker) {
2731     $attr{$attr_ln} = $attr;
2732 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
2733 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
2734     $status = $HTMLDatasetAttrStatus;
2735 wakaba 1.1 } else {
2736     $checker = $HTMLAttrChecker->{$attr_ln};
2737     }
2738     }
2739     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
2740     || $AttrChecker->{$attr_ns}->{''};
2741 wakaba 1.82 $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
2742     || $AttrStatus->{$attr_ns}->{''};
2743     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
2744 wakaba 1.62
2745 wakaba 1.1 if ($checker) {
2746 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
2747 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
2748 wakaba 1.54 #
2749 wakaba 1.1 } else {
2750     $self->{onerror}->(node => $attr, level => 'unsupported',
2751     type => 'attribute');
2752 wakaba 1.50 ## ISSUE: No conformance createria for unknown attributes in the spec
2753 wakaba 1.1 }
2754 wakaba 1.49
2755 wakaba 1.82 $self->_attr_status_info ($attr, $status);
2756 wakaba 1.1 }
2757    
2758 wakaba 1.40 $element_state->{in_a_href_original} = $self->{flag}->{in_a_href};
2759 wakaba 1.4 if (defined $attr{href}) {
2760     $self->{has_hyperlink_element} = 1;
2761 wakaba 1.40 $self->{flag}->{in_a_href} = 1;
2762 wakaba 1.4 } else {
2763 wakaba 1.1 for (qw/target ping rel media hreflang type/) {
2764     if (defined $attr{$_}) {
2765     $self->{onerror}->(node => $attr{$_},
2766     type => 'attribute not allowed');
2767     }
2768     }
2769     }
2770 wakaba 1.66
2771     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
2772 wakaba 1.1 },
2773 wakaba 1.40 check_start => sub {
2774     my ($self, $item, $element_state) = @_;
2775     $self->_add_minus_elements ($element_state, $HTMLInteractiveContent);
2776 wakaba 1.79
2777     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2778     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2779 wakaba 1.40 },
2780     check_end => sub {
2781     my ($self, $item, $element_state) = @_;
2782     $self->_remove_minus_elements ($element_state);
2783 wakaba 1.59 delete $self->{flag}->{in_a_href}
2784     unless $element_state->{in_a_href_original};
2785 wakaba 1.1
2786 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2787 wakaba 1.1 },
2788     };
2789    
2790     $Element->{$HTML_NS}->{q} = {
2791 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2792 wakaba 1.40 %HTMLPhrasingContentChecker,
2793     check_attrs => $GetHTMLAttrsChecker->({
2794 wakaba 1.50 cite => $HTMLURIAttrChecker,
2795     }, {
2796 wakaba 1.49 %HTMLAttrStatus,
2797 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2798     cite => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2799 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2800 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
2801     sdasuff => FEATURE_HTML2X_RFC,
2802 wakaba 1.1 }),
2803 wakaba 1.66 check_start => sub {
2804     my ($self, $item, $element_state) = @_;
2805    
2806     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
2807 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2808     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2809 wakaba 1.66 },
2810 wakaba 1.1 };
2811 wakaba 1.75 ## TODO: "Quotation punctuation (such as quotation marks), if any, must be
2812     ## placed inside the <code>q</code> element." Though we cannot test the
2813     ## element against this requirement since it incluides a semantic bit,
2814     ## it might be possible to inform of the existence of quotation marks OUTSIDE
2815     ## the |q| element.
2816 wakaba 1.1
2817     $Element->{$HTML_NS}->{cite} = {
2818 wakaba 1.40 %HTMLPhrasingContentChecker,
2819 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2820 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2821     %HTMLAttrStatus,
2822 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2823 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2824 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2825 wakaba 1.49 }),
2826 wakaba 1.1 };
2827    
2828     $Element->{$HTML_NS}->{em} = {
2829 wakaba 1.40 %HTMLPhrasingContentChecker,
2830 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2831 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2832     %HTMLAttrStatus,
2833 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2834 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2835 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2836 wakaba 1.49 }),
2837 wakaba 1.1 };
2838    
2839     $Element->{$HTML_NS}->{strong} = {
2840 wakaba 1.40 %HTMLPhrasingContentChecker,
2841 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2842 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2843     %HTMLAttrStatus,
2844 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2845 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2846 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
2847 wakaba 1.49 }),
2848 wakaba 1.1 };
2849    
2850     $Element->{$HTML_NS}->{small} = {
2851 wakaba 1.40 %HTMLPhrasingContentChecker,
2852 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
2853     check_attrs => $GetHTMLAttrsChecker->({}, {
2854     %HTMLAttrStatus,
2855     %HTMLM12NCommonAttrStatus,
2856 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2857 wakaba 1.49 }),
2858 wakaba 1.1 };
2859    
2860 wakaba 1.51 $Element->{$HTML_NS}->{big} = {
2861     %HTMLPhrasingContentChecker,
2862     status => FEATURE_M12N10_REC,
2863     check_attrs => $GetHTMLAttrsChecker->({}, {
2864     %HTMLAttrStatus,
2865     %HTMLM12NCommonAttrStatus,
2866     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2867     }),
2868     };
2869    
2870 wakaba 1.38 $Element->{$HTML_NS}->{mark} = {
2871 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2872 wakaba 1.40 %HTMLPhrasingContentChecker,
2873 wakaba 1.1 };
2874    
2875     $Element->{$HTML_NS}->{dfn} = {
2876 wakaba 1.40 %HTMLPhrasingContentChecker,
2877 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2878 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2879     %HTMLAttrStatus,
2880 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2881 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2882 wakaba 1.49 }),
2883 wakaba 1.40 check_start => sub {
2884     my ($self, $item, $element_state) = @_;
2885     $self->_add_minus_elements ($element_state, {$HTML_NS => {dfn => 1}});
2886 wakaba 1.1
2887 wakaba 1.40 my $node = $item->{node};
2888 wakaba 1.1 my $term = $node->get_attribute_ns (undef, 'title');
2889     unless (defined $term) {
2890     for my $child (@{$node->child_nodes}) {
2891     if ($child->node_type == 1) { # ELEMENT_NODE
2892     if (defined $term) {
2893     undef $term;
2894     last;
2895     } elsif ($child->manakai_local_name eq 'abbr') {
2896     my $nsuri = $child->namespace_uri;
2897     if (defined $nsuri and $nsuri eq $HTML_NS) {
2898     my $attr = $child->get_attribute_node_ns (undef, 'title');
2899     if ($attr) {
2900     $term = $attr->value;
2901     }
2902     }
2903     }
2904     } elsif ($child->node_type == 3 or $child->node_type == 4) {
2905     ## TEXT_NODE or CDATA_SECTION_NODE
2906     if ($child->data =~ /\A[\x09-\x0D\x20]+\z/) { # Inter-element whitespace
2907     next;
2908     }
2909     undef $term;
2910     last;
2911     }
2912     }
2913     unless (defined $term) {
2914     $term = $node->text_content;
2915     }
2916     }
2917     if ($self->{term}->{$term}) {
2918     push @{$self->{term}->{$term}}, $node;
2919     } else {
2920     $self->{term}->{$term} = [$node];
2921     }
2922 wakaba 1.77 ## ISSUE: The HTML5 definition for the defined term does not work with
2923     ## |ruby| unless |dfn| has |title|.
2924 wakaba 1.79
2925     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
2926     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
2927 wakaba 1.40 },
2928     check_end => sub {
2929     my ($self, $item, $element_state) = @_;
2930     $self->_remove_minus_elements ($element_state);
2931 wakaba 1.1
2932 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
2933 wakaba 1.1 },
2934     };
2935    
2936     $Element->{$HTML_NS}->{abbr} = {
2937 wakaba 1.40 %HTMLPhrasingContentChecker,
2938 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
2939 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
2940     %HTMLAttrStatus,
2941 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
2942     full => FEATURE_XHTML2_ED,
2943 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2944 wakaba 1.49 }),
2945 wakaba 1.77 ## NOTE: "If an abbreviation is pluralised, the expansion's grammatical
2946     ## number (plural vs singular) must match the grammatical number of the
2947     ## contents of the element." Though this can be checked by machine,
2948     ## it requires language-specific knowledge and dictionary, such that
2949     ## we don't support the check of the requirement.
2950     ## ISSUE: Is <abbr title="Cascading Style Sheets">CSS</abbr> conforming?
2951 wakaba 1.49 };
2952    
2953     $Element->{$HTML_NS}->{acronym} = {
2954     %HTMLPhrasingContentChecker,
2955     status => FEATURE_M12N10_REC,
2956     check_attrs => $GetHTMLAttrsChecker->({}, {
2957     %HTMLAttrStatus,
2958     %HTMLM12NCommonAttrStatus,
2959 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
2960 wakaba 1.49 }),
2961 wakaba 1.1 };
2962    
2963     $Element->{$HTML_NS}->{time} = {
2964 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
2965 wakaba 1.40 %HTMLPhrasingContentChecker,
2966     check_attrs => $GetHTMLAttrsChecker->({
2967 wakaba 1.1 datetime => sub { 1 }, # checked in |checker|
2968 wakaba 1.49 }, {
2969     %HTMLAttrStatus,
2970     %HTMLM12NCommonAttrStatus,
2971 wakaba 1.72 datetime => FEATURE_HTML5_FD,
2972 wakaba 1.1 }),
2973     ## TODO: Write tests
2974 wakaba 1.40 check_end => sub {
2975     my ($self, $item, $element_state) = @_;
2976 wakaba 1.1
2977 wakaba 1.40 my $attr = $item->{node}->get_attribute_node_ns (undef, 'datetime');
2978 wakaba 1.1 my $input;
2979     my $reg_sp;
2980     my $input_node;
2981     if ($attr) {
2982     $input = $attr->value;
2983     $reg_sp = qr/[\x09-\x0D\x20]*/;
2984     $input_node = $attr;
2985     } else {
2986 wakaba 1.40 $input = $item->{node}->text_content;
2987 wakaba 1.1 $reg_sp = qr/\p{Zs}*/;
2988 wakaba 1.40 $input_node = $item->{node};
2989 wakaba 1.1
2990     ## ISSUE: What is the definition for "successfully extracts a date
2991     ## or time"? If the algorithm says the string is invalid but
2992     ## return some date or time, is it "successfully"?
2993     }
2994    
2995     my $hour;
2996     my $minute;
2997     my $second;
2998     if ($input =~ /
2999     \A
3000     [\x09-\x0D\x20]*
3001     ([0-9]+) # 1
3002     (?>
3003     -([0-9]+) # 2
3004     -([0-9]+) # 3
3005     [\x09-\x0D\x20]*
3006     (?>
3007     T
3008     [\x09-\x0D\x20]*
3009     )?
3010     ([0-9]+) # 4
3011     :([0-9]+) # 5
3012     (?>
3013     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 6
3014     )?
3015     [\x09-\x0D\x20]*
3016     (?>
3017     Z
3018     [\x09-\x0D\x20]*
3019     |
3020     [+-]([0-9]+):([0-9]+) # 7, 8
3021     [\x09-\x0D\x20]*
3022     )?
3023     \z
3024     |
3025     :([0-9]+) # 9
3026     (?>
3027     :([0-9]+(?>\.[0-9]*)?|\.[0-9]*) # 10
3028     )?
3029     [\x09-\x0D\x20]*\z
3030     )
3031     /x) {
3032     if (defined $2) { ## YYYY-MM-DD T? hh:mm
3033     if (length $1 != 4 or length $2 != 2 or length $3 != 2 or
3034     length $4 != 2 or length $5 != 2) {
3035     $self->{onerror}->(node => $input_node,
3036     type => 'dateortime:syntax error');
3037     }
3038    
3039     if (1 <= $2 and $2 <= 12) {
3040     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
3041     if $3 < 1 or
3042     $3 > [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]->[$2];
3043     $self->{onerror}->(node => $input_node, type => 'datetime:bad day')
3044     if $2 == 2 and $3 == 29 and
3045     not ($1 % 400 == 0 or ($1 % 4 == 0 and $1 % 100 != 0));
3046     } else {
3047     $self->{onerror}->(node => $input_node,
3048     type => 'datetime:bad month');
3049     }
3050    
3051     ($hour, $minute, $second) = ($4, $5, $6);
3052    
3053     if (defined $7) { ## [+-]hh:mm
3054     if (length $7 != 2 or length $8 != 2) {
3055     $self->{onerror}->(node => $input_node,
3056     type => 'dateortime:syntax error');
3057     }
3058    
3059     $self->{onerror}->(node => $input_node,
3060     type => 'datetime:bad timezone hour')
3061     if $7 > 23;
3062     $self->{onerror}->(node => $input_node,
3063     type => 'datetime:bad timezone minute')
3064     if $8 > 59;
3065     }
3066     } else { ## hh:mm
3067     if (length $1 != 2 or length $9 != 2) {
3068     $self->{onerror}->(node => $input_node,
3069     type => qq'dateortime:syntax error');
3070     }
3071    
3072     ($hour, $minute, $second) = ($1, $9, $10);
3073     }
3074    
3075     $self->{onerror}->(node => $input_node, type => 'datetime:bad hour')
3076     if $hour > 23;
3077     $self->{onerror}->(node => $input_node, type => 'datetime:bad minute')
3078     if $minute > 59;
3079    
3080     if (defined $second) { ## s
3081     ## NOTE: Integer part of second don't have to have length of two.
3082    
3083     if (substr ($second, 0, 1) eq '.') {
3084     $self->{onerror}->(node => $input_node,
3085     type => 'dateortime:syntax error');
3086     }
3087    
3088     $self->{onerror}->(node => $input_node, type => 'datetime:bad second')
3089     if $second >= 60;
3090     }
3091     } else {
3092     $self->{onerror}->(node => $input_node,
3093     type => 'dateortime:syntax error');
3094     }
3095    
3096 wakaba 1.40 $HTMLPhrasingContentChecker{check_end}->(@_);
3097 wakaba 1.1 },
3098     };
3099    
3100     $Element->{$HTML_NS}->{meter} = { ## TODO: "The recommended way of giving the value is to include it as contents of the element"
3101 wakaba 1.77 ## TODO: value inequalities (HTML5 revision 1463)
3102 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3103 wakaba 1.40 %HTMLPhrasingContentChecker,
3104     check_attrs => $GetHTMLAttrsChecker->({
3105 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3106     min => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3107     low => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3108     high => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3109     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3110     optimum => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
3111 wakaba 1.50 }, {
3112     %HTMLAttrStatus,
3113     high => FEATURE_HTML5_DEFAULT,
3114     low => FEATURE_HTML5_DEFAULT,
3115     max => FEATURE_HTML5_DEFAULT,
3116     min => FEATURE_HTML5_DEFAULT,
3117     optimum => FEATURE_HTML5_DEFAULT,
3118     value => FEATURE_HTML5_DEFAULT,
3119 wakaba 1.1 }),
3120     };
3121    
3122     $Element->{$HTML_NS}->{progress} = { ## TODO: recommended to use content
3123 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
3124 wakaba 1.40 %HTMLPhrasingContentChecker,
3125     check_attrs => $GetHTMLAttrsChecker->({
3126 wakaba 1.1 value => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift >= 0 }),
3127     max => $GetHTMLFloatingPointNumberAttrChecker->(sub { shift > 0 }),
3128 wakaba 1.50 }, {
3129     %HTMLAttrStatus,
3130     max => FEATURE_HTML5_DEFAULT,
3131     value => FEATURE_HTML5_DEFAULT,
3132 wakaba 1.1 }),
3133     };
3134    
3135     $Element->{$HTML_NS}->{code} = {
3136 wakaba 1.40 %HTMLPhrasingContentChecker,
3137 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3138 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3139     %HTMLAttrStatus,
3140 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3141 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3142 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3143 wakaba 1.49 }),
3144 wakaba 1.1 };
3145    
3146     $Element->{$HTML_NS}->{var} = {
3147 wakaba 1.40 %HTMLPhrasingContentChecker,
3148 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3149 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3150     %HTMLAttrStatus,
3151 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3152 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3153 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3154 wakaba 1.49 }),
3155 wakaba 1.1 };
3156    
3157     $Element->{$HTML_NS}->{samp} = {
3158 wakaba 1.40 %HTMLPhrasingContentChecker,
3159 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3160 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3161     %HTMLAttrStatus,
3162 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3163 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3164 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3165 wakaba 1.49 }),
3166 wakaba 1.1 };
3167    
3168     $Element->{$HTML_NS}->{kbd} = {
3169 wakaba 1.40 %HTMLPhrasingContentChecker,
3170 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3171 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3172     %HTMLAttrStatus,
3173 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3174 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3175 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3176 wakaba 1.49 }),
3177 wakaba 1.1 };
3178    
3179     $Element->{$HTML_NS}->{sub} = {
3180 wakaba 1.40 %HTMLPhrasingContentChecker,
3181 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3182 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3183     %HTMLAttrStatus,
3184 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3185 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3186 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3187 wakaba 1.49 }),
3188 wakaba 1.1 };
3189    
3190 wakaba 1.51 $Element->{$HTML_NS}->{sup} = $Element->{$HTML_NS}->{sub};
3191 wakaba 1.1
3192     $Element->{$HTML_NS}->{span} = {
3193 wakaba 1.40 %HTMLPhrasingContentChecker,
3194 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3195 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3196     %HTMLAttrStatus,
3197 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3198 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3199     dataformatas => FEATURE_HTML4_REC_RESERVED,
3200     datasrc => FEATURE_HTML4_REC_RESERVED,
3201 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3202 wakaba 1.61 sdaform => FEATURE_HTML2X_RFC,
3203 wakaba 1.49 }),
3204 wakaba 1.1 };
3205    
3206     $Element->{$HTML_NS}->{i} = {
3207 wakaba 1.40 %HTMLPhrasingContentChecker,
3208 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3209     check_attrs => $GetHTMLAttrsChecker->({}, {
3210     %HTMLAttrStatus,
3211     %HTMLM12NCommonAttrStatus,
3212 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3213 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
3214 wakaba 1.49 }),
3215 wakaba 1.1 };
3216    
3217 wakaba 1.51 $Element->{$HTML_NS}->{b} = $Element->{$HTML_NS}->{i};
3218    
3219 wakaba 1.61 $Element->{$HTML_NS}->{tt} = {
3220     %HTMLPhrasingContentChecker,
3221     status => FEATURE_M12N10_REC,
3222     check_attrs => $GetHTMLAttrsChecker->({}, {
3223     %HTMLAttrStatus,
3224     %HTMLM12NCommonAttrStatus,
3225     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3226     sdaform => FEATURE_HTML20_RFC,
3227     }),
3228     };
3229 wakaba 1.51
3230     $Element->{$HTML_NS}->{s} = {
3231 wakaba 1.40 %HTMLPhrasingContentChecker,
3232 wakaba 1.51 status => FEATURE_M12N10_REC_DEPRECATED,
3233 wakaba 1.49 check_attrs => $GetHTMLAttrsChecker->({}, {
3234     %HTMLAttrStatus,
3235     %HTMLM12NCommonAttrStatus,
3236 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3237 wakaba 1.49 }),
3238 wakaba 1.1 };
3239    
3240 wakaba 1.51 $Element->{$HTML_NS}->{strike} = $Element->{$HTML_NS}->{s};
3241    
3242     $Element->{$HTML_NS}->{u} = $Element->{$HTML_NS}->{s};
3243    
3244 wakaba 1.1 $Element->{$HTML_NS}->{bdo} = {
3245 wakaba 1.40 %HTMLPhrasingContentChecker,
3246 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3247 wakaba 1.40 check_attrs => sub {
3248     my ($self, $item, $element_state) = @_;
3249 wakaba 1.49 $GetHTMLAttrsChecker->({}, {
3250     %HTMLAttrStatus,
3251 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3252     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3253     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3254 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3255 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3256     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3257 wakaba 1.61 sdapref => FEATURE_HTML2X_RFC,
3258     sdasuff => FEATURE_HTML2X_RFC,
3259 wakaba 1.49 })->($self, $item, $element_state);
3260 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'dir')) {
3261     $self->{onerror}->(node => $item->{node},
3262     type => 'attribute missing:dir');
3263 wakaba 1.1 }
3264     },
3265     ## ISSUE: The spec does not directly say that |dir| is a enumerated attr.
3266     };
3267    
3268 wakaba 1.99 $Element->{$HTML_NS}->{ruby} = {
3269     %HTMLPhrasingContentChecker,
3270     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3271     check_attrs => $GetHTMLAttrsChecker->({}, {
3272     %HTMLAttrStatus,
3273     %HTMLM12NXHTML2CommonAttrStatus, # XHTML 1.1 & XHTML 2.0 & XHTML+RDFa 1.0
3274     lang => FEATURE_HTML5_DEFAULT,
3275     }),
3276     check_start => sub {
3277     my ($self, $item, $element_state) = @_;
3278    
3279     $element_state->{phase} = 'before-rb';
3280     #$element_state->{has_sig}
3281 wakaba 1.100
3282     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3283     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3284 wakaba 1.99 },
3285     ## NOTE: (phrasing, (rt | (rp, rt, rp)))+
3286     check_child_element => sub {
3287     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3288     $child_is_transparent, $element_state) = @_;
3289     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3290     $self->{onerror}->(node => $child_el,
3291     type => 'element not allowed:minus',
3292     level => $self->{must_level});
3293     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3294     #
3295     } elsif ($element_state->{phase} eq 'before-rb') {
3296     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3297     $element_state->{phase} = 'in-rb';
3298     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3299     $self->{onerror}->(node => $child_el,
3300     level => $self->{should_level},
3301     type => 'no significant content before'); ## TODO: type
3302     $element_state->{phase} = 'after-rt';
3303     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3304     $self->{onerror}->(node => $child_el,
3305     level => $self->{should_level},
3306     type => 'no significant content before'); ## TODO: type
3307     $element_state->{phase} = 'after-rp1';
3308     } else {
3309     $self->{onerror}->(node => $child_el,
3310     type => 'element not allowed:ruby base', ## TODO: type
3311     level => $self->{must_level});
3312     $element_state->{phase} = 'in-rb';
3313     }
3314     } elsif ($element_state->{phase} eq 'in-rb') {
3315     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3316     #$element_state->{phase} = 'in-rb';
3317     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3318     unless ($element_state->{has_significant}) {
3319     $self->{onerror}->(node => $child_el,
3320     level => $self->{should_level},
3321     type => 'no significant content before'); ## TODO: type
3322     }
3323     $element_state->{phase} = 'after-rt';
3324     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3325     unless ($element_state->{has_significant}) {
3326     $self->{onerror}->(node => $child_el,
3327     level => $self->{should_level},
3328     type => 'no significant content before'); ## TODO: type
3329     }
3330     $element_state->{phase} = 'after-rp1';
3331     } else {
3332     $self->{onerror}->(node => $child_el,
3333     type => 'element not allowed:ruby base', ## TODO: type
3334     level => $self->{must_level});
3335     #$element_state->{phase} = 'in-rb';
3336     }
3337     } elsif ($element_state->{phase} eq 'after-rt') {
3338     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3339     if ($element_state->{has_significant}) {
3340     $element_state->{has_sig} = 1;
3341     delete $element_state->{has_significant};
3342     }
3343     $element_state->{phase} = 'in-rb';
3344     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3345     $self->{onerror}->(node => $child_el,
3346     level => $self->{should_level},
3347     type => 'no significant content before'); ## TODO: type
3348     $element_state->{phase} = 'after-rp1';
3349     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3350     $self->{onerror}->(node => $child_el,
3351     level => $self->{should_level},
3352     type => 'no significant content before'); ## TODO: type
3353     #$element_state->{phase} = 'after-rt';
3354     } else {
3355     $self->{onerror}->(node => $child_el,
3356     type => 'element not allowed:ruby base', ## TODO: type
3357     level => $self->{must_level});
3358     if ($element_state->{has_significant}) {
3359     $element_state->{has_sig} = 1;
3360     delete $element_state->{has_significant};
3361     }
3362     $element_state->{phase} = 'in-rb';
3363     }
3364     } elsif ($element_state->{phase} eq 'after-rp1') {
3365     if ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3366     $element_state->{phase} = 'after-rp-rt';
3367     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3368     $self->{onerror}->(node => $child_el,
3369     type => 'ps element missing:rt', ## TODO: type
3370     level => $self->{must_level});
3371     $element_state->{phase} = 'after-rp2';
3372     } else {
3373     $self->{onerror}->(node => $child_el,
3374     type => 'ps element missing:rt', ## TODO: type
3375     level => $self->{must_level});
3376     $self->{onerror}->(node => $child_el,
3377     type => 'ps element missing:rp', ## TODO: type
3378     level => $self->{must_level});
3379     unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3380     $self->{onerror}->(node => $child_el,
3381     type => 'element not allowed:ruby base', ## TODO: type
3382     level => $self->{must_level});
3383     }
3384     if ($element_state->{has_significant}) {
3385     $element_state->{has_sig} = 1;
3386     delete $element_state->{has_significant};
3387     }
3388     $element_state->{phase} = 'in-rb';
3389     }
3390     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3391     if ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3392     $element_state->{phase} = 'after-rp2';
3393     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3394     $self->{onerror}->(node => $child_el,
3395     type => 'ps element missing:rp', ## TODO: type
3396     level => $self->{must_level});
3397     $self->{onerror}->(node => $child_el,
3398     level => $self->{should_level},
3399     type => 'no significant content before'); ## TODO: type
3400     $element_state->{phase} = 'after-rt';
3401     } else {
3402     $self->{onerror}->(node => $child_el,
3403     type => 'ps element missing:rp', ## TODO: type
3404     level => $self->{must_level});
3405     unless ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3406     $self->{onerror}->(node => $child_el,
3407     type => 'element not allowed:ruby base', ## TODO: type
3408     level => $self->{must_level});
3409     }
3410     if ($element_state->{has_significant}) {
3411     $element_state->{has_sig} = 1;
3412     delete $element_state->{has_significant};
3413     }
3414     $element_state->{phase} = 'in-rb';
3415     }
3416     } elsif ($element_state->{phase} eq 'after-rp2') {
3417     if ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
3418     if ($element_state->{has_significant}) {
3419     $element_state->{has_sig} = 1;
3420     delete $element_state->{has_significant};
3421     }
3422     $element_state->{phase} = 'in-rb';
3423     } elsif ($child_ln eq 'rt' and $child_nsuri eq $HTML_NS) {
3424     $self->{onerror}->(node => $child_el,
3425     level => $self->{should_level},
3426     type => 'no significant content before'); ## TODO: type
3427     $element_state->{phase} = 'after-rt';
3428     } elsif ($child_ln eq 'rp' and $child_nsuri eq $HTML_NS) {
3429     $self->{onerror}->(node => $child_el,
3430     level => $self->{should_level},
3431     type => 'no significant content before'); ## TODO: type
3432     $element_state->{phase} = 'after-rp1';
3433     } else {
3434     $self->{onerror}->(node => $child_el,
3435     type => 'element not allowed:ruby base', ## TODO: type
3436     level => $self->{must_level});
3437     if ($element_state->{has_significant}) {
3438     $element_state->{has_sig} = 1;
3439     delete $element_state->{has_significant};
3440     }
3441     $element_state->{phase} = 'in-rb';
3442     }
3443     } else {
3444     die "check_child_element: Bad |ruby| phase: $element_state->{phase}";
3445     }
3446     },
3447     check_child_text => sub {
3448     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3449     if ($has_significant) {
3450     if ($element_state->{phase} eq 'before-rb') {
3451     $element_state->{phase} = 'in-rb';
3452     } elsif ($element_state->{phase} eq 'in-rb') {
3453     #
3454     } elsif ($element_state->{phase} eq 'after-rt' or
3455     $element_state->{phase} eq 'after-rp2') {
3456     $element_state->{phase} = 'in-rb';
3457     } elsif ($element_state->{phase} eq 'after-rp1') {
3458     $self->{onerror}->(node => $child_node,
3459     type => 'ps element missing:rt', ## TODO: type
3460     level => $self->{must_level});
3461     $self->{onerror}->(node => $child_node,
3462     type => 'ps element missing:rp', ## TODO: type
3463     level => $self->{must_level});
3464     $element_state->{phase} = 'in-rb';
3465     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3466     $self->{onerror}->(node => $child_node,
3467     type => 'ps element missing:rp', ## TODO: type
3468     level => $self->{must_level});
3469     $element_state->{phase} = 'in-rb';
3470     } else {
3471     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3472     }
3473     }
3474     },
3475     check_end => sub {
3476     my ($self, $item, $element_state) = @_;
3477     $self->_remove_minus_elements ($element_state);
3478    
3479     if ($element_state->{phase} eq 'before-rb') {
3480     $self->{onerror}->(node => $item->{node},
3481     level => $self->{should_level},
3482     type => 'no significant content');
3483     $self->{onerror}->(node => $item->{node},
3484     type => 'element missing:rt', ## TODO: type
3485     level => $self->{must_level});
3486     } elsif ($element_state->{phase} eq 'in-rb') {
3487     unless ($element_state->{has_significant}) {
3488     $self->{onerror}->(node => $item->{node},
3489     level => $self->{should_level},
3490     type => 'no significant content at the end'); ## TODO: type
3491     }
3492     $self->{onerror}->(node => $item->{node},
3493     type => 'element missing:rt', ## TODO: type
3494     level => $self->{must_level});
3495     } elsif ($element_state->{phase} eq 'after-rt' or
3496     $element_state->{phase} eq 'after-rp2') {
3497     #
3498     } elsif ($element_state->{phase} eq 'after-rp1') {
3499     $self->{onerror}->(node => $item->{node},
3500     type => 'element missing:rt', ## TODO: type
3501     level => $self->{must_level});
3502     $self->{onerror}->(node => $item->{node},
3503     type => 'element missing:rp', ## TODO: type
3504     level => $self->{must_level});
3505     } elsif ($element_state->{phase} eq 'after-rp-rt') {
3506     $self->{onerror}->(node => $item->{node},
3507     type => 'element missing:rp', ## TODO: type
3508     level => $self->{must_level});
3509     } else {
3510     die "check_child_text: Bad |ruby| phase: $element_state->{phase}";
3511     }
3512    
3513     ## NOTE: A modified version of |check_end| of %AnyChecker.
3514     if ($element_state->{has_significant} or $element_state->{has_sig}) {
3515     $item->{real_parent_state}->{has_significant} = 1;
3516     }
3517     },
3518     };
3519    
3520     $Element->{$HTML_NS}->{rt} = {
3521     %HTMLPhrasingContentChecker,
3522     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3523     check_attrs => $GetHTMLAttrsChecker->({}, {
3524     %HTMLAttrStatus,
3525     %HTMLM12NXHTML2CommonAttrStatus,
3526     lang => FEATURE_HTML5_DEFAULT,
3527     }),
3528     };
3529    
3530     $Element->{$HTML_NS}->{rp} = {
3531     %HTMLTextChecker,
3532     status => FEATURE_HTML5_DEFAULT | FEATURE_RUBY_REC,
3533     check_attrs => $GetHTMLAttrsChecker->({}, {
3534     %HTMLAttrStatus,
3535     %HTMLM12NXHTML2CommonAttrStatus,
3536     lang => FEATURE_HTML5_DEFAULT,
3537     }),
3538 wakaba 1.100 check_start => sub {
3539 wakaba 1.99 my ($self, $item, $element_state) = @_;
3540     $element_state->{text} = '';
3541 wakaba 1.100
3542     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3543     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3544 wakaba 1.99 },
3545     check_child_text => sub {
3546     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3547     if ($has_significant) {
3548     $element_state->{text} .= $child_node->data;
3549     ## NOTE: |<rp> <!---->(</rp>| is allowed.
3550     }
3551     },
3552     check_end => sub {
3553     my ($self, $item, $element_state) = @_;
3554    
3555     my $p_class = ($item->{parent_state} and
3556     $item->{parent_state}->{phase} and
3557     $item->{parent_state}->{phase} eq 'after-rp2')
3558     ? qr/\p{Pe}/ : qr/\p{Ps}/;
3559     if ($element_state->{text} =~ /\A$p_class\z/) {
3560     #=~ /\A[\x09-\x0D\x20]*${p_class}[\x09-\x0D\x20]*\z/) {
3561     #
3562     } else {
3563     $self->{onerror}->(node => $item->{node},
3564     type => 'rp:syntax error', ## TODO: type
3565     level => $self->{must_level});
3566     }
3567    
3568     $HTMLTextChecker{check_end}->(@_);
3569     },
3570     };
3571    
3572 wakaba 1.29 =pod
3573    
3574     ## TODO:
3575    
3576     +
3577     + <p>Partly because of the confusion described above, authors are
3578     + strongly recommended to always mark up all paragraphs with the
3579     + <code>p</code> element, and to not have any <code>ins</code> or
3580     + <code>del</code> elements that cross across any <span
3581     + title="paragraph">implied paragraphs</span>.</p>
3582     +
3583     (An informative note)
3584    
3585     <p><code>ins</code> elements should not cross <span
3586     + title="paragraph">implied paragraph</span> boundaries.</p>
3587     (normative)
3588    
3589     + <p><code>del</code> elements should not cross <span
3590     + title="paragraph">implied paragraph</span> boundaries.</p>
3591     (normative)
3592    
3593     =cut
3594    
3595 wakaba 1.1 $Element->{$HTML_NS}->{ins} = {
3596 wakaba 1.40 %HTMLTransparentChecker,
3597 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3598 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3599 wakaba 1.1 cite => $HTMLURIAttrChecker,
3600     datetime => $HTMLDatetimeAttrChecker,
3601 wakaba 1.49 }, {
3602     %HTMLAttrStatus,
3603     %HTMLM12NCommonAttrStatus,
3604 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3605 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3606 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3607 wakaba 1.1 }),
3608 wakaba 1.66 check_start => sub {
3609     my ($self, $item, $element_state) = @_;
3610    
3611     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3612 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3613     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3614 wakaba 1.66 },
3615 wakaba 1.1 };
3616    
3617     $Element->{$HTML_NS}->{del} = {
3618 wakaba 1.40 %HTMLTransparentChecker,
3619 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3620 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3621 wakaba 1.1 cite => $HTMLURIAttrChecker,
3622     datetime => $HTMLDatetimeAttrChecker,
3623 wakaba 1.49 }, {
3624     %HTMLAttrStatus,
3625     %HTMLM12NCommonAttrStatus,
3626 wakaba 1.50 cite => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3627 wakaba 1.72 datetime => FEATURE_HTML5_FD | FEATURE_M12N10_REC,
3628 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3629 wakaba 1.1 }),
3630 wakaba 1.40 check_end => sub {
3631     my ($self, $item, $element_state) = @_;
3632     if ($element_state->{has_significant}) {
3633     ## NOTE: Significantness flag does not propagate.
3634     } elsif ($item->{transparent}) {
3635     #
3636     } else {
3637     $self->{onerror}->(node => $item->{node},
3638     level => $self->{should_level},
3639     type => 'no significant content');
3640     }
3641 wakaba 1.1 },
3642 wakaba 1.66 check_start => sub {
3643     my ($self, $item, $element_state) = @_;
3644    
3645     $element_state->{uri_info}->{cite}->{type}->{cite} = 1;
3646 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3647     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3648 wakaba 1.66 },
3649 wakaba 1.1 };
3650    
3651 wakaba 1.35 $Element->{$HTML_NS}->{figure} = {
3652 wakaba 1.72 %HTMLFlowContentChecker,
3653 wakaba 1.48 status => FEATURE_HTML5_FD,
3654 wakaba 1.72 ## NOTE: legend, Flow | Flow, legend?
3655 wakaba 1.41 check_child_element => sub {
3656     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3657     $child_is_transparent, $element_state) = @_;
3658     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3659     $self->{onerror}->(node => $child_el,
3660     type => 'element not allowed:minus',
3661     level => $self->{must_level});
3662     $element_state->{has_non_legend} = 1;
3663     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3664     #
3665     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
3666     if ($element_state->{has_legend_at_first}) {
3667     $self->{onerror}->(node => $child_el,
3668     type => 'element not allowed:figure legend',
3669     level => $self->{must_level});
3670     } elsif ($element_state->{has_legend}) {
3671     $self->{onerror}->(node => $element_state->{has_legend},
3672     type => 'element not allowed:figure legend',
3673     level => $self->{must_level});
3674     $element_state->{has_legend} = $child_el;
3675     } elsif ($element_state->{has_non_legend}) {
3676     $element_state->{has_legend} = $child_el;
3677     } else {
3678     $element_state->{has_legend_at_first} = 1;
3679 wakaba 1.35 }
3680 wakaba 1.41 delete $element_state->{has_non_legend};
3681     } else {
3682 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3683 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
3684 wakaba 1.41 }
3685     },
3686     check_child_text => sub {
3687     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3688     if ($has_significant) {
3689     $element_state->{has_non_legend} = 1;
3690 wakaba 1.35 }
3691 wakaba 1.41 },
3692     check_end => sub {
3693     my ($self, $item, $element_state) = @_;
3694 wakaba 1.35
3695 wakaba 1.41 if ($element_state->{has_legend_at_first}) {
3696     #
3697     } elsif ($element_state->{has_legend}) {
3698     if ($element_state->{has_non_legend}) {
3699     $self->{onerror}->(node => $element_state->{has_legend},
3700 wakaba 1.35 type => 'element not allowed:figure legend',
3701     level => $self->{must_level});
3702     }
3703     }
3704 wakaba 1.41
3705 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
3706 wakaba 1.41 ## ISSUE: |<figure><legend>aa</legend></figure>| should be an error?
3707 wakaba 1.35 },
3708     };
3709 wakaba 1.8 ## TODO: Test for <nest/> in <figure/>
3710 wakaba 1.1
3711 wakaba 1.92 my $AttrCheckerNotImplemented = sub {
3712     my ($self, $attr) = @_;
3713     $self->{onerror}->(node => $attr, level => 'unsupported',
3714     type => 'attribute');
3715     };
3716    
3717 wakaba 1.1 $Element->{$HTML_NS}->{img} = {
3718 wakaba 1.40 %HTMLEmptyChecker,
3719 wakaba 1.98 status => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3720 wakaba 1.40 check_attrs => sub {
3721     my ($self, $item, $element_state) = @_;
3722 wakaba 1.1 $GetHTMLAttrsChecker->({
3723 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3724     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3725     }),
3726 wakaba 1.1 alt => sub { }, ## NOTE: No syntactical requirement
3727 wakaba 1.70 border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3728 wakaba 1.1 src => $HTMLURIAttrChecker,
3729     usemap => $HTMLUsemapAttrChecker,
3730 wakaba 1.70 hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3731 wakaba 1.1 ismap => sub {
3732 wakaba 1.40 my ($self, $attr, $parent_item) = @_;
3733     if (not $self->{flag}->{in_a_href}) {
3734 wakaba 1.15 $self->{onerror}->(node => $attr,
3735 wakaba 1.59 type => 'attribute not allowed:ismap',
3736     level => $self->{must_level});
3737 wakaba 1.1 }
3738 wakaba 1.40 $GetHTMLBooleanAttrChecker->('ismap')->($self, $attr, $parent_item);
3739 wakaba 1.1 },
3740 wakaba 1.70 longdesc => $HTMLURIAttrChecker,
3741     ## TODO: HTML4 |name|
3742 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3743 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3744 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3745 wakaba 1.49 }, {
3746     %HTMLAttrStatus,
3747 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3748 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
3749 wakaba 1.98 alt => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3750 wakaba 1.49 border => FEATURE_M12N10_REC_DEPRECATED,
3751 wakaba 1.98 height => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3752 wakaba 1.49 hspace => FEATURE_M12N10_REC_DEPRECATED,
3753 wakaba 1.98 ismap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3754 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3755 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3756     name => FEATURE_M12N10_REC_DEPRECATED,
3757 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
3758 wakaba 1.98 src => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3759     usemap => FEATURE_HTML5_WD | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3760 wakaba 1.49 vspace => FEATURE_M12N10_REC_DEPRECATED,
3761 wakaba 1.98 width => FEATURE_HTML5_WD | FEATURE_M12N10_REC,
3762 wakaba 1.66 })->($self, $item, $element_state);
3763 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'alt')) {
3764     $self->{onerror}->(node => $item->{node},
3765 wakaba 1.37 type => 'attribute missing:alt',
3766     level => $self->{should_level});
3767 wakaba 1.1 }
3768 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
3769     $self->{onerror}->(node => $item->{node},
3770     type => 'attribute missing:src');
3771 wakaba 1.1 }
3772 wakaba 1.66
3773     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3774     $element_state->{uri_info}->{lowsrc}->{type}->{embedded} = 1;
3775     $element_state->{uri_info}->{dynsrc}->{type}->{embedded} = 1;
3776     $element_state->{uri_info}->{longdesc}->{type}->{cite} = 1;
3777 wakaba 1.1 },
3778     };
3779    
3780     $Element->{$HTML_NS}->{iframe} = {
3781 wakaba 1.40 %HTMLTextChecker,
3782 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3783     ## NOTE: Not part of M12N10 Strict
3784 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
3785 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3786 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3787 wakaba 1.92 sandbox => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->({
3788     'allow-same-origin' => 1, 'allow-forms' => 1, 'allow-scripts' => 1,
3789     }),
3790     seemless => $GetHTMLBooleanAttrChecker->('seemless'),
3791 wakaba 1.1 src => $HTMLURIAttrChecker,
3792 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3793 wakaba 1.49 }, {
3794     %HTMLAttrStatus,
3795     %HTMLM12NCommonAttrStatus,
3796     align => FEATURE_XHTML10_REC,
3797 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3798 wakaba 1.49 frameborder => FEATURE_M12N10_REC,
3799 wakaba 1.92 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3800 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3801 wakaba 1.49 longdesc => FEATURE_M12N10_REC,
3802     marginheight => FEATURE_M12N10_REC,
3803     marginwidth => FEATURE_M12N10_REC,
3804 wakaba 1.76 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
3805     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3806 wakaba 1.92 sandbox => FEATURE_HTML5_DEFAULT,
3807 wakaba 1.49 scrolling => FEATURE_M12N10_REC,
3808 wakaba 1.92 seemless => FEATURE_HTML5_DEFAULT,
3809 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3810     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3811 wakaba 1.92 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3812 wakaba 1.1 }),
3813 wakaba 1.66 check_start => sub {
3814     my ($self, $item, $element_state) = @_;
3815    
3816     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3817 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
3818     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
3819 wakaba 1.66 },
3820 wakaba 1.40 };
3821    
3822 wakaba 1.1 $Element->{$HTML_NS}->{embed} = {
3823 wakaba 1.40 %HTMLEmptyChecker,
3824 wakaba 1.98 status => FEATURE_HTML5_WD,
3825 wakaba 1.40 check_attrs => sub {
3826     my ($self, $item, $element_state) = @_;
3827 wakaba 1.1 my $has_src;
3828 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
3829 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
3830     $attr_ns = '' unless defined $attr_ns;
3831     my $attr_ln = $attr->manakai_local_name;
3832     my $checker;
3833 wakaba 1.73
3834     my $status = {
3835     %HTMLAttrStatus,
3836     height => FEATURE_HTML5_DEFAULT,
3837 wakaba 1.98 src => FEATURE_HTML5_WD,
3838     type => FEATURE_HTML5_WD,
3839 wakaba 1.73 width => FEATURE_HTML5_DEFAULT,
3840     }->{$attr_ln};
3841    
3842 wakaba 1.1 if ($attr_ns eq '') {
3843     if ($attr_ln eq 'src') {
3844     $checker = $HTMLURIAttrChecker;
3845     $has_src = 1;
3846     } elsif ($attr_ln eq 'type') {
3847     $checker = $HTMLIMTAttrChecker;
3848 wakaba 1.92 } elsif ($attr_ln eq 'width' or $attr_ln eq 'height') {
3849     $checker = $AttrCheckerNotImplemented; ## TODO: because spec does not define them yet.
3850 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
3851 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
3852     $status = $HTMLDatasetAttrStatus;
3853 wakaba 1.1 } else {
3854     $checker = $HTMLAttrChecker->{$attr_ln}
3855     || sub { }; ## NOTE: Any local attribute is ok.
3856 wakaba 1.98 $status = FEATURE_HTML5_WD | FEATURE_ALLOWED;
3857 wakaba 1.1 }
3858     }
3859     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
3860 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
3861     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
3862     || $AttrStatus->{$attr_ns}->{''};
3863     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
3864 wakaba 1.62
3865 wakaba 1.1 if ($checker) {
3866 wakaba 1.66 $checker->($self, $attr, $item, $element_state);
3867 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
3868 wakaba 1.54 #
3869 wakaba 1.1 } else {
3870     $self->{onerror}->(node => $attr, level => 'unsupported',
3871     type => 'attribute');
3872 wakaba 1.50 ## ISSUE: No conformance createria for global attributes in the spec
3873     }
3874    
3875 wakaba 1.82 $self->_attr_status_info ($attr, $status);
3876 wakaba 1.1 }
3877    
3878     unless ($has_src) {
3879 wakaba 1.40 $self->{onerror}->(node => $item->{node},
3880 wakaba 1.1 type => 'attribute missing:src');
3881     }
3882 wakaba 1.66
3883     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
3884 wakaba 1.1 },
3885     };
3886    
3887 wakaba 1.49 ## TODO:
3888     ## {applet} FEATURE_M12N10_REC_DEPRECATED
3889     ## class, id, title, alt, archive, code, codebase, height, object, width name style,hspace,vspace(xhtml10)
3890    
3891 wakaba 1.1 $Element->{$HTML_NS}->{object} = {
3892 wakaba 1.40 %HTMLTransparentChecker,
3893 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3894 wakaba 1.40 check_attrs => sub {
3895     my ($self, $item, $element_state) = @_;
3896 wakaba 1.1 $GetHTMLAttrsChecker->({
3897 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
3898     bottom => 1, middle => 1, top => 1, left => 1, right => 1,
3899     }),
3900     archive => $HTMLSpaceURIsAttrChecker,
3901     ## TODO: Relative to @codebase
3902     border => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3903     classid => $HTMLURIAttrChecker,
3904     codebase => $HTMLURIAttrChecker,
3905     codetype => $HTMLIMTAttrChecker,
3906     ## TODO: "RECOMMENDED when |classid| is specified" [HTML4]
3907 wakaba 1.1 data => $HTMLURIAttrChecker,
3908 wakaba 1.70 declare => $GetHTMLBooleanAttrChecker->('declare'),
3909     ## NOTE: "The object MUST be instantiated by a subsequent OBJECT ..."
3910     ## [HTML4] but we don't know how to test this.
3911     hspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3912 wakaba 1.76 name => $HTMLBrowsingContextNameAttrChecker,
3913 wakaba 1.70 standby => sub {}, ## NOTE: %Text; in HTML4
3914 wakaba 1.1 type => $HTMLIMTAttrChecker,
3915     usemap => $HTMLUsemapAttrChecker,
3916 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3917 wakaba 1.70 vspace => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
3918 wakaba 1.92 width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
3919 wakaba 1.49 }, {
3920     %HTMLAttrStatus,
3921 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
3922 wakaba 1.49 align => FEATURE_XHTML10_REC,
3923 wakaba 1.82 archive => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3924 wakaba 1.49 border => FEATURE_XHTML10_REC,
3925     classid => FEATURE_M12N10_REC,
3926     codebase => FEATURE_M12N10_REC,
3927     codetype => FEATURE_M12N10_REC,
3928 wakaba 1.82 'content-length' => FEATURE_XHTML2_ED,
3929 wakaba 1.50 data => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3930 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
3931     dataformatas => FEATURE_HTML4_REC_RESERVED,
3932     datasrc => FEATURE_HTML4_REC_RESERVED,
3933 wakaba 1.82 declare => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
3934 wakaba 1.50 height => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3935 wakaba 1.49 hspace => FEATURE_XHTML10_REC,
3936 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
3937 wakaba 1.76 name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3938 wakaba 1.49 standby => FEATURE_M12N10_REC,
3939 wakaba 1.50 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3940     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3941     usemap => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3942 wakaba 1.49 vspace => FEATURE_XHTML10_REC,
3943 wakaba 1.50 width => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
3944 wakaba 1.66 })->($self, $item, $element_state);
3945 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'data')) {
3946     unless ($item->{node}->has_attribute_ns (undef, 'type')) {
3947     $self->{onerror}->(node => $item->{node},
3948 wakaba 1.1 type => 'attribute missing:data|type');
3949     }
3950     }
3951 wakaba 1.66
3952     $element_state->{uri_info}->{data}->{type}->{embedded} = 1;
3953     $element_state->{uri_info}->{classid}->{type}->{embedded} = 1;
3954     $element_state->{uri_info}->{codebase}->{type}->{base} = 1;
3955     ## TODO: archive
3956     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
3957 wakaba 1.1 },
3958 wakaba 1.72 ## NOTE: param*, transparent (Flow)
3959 wakaba 1.41 check_child_element => sub {
3960     my ($self, $item, $child_el, $child_nsuri, $child_ln,
3961     $child_is_transparent, $element_state) = @_;
3962     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
3963     $self->{onerror}->(node => $child_el,
3964     type => 'element not allowed:minus',
3965     level => $self->{must_level});
3966     $element_state->{has_non_legend} = 1;
3967     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
3968     #
3969     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'param') {
3970     if ($element_state->{has_non_param}) {
3971 wakaba 1.72 $self->{onerror}->(node => $child_el, ## TODO: type
3972     type => 'element not allowed:flow',
3973 wakaba 1.41 level => $self->{must_level});
3974 wakaba 1.39 }
3975 wakaba 1.41 } else {
3976 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
3977 wakaba 1.41 $element_state->{has_non_param} = 1;
3978 wakaba 1.39 }
3979 wakaba 1.25 },
3980 wakaba 1.41 check_child_text => sub {
3981     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
3982     if ($has_significant) {
3983     $element_state->{has_non_param} = 1;
3984     }
3985 wakaba 1.42 },
3986     check_end => sub {
3987     my ($self, $item, $element_state) = @_;
3988     if ($element_state->{has_significant}) {
3989 wakaba 1.46 $item->{real_parent_state}->{has_significant} = 1;
3990 wakaba 1.42 } elsif ($item->{node}->manakai_parent_element) {
3991     ## NOTE: Transparent.
3992     } else {
3993     $self->{onerror}->(node => $item->{node},
3994     level => $self->{should_level},
3995     type => 'no significant content');
3996     }
3997     },
3998 wakaba 1.8 ## TODO: Tests for <nest/> in <object/>
3999 wakaba 1.1 };
4000 wakaba 1.41 ## ISSUE: Is |<menu><object data><li>aa</li></object></menu>| conforming?
4001     ## What about |<section><object data><style scoped></style>x</object></section>|?
4002     ## |<section><ins></ins><object data><style scoped></style>x</object></section>|?
4003 wakaba 1.1
4004     $Element->{$HTML_NS}->{param} = {
4005 wakaba 1.40 %HTMLEmptyChecker,
4006 wakaba 1.94 status => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4007 wakaba 1.40 check_attrs => sub {
4008     my ($self, $item, $element_state) = @_;
4009 wakaba 1.1 $GetHTMLAttrsChecker->({
4010     name => sub { },
4011 wakaba 1.70 type => $HTMLIMTAttrChecker,
4012 wakaba 1.1 value => sub { },
4013 wakaba 1.70 valuetype => $GetHTMLEnumeratedAttrChecker->({
4014     data => 1, ref => 1, object => 1,
4015     }),
4016 wakaba 1.49 }, {
4017     %HTMLAttrStatus,
4018 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4019 wakaba 1.94 name => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4020 wakaba 1.82 type => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4021 wakaba 1.94 value => FEATURE_HTML5_LC | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4022 wakaba 1.82 valuetype => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4023 wakaba 1.66 })->(@_);
4024 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'name')) {
4025     $self->{onerror}->(node => $item->{node},
4026 wakaba 1.1 type => 'attribute missing:name');
4027     }
4028 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'value')) {
4029     $self->{onerror}->(node => $item->{node},
4030 wakaba 1.1 type => 'attribute missing:value');
4031     }
4032     },
4033     };
4034    
4035     $Element->{$HTML_NS}->{video} = {
4036 wakaba 1.40 %HTMLTransparentChecker,
4037 wakaba 1.48 status => FEATURE_HTML5_LC,
4038 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4039 wakaba 1.1 src => $HTMLURIAttrChecker,
4040     ## TODO: start, loopstart, loopend, end
4041     ## ISSUE: they MUST be "value time offset"s. Value?
4042 wakaba 1.11 ## ISSUE: playcount has no conformance creteria
4043 wakaba 1.1 autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4044     controls => $GetHTMLBooleanAttrChecker->('controls'),
4045 wakaba 1.59 poster => $HTMLURIAttrChecker,
4046 wakaba 1.92 height => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4047     width => $AttrCheckerNotImplemented, ## TODO: spec does not define yet
4048 wakaba 1.50 }, {
4049     %HTMLAttrStatus,
4050     autoplay => FEATURE_HTML5_LC,
4051     controls => FEATURE_HTML5_LC,
4052     end => FEATURE_HTML5_LC,
4053     height => FEATURE_HTML5_LC,
4054     loopend => FEATURE_HTML5_LC,
4055     loopstart => FEATURE_HTML5_LC,
4056     playcount => FEATURE_HTML5_LC,
4057     poster => FEATURE_HTML5_LC,
4058     src => FEATURE_HTML5_LC,
4059     start => FEATURE_HTML5_LC,
4060     width => FEATURE_HTML5_LC,
4061 wakaba 1.1 }),
4062 wakaba 1.42 check_start => sub {
4063     my ($self, $item, $element_state) = @_;
4064     $element_state->{allow_source}
4065     = not $item->{node}->has_attribute_ns (undef, 'src');
4066     $element_state->{has_source} ||= $element_state->{allow_source} * -1;
4067     ## NOTE: It might be set true by |check_element|.
4068 wakaba 1.66
4069     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4070     $element_state->{uri_info}->{poster}->{type}->{embedded} = 1;
4071 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4072     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4073 wakaba 1.42 },
4074     check_child_element => sub {
4075     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4076     $child_is_transparent, $element_state) = @_;
4077     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4078     $self->{onerror}->(node => $child_el,
4079     type => 'element not allowed:minus',
4080     level => $self->{must_level});
4081     delete $element_state->{allow_source};
4082     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4083     #
4084     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'source') {
4085 wakaba 1.45 unless ($element_state->{allow_source}) {
4086 wakaba 1.72 $self->{onerror}->(node => $child_el, ## TODO: type
4087     type => 'element not allowed:flow',
4088 wakaba 1.42 level => $self->{must_level});
4089     }
4090 wakaba 1.45 $element_state->{has_source} = 1;
4091 wakaba 1.1 } else {
4092 wakaba 1.42 delete $element_state->{allow_source};
4093 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
4094 wakaba 1.42 }
4095     },
4096     check_child_text => sub {
4097     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4098     if ($has_significant) {
4099     delete $element_state->{allow_source};
4100     }
4101 wakaba 1.72 $HTMLFlowContentChecker{check_child_text}->(@_);
4102 wakaba 1.42 },
4103     check_end => sub {
4104     my ($self, $item, $element_state) = @_;
4105     if ($element_state->{has_source} == -1) {
4106     $self->{onerror}->(node => $item->{node},
4107     type => 'element missing:source',
4108     level => $self->{must_level});
4109 wakaba 1.1 }
4110 wakaba 1.42
4111     $Element->{$HTML_NS}->{object}->{check_end}->(@_);
4112 wakaba 1.1 },
4113     };
4114    
4115     $Element->{$HTML_NS}->{audio} = {
4116 wakaba 1.40 %{$Element->{$HTML_NS}->{video}},
4117 wakaba 1.48 status => FEATURE_HTML5_LC,
4118 wakaba 1.42 check_attrs => $GetHTMLAttrsChecker->({
4119     src => $HTMLURIAttrChecker,
4120     ## TODO: start, loopstart, loopend, end
4121     ## ISSUE: they MUST be "value time offset"s. Value?
4122     ## ISSUE: playcount has no conformance creteria
4123     autoplay => $GetHTMLBooleanAttrChecker->('autoplay'),
4124     controls => $GetHTMLBooleanAttrChecker->('controls'),
4125 wakaba 1.50 }, {
4126     %HTMLAttrStatus,
4127     autoplay => FEATURE_HTML5_LC,
4128     controls => FEATURE_HTML5_LC,
4129     end => FEATURE_HTML5_LC,
4130     loopend => FEATURE_HTML5_LC,
4131     loopstart => FEATURE_HTML5_LC,
4132     playcount => FEATURE_HTML5_LC,
4133     src => FEATURE_HTML5_LC,
4134     start => FEATURE_HTML5_LC,
4135 wakaba 1.42 }),
4136 wakaba 1.1 };
4137    
4138     $Element->{$HTML_NS}->{source} = {
4139 wakaba 1.40 %HTMLEmptyChecker,
4140 wakaba 1.48 status => FEATURE_HTML5_DEFAULT,
4141 wakaba 1.40 check_attrs => sub {
4142     my ($self, $item, $element_state) = @_;
4143 wakaba 1.1 $GetHTMLAttrsChecker->({
4144 wakaba 1.90 media => $HTMLMQAttrChecker,
4145     pixelratio => $GetHTMLFloatingPointNumberAttrChecker->(sub { 1 }),
4146     src => $HTMLURIAttrChecker, ## ISSUE: Negative or zero pixelratio=""
4147 wakaba 1.1 type => $HTMLIMTAttrChecker,
4148 wakaba 1.50 }, {
4149     %HTMLAttrStatus,
4150     media => FEATURE_HTML5_DEFAULT,
4151 wakaba 1.90 pixelratio => FEATURE_HTML5_DEFAULT,
4152 wakaba 1.50 src => FEATURE_HTML5_DEFAULT,
4153     type => FEATURE_HTML5_DEFAULT,
4154 wakaba 1.66 })->(@_);
4155 wakaba 1.40 unless ($item->{node}->has_attribute_ns (undef, 'src')) {
4156     $self->{onerror}->(node => $item->{node},
4157 wakaba 1.1 type => 'attribute missing:src');
4158     }
4159 wakaba 1.66
4160     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
4161 wakaba 1.1 },
4162     };
4163    
4164     $Element->{$HTML_NS}->{canvas} = {
4165 wakaba 1.40 %HTMLTransparentChecker,
4166 wakaba 1.89 status => FEATURE_HTML5_COMPLETE,
4167 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4168 wakaba 1.1 height => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4169     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4170 wakaba 1.50 }, {
4171     %HTMLAttrStatus,
4172 wakaba 1.89 height => FEATURE_HTML5_COMPLETE,
4173     width => FEATURE_HTML5_COMPLETE,
4174 wakaba 1.1 }),
4175     };
4176    
4177     $Element->{$HTML_NS}->{map} = {
4178 wakaba 1.72 %HTMLFlowContentChecker,
4179 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4180 wakaba 1.40 check_attrs => sub {
4181     my ($self, $item, $element_state) = @_;
4182 wakaba 1.4 my $has_id;
4183 wakaba 1.100 my $has_name;
4184 wakaba 1.4 $GetHTMLAttrsChecker->({
4185     id => sub {
4186 wakaba 1.100 ## NOTE: Same as the global |id=""|, with |$self->{map}| registration.
4187 wakaba 1.4 my ($self, $attr) = @_;
4188     my $value = $attr->value;
4189 wakaba 1.100 if (length $value) {
4190 wakaba 1.4 if ($self->{id}->{$value}) {
4191     $self->{onerror}->(node => $attr, type => 'duplicate ID');
4192     push @{$self->{id}->{$value}}, $attr;
4193     } else {
4194     $self->{id}->{$value} = [$attr];
4195     }
4196 wakaba 1.1 } else {
4197 wakaba 1.4 ## NOTE: MUST contain at least one character
4198     $self->{onerror}->(node => $attr, type => 'empty attribute value');
4199 wakaba 1.1 }
4200 wakaba 1.4 if ($value =~ /[\x09-\x0D\x20]/) {
4201     $self->{onerror}->(node => $attr, type => 'space in ID');
4202     }
4203 wakaba 1.100 #$self->{map}->{$value} ||= $attr;
4204     $has_id = [$value, $attr];
4205     },
4206     name => sub {
4207     my ($self, $attr) = @_;
4208     my $value = $attr->value;
4209     if (length $value) {
4210     ## NOTE: Duplication is not non-conforming.
4211     ## NOTE: Space characters are not non-conforming.
4212     #
4213     } else {
4214     $self->{onerror}->(node => $attr,
4215     type => 'empty attribute value',
4216     level => $self->{must_level});
4217     }
4218 wakaba 1.4 $self->{map}->{$value} ||= $attr;
4219 wakaba 1.100 $has_name = [$value, $attr];
4220 wakaba 1.4 },
4221 wakaba 1.49 }, {
4222     %HTMLAttrStatus,
4223 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4224     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4225     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4226     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4227 wakaba 1.100 #name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
4228     name => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4229 wakaba 1.50 onclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4230     ondblclick => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4231     onmousedown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4232     onmouseup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4233     onmouseover => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4234     onmousemove => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4235     onmouseout => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4236     onkeypress => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4237     onkeydown => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4238     onkeyup => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4239     title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4240 wakaba 1.66 })->(@_);
4241 wakaba 1.100
4242     if ($has_name and $has_id) {
4243     if ($has_name->[0] ne $has_id->[0]) {
4244     $self->{onerror}->(node => $has_id->[1],
4245     type => 'id ne name', ## TODO: type
4246     level => $self->{must_level});
4247     }
4248     } elsif (not $has_name) {
4249     $self->{onerror}->(node => $item->{node},
4250     type => 'attribute missing:name',
4251     level => $self->{must_level});
4252     }
4253 wakaba 1.4 },
4254 wakaba 1.59 check_start => sub {
4255     my ($self, $item, $element_state) = @_;
4256     $element_state->{in_map_original} = $self->{flag}->{in_map};
4257     $self->{flag}->{in_map} = 1;
4258 wakaba 1.79
4259     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4260     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4261 wakaba 1.59 },
4262     check_end => sub {
4263     my ($self, $item, $element_state) = @_;
4264     delete $self->{flag}->{in_map} unless $element_state->{in_map_original};
4265 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
4266 wakaba 1.59 },
4267 wakaba 1.1 };
4268    
4269     $Element->{$HTML_NS}->{area} = {
4270 wakaba 1.40 %HTMLEmptyChecker,
4271 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4272 wakaba 1.40 check_attrs => sub {
4273     my ($self, $item, $element_state) = @_;
4274 wakaba 1.1 my %attr;
4275     my $coords;
4276 wakaba 1.40 for my $attr (@{$item->{node}->attributes}) {
4277 wakaba 1.1 my $attr_ns = $attr->namespace_uri;
4278     $attr_ns = '' unless defined $attr_ns;
4279     my $attr_ln = $attr->manakai_local_name;
4280     my $checker;
4281 wakaba 1.73 my $status;
4282 wakaba 1.1 if ($attr_ns eq '') {
4283 wakaba 1.73 $status = {
4284     %HTMLAttrStatus,
4285     %HTMLM12NCommonAttrStatus,
4286     accesskey => FEATURE_M12N10_REC,
4287     alt => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4288     coords => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4289 wakaba 1.99 href => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC | FEATURE_M12N10_REC,
4290 wakaba 1.73 hreflang => FEATURE_HTML5_DEFAULT,
4291     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4292     media => FEATURE_HTML5_DEFAULT,
4293     nohref => FEATURE_M12N10_REC,
4294     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4295     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4296     ping => FEATURE_HTML5_DEFAULT,
4297 wakaba 1.99 rel => FEATURE_HTML5_DEFAULT | FEATURE_RDFA_LC,
4298 wakaba 1.73 shape => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4299     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4300     target => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4301     type => FEATURE_HTML5_DEFAULT,
4302     }->{$attr_ln};
4303    
4304 wakaba 1.1 $checker = {
4305 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4306 wakaba 1.1 alt => sub { },
4307     ## NOTE: |alt| value has no conformance creteria.
4308     shape => $GetHTMLEnumeratedAttrChecker->({
4309     circ => -1, circle => 1,
4310     default => 1,
4311     poly => 1, polygon => -1,
4312     rect => 1, rectangle => -1,
4313     }),
4314     coords => sub {
4315     my ($self, $attr) = @_;
4316     my $value = $attr->value;
4317     if ($value =~ /\A-?[0-9]+(?>,-?[0-9]+)*\z/) {
4318     $coords = [split /,/, $value];
4319     } else {
4320     $self->{onerror}->(node => $attr,
4321     type => 'coords:syntax error');
4322     }
4323     },
4324 wakaba 1.70 nohref => $GetHTMLBooleanAttrChecker->('nohref'),
4325     target => $HTMLTargetAttrChecker,
4326 wakaba 1.1 href => $HTMLURIAttrChecker,
4327     ping => $HTMLSpaceURIsAttrChecker,
4328 wakaba 1.40 rel => sub { $HTMLLinkTypesAttrChecker->(1, $item, @_) },
4329 wakaba 1.1 media => $HTMLMQAttrChecker,
4330     hreflang => $HTMLLanguageTagAttrChecker,
4331     type => $HTMLIMTAttrChecker,
4332     }->{$attr_ln};
4333     if ($checker) {
4334     $attr{$attr_ln} = $attr;
4335 wakaba 1.97 } elsif ($attr_ln =~ /^data-./s) {
4336 wakaba 1.73 $checker = $HTMLDatasetAttrChecker;
4337     $status = $HTMLDatasetAttrStatus;
4338 wakaba 1.1 } else {
4339     $checker = $HTMLAttrChecker->{$attr_ln};
4340     }
4341     }
4342     $checker ||= $AttrChecker->{$attr_ns}->{$attr_ln}
4343 wakaba 1.82 || $AttrChecker->{$attr_ns}->{''};
4344     $status ||= $AttrStatus->{$attr_ns}->{$attr_ln}
4345     || $AttrStatus->{$attr_ns}->{''};
4346     $status = FEATURE_ALLOWED if not defined $status and length $attr_ns;
4347 wakaba 1.62
4348 wakaba 1.1 if ($checker) {
4349 wakaba 1.66 $checker->($self, $attr, $item, $element_state) if ref $checker;
4350 wakaba 1.62 } elsif ($attr_ns eq '' and not $status) {
4351 wakaba 1.54 #
4352 wakaba 1.1 } else {
4353     $self->{onerror}->(node => $attr, level => 'unsupported',
4354     type => 'attribute');
4355     ## ISSUE: No comformance createria for unknown attributes in the spec
4356     }
4357 wakaba 1.49
4358 wakaba 1.82 $self->_attr_status_info ($attr, $status);
4359 wakaba 1.1 }
4360    
4361     if (defined $attr{href}) {
4362 wakaba 1.4 $self->{has_hyperlink_element} = 1;
4363 wakaba 1.1 unless (defined $attr{alt}) {
4364 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4365 wakaba 1.1 type => 'attribute missing:alt');
4366     }
4367     } else {
4368     for (qw/target ping rel media hreflang type alt/) {
4369     if (defined $attr{$_}) {
4370     $self->{onerror}->(node => $attr{$_},
4371     type => 'attribute not allowed');
4372     }
4373     }
4374     }
4375    
4376     my $shape = 'rectangle';
4377     if (defined $attr{shape}) {
4378     $shape = {
4379     circ => 'circle', circle => 'circle',
4380     default => 'default',
4381     poly => 'polygon', polygon => 'polygon',
4382     rect => 'rectangle', rectangle => 'rectangle',
4383     }->{lc $attr{shape}->value} || 'rectangle';
4384     ## TODO: ASCII lowercase?
4385     }
4386    
4387     if ($shape eq 'circle') {
4388     if (defined $attr{coords}) {
4389     if (defined $coords) {
4390     if (@$coords == 3) {
4391     if ($coords->[2] < 0) {
4392     $self->{onerror}->(node => $attr{coords},
4393     type => 'coords:out of range:2');
4394     }
4395     } else {
4396     $self->{onerror}->(node => $attr{coords},
4397     type => 'coords:number:3:'.@$coords);
4398     }
4399     } else {
4400     ## NOTE: A syntax error has been reported.
4401     }
4402     } else {
4403 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4404 wakaba 1.1 type => 'attribute missing:coords');
4405     }
4406     } elsif ($shape eq 'default') {
4407     if (defined $attr{coords}) {
4408     $self->{onerror}->(node => $attr{coords},
4409     type => 'attribute not allowed');
4410     }
4411     } elsif ($shape eq 'polygon') {
4412     if (defined $attr{coords}) {
4413     if (defined $coords) {
4414     if (@$coords >= 6) {
4415     unless (@$coords % 2 == 0) {
4416     $self->{onerror}->(node => $attr{coords},
4417     type => 'coords:number:even:'.@$coords);
4418     }
4419     } else {
4420     $self->{onerror}->(node => $attr{coords},
4421     type => 'coords:number:>=6:'.@$coords);
4422     }
4423     } else {
4424     ## NOTE: A syntax error has been reported.
4425     }
4426     } else {
4427 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4428 wakaba 1.1 type => 'attribute missing:coords');
4429     }
4430     } elsif ($shape eq 'rectangle') {
4431     if (defined $attr{coords}) {
4432     if (defined $coords) {
4433     if (@$coords == 4) {
4434     unless ($coords->[0] < $coords->[2]) {
4435     $self->{onerror}->(node => $attr{coords},
4436     type => 'coords:out of range:0');
4437     }
4438     unless ($coords->[1] < $coords->[3]) {
4439     $self->{onerror}->(node => $attr{coords},
4440     type => 'coords:out of range:1');
4441     }
4442     } else {
4443     $self->{onerror}->(node => $attr{coords},
4444     type => 'coords:number:4:'.@$coords);
4445     }
4446     } else {
4447     ## NOTE: A syntax error has been reported.
4448     }
4449     } else {
4450 wakaba 1.40 $self->{onerror}->(node => $item->{node},
4451 wakaba 1.1 type => 'attribute missing:coords');
4452     }
4453     }
4454 wakaba 1.66
4455     $element_state->{uri_info}->{href}->{type}->{hyperlink} = 1;
4456 wakaba 1.1 },
4457 wakaba 1.59 check_start => sub {
4458     my ($self, $item, $element_state) = @_;
4459     unless ($self->{flag}->{in_map} or
4460     not $item->{node}->manakai_parent_element) {
4461     $self->{onerror}->(node => $item->{node},
4462     type => 'element not allowed:area',
4463     level => $self->{must_level});
4464     }
4465 wakaba 1.79
4466     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4467     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4468 wakaba 1.59 },
4469 wakaba 1.1 };
4470    
4471     $Element->{$HTML_NS}->{table} = {
4472 wakaba 1.40 %HTMLChecker,
4473 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4474 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4475 wakaba 1.86 cellpadding => $HTMLLengthAttrChecker,
4476     cellspacing => $HTMLLengthAttrChecker,
4477 wakaba 1.69 frame => $GetHTMLEnumeratedAttrChecker->({
4478     void => 1, above => 1, below => 1, hsides => 1, vsides => 1,
4479     lhs => 1, rhs => 1, box => 1, border => 1,
4480     }),
4481     rules => $GetHTMLEnumeratedAttrChecker->({
4482     none => 1, groups => 1, rows => 1, cols => 1, all => 1,
4483     }),
4484     summary => sub {}, ## NOTE: %Text; in HTML4.
4485     width => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## %Pixels;
4486     }, {
4487 wakaba 1.49 %HTMLAttrStatus,
4488 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4489 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4490     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4491     border => FEATURE_M12N10_REC,
4492     cellpadding => FEATURE_M12N10_REC,
4493     cellspacing => FEATURE_M12N10_REC,
4494 wakaba 1.61 cols => FEATURE_RFC1942,
4495 wakaba 1.49 datafld => FEATURE_HTML4_REC_RESERVED,
4496     dataformatas => FEATURE_HTML4_REC_RESERVED,
4497     datapagesize => FEATURE_M12N10_REC,
4498     datasrc => FEATURE_HTML4_REC_RESERVED,
4499     frame => FEATURE_M12N10_REC,
4500 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4501 wakaba 1.49 rules => FEATURE_M12N10_REC,
4502     summary => FEATURE_M12N10_REC,
4503     width => FEATURE_M12N10_REC,
4504     }),
4505 wakaba 1.40 check_start => sub {
4506     my ($self, $item, $element_state) = @_;
4507     $element_state->{phase} = 'before caption';
4508 wakaba 1.66
4509     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
4510 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4511     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4512 wakaba 1.40 },
4513     check_child_element => sub {
4514     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4515     $child_is_transparent, $element_state) = @_;
4516     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4517     $self->{onerror}->(node => $child_el,
4518     type => 'element not allowed:minus',
4519     level => $self->{must_level});
4520     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4521     #
4522     } elsif ($element_state->{phase} eq 'in tbodys') {
4523     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4524     #$element_state->{phase} = 'in tbodys';
4525     } elsif (not $element_state->{has_tfoot} and
4526     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4527     $element_state->{phase} = 'after tfoot';
4528     $element_state->{has_tfoot} = 1;
4529     } else {
4530     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4531     }
4532     } elsif ($element_state->{phase} eq 'in trs') {
4533     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4534     #$element_state->{phase} = 'in trs';
4535     } elsif (not $element_state->{has_tfoot} and
4536     $child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4537     $element_state->{phase} = 'after tfoot';
4538     $element_state->{has_tfoot} = 1;
4539     } else {
4540     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4541     }
4542     } elsif ($element_state->{phase} eq 'after thead') {
4543     if ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4544     $element_state->{phase} = 'in tbodys';
4545     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4546     $element_state->{phase} = 'in trs';
4547     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4548     $element_state->{phase} = 'in tbodys';
4549     $element_state->{has_tfoot} = 1;
4550     } else {
4551     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4552     }
4553     } elsif ($element_state->{phase} eq 'in colgroup') {
4554     if ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4555     $element_state->{phase} = 'in colgroup';
4556     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4557     $element_state->{phase} = 'after thead';
4558     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4559     $element_state->{phase} = 'in tbodys';
4560     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4561     $element_state->{phase} = 'in trs';
4562     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4563     $element_state->{phase} = 'in tbodys';
4564     $element_state->{has_tfoot} = 1;
4565     } else {
4566     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4567     }
4568     } elsif ($element_state->{phase} eq 'before caption') {
4569     if ($child_nsuri eq $HTML_NS and $child_ln eq 'caption') {
4570     $element_state->{phase} = 'in colgroup';
4571     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'colgroup') {
4572     $element_state->{phase} = 'in colgroup';
4573     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'thead') {
4574     $element_state->{phase} = 'after thead';
4575     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tbody') {
4576     $element_state->{phase} = 'in tbodys';
4577     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4578     $element_state->{phase} = 'in trs';
4579     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tfoot') {
4580     $element_state->{phase} = 'in tbodys';
4581     $element_state->{has_tfoot} = 1;
4582     } else {
4583     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4584     }
4585     } elsif ($element_state->{phase} eq 'after tfoot') {
4586     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4587     } else {
4588     die "check_child_element: Bad |table| phase: $element_state->{phase}";
4589     }
4590     },
4591     check_child_text => sub {
4592     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4593     if ($has_significant) {
4594     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4595 wakaba 1.1 }
4596 wakaba 1.40 },
4597     check_end => sub {
4598     my ($self, $item, $element_state) = @_;
4599 wakaba 1.1
4600     ## Table model errors
4601     require Whatpm::HTMLTable;
4602 wakaba 1.87 my $table = Whatpm::HTMLTable->form_table ($item->{node}, sub {
4603 wakaba 1.1 my %opt = @_;
4604 wakaba 1.84 $opt{type} = 'table:' . $opt{type};
4605     $self->{onerror}->(%opt);
4606 wakaba 1.87 }, $self->{must_level});
4607     Whatpm::HTMLTable->assign_header
4608     ($table, $self->{onerror}, $self->{must_level});
4609     push @{$self->{return}->{table}}, $table;
4610 wakaba 1.1
4611 wakaba 1.40 $HTMLChecker{check_end}->(@_);
4612 wakaba 1.1 },
4613     };
4614    
4615     $Element->{$HTML_NS}->{caption} = {
4616 wakaba 1.40 %HTMLPhrasingContentChecker,
4617 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4618 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4619     align => $GetHTMLEnumeratedAttrChecker->({
4620     top => 1, bottom => 1, left => 1, right => 1,
4621     }),
4622     }, {
4623 wakaba 1.49 %HTMLAttrStatus,
4624 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4625 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
4626 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4627 wakaba 1.49 }),
4628 wakaba 1.1 };
4629    
4630 wakaba 1.69 my %cellalign = (
4631     ## HTML4 %cellhalign;
4632 wakaba 1.70 align => $GetHTMLEnumeratedAttrChecker->({
4633     left => 1, center => 1, right => 1, justify => 1, char => 1,
4634     }),
4635     char => sub {
4636     my ($self, $attr) = @_;
4637 wakaba 1.69
4638 wakaba 1.70 ## NOTE: "character" or |%Character;| in HTML4.
4639    
4640     my $value = $attr->value;
4641     if (length $value != 1) {
4642     $self->{onerror}->(node => $attr, type => 'char:syntax error',
4643     level => $self->{fact_level}); ## TODO: type
4644     }
4645     },
4646 wakaba 1.86 charoff => $HTMLLengthAttrChecker,
4647    
4648 wakaba 1.69 ## HTML4 %cellvalign;
4649 wakaba 1.70 valign => $GetHTMLEnumeratedAttrChecker->({
4650     top => 1, middle => 1, bottom => 1, baseline => 1,
4651     }),
4652 wakaba 1.69 );
4653    
4654 wakaba 1.1 $Element->{$HTML_NS}->{colgroup} = {
4655 wakaba 1.40 %HTMLEmptyChecker,
4656 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4657 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4658 wakaba 1.69 %cellalign,
4659 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4660     ## NOTE: Defined only if "the |colgroup| element contains no |col| elements"
4661     ## TODO: "attribute not supported" if |col|.
4662     ## ISSUE: MUST NOT if any |col|?
4663     ## ISSUE: MUST NOT for |<colgroup span="1"><any><col/></any></colgroup>| (though non-conforming)?
4664 wakaba 1.49 }, {
4665     %HTMLAttrStatus,
4666 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4667 wakaba 1.49 align => FEATURE_M12N10_REC,
4668     char => FEATURE_M12N10_REC,
4669     charoff => FEATURE_M12N10_REC,
4670 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4671 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4672 wakaba 1.49 valign => FEATURE_M12N10_REC,
4673     width => FEATURE_M12N10_REC,
4674 wakaba 1.1 }),
4675 wakaba 1.40 check_child_element => sub {
4676     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4677     $child_is_transparent, $element_state) = @_;
4678     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4679     $self->{onerror}->(node => $child_el,
4680     type => 'element not allowed:minus',
4681     level => $self->{must_level});
4682     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4683     #
4684     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'col') {
4685     #
4686     } else {
4687     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4688     }
4689     },
4690     check_child_text => sub {
4691     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4692     if ($has_significant) {
4693     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4694 wakaba 1.1 }
4695     },
4696     };
4697    
4698     $Element->{$HTML_NS}->{col} = {
4699 wakaba 1.40 %HTMLEmptyChecker,
4700 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4701 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4702 wakaba 1.69 %cellalign,
4703 wakaba 1.1 span => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4704 wakaba 1.49 }, {
4705     %HTMLAttrStatus,
4706 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4707 wakaba 1.49 align => FEATURE_M12N10_REC,
4708     char => FEATURE_M12N10_REC,
4709     charoff => FEATURE_M12N10_REC,
4710 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4711 wakaba 1.82 span => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4712 wakaba 1.49 valign => FEATURE_M12N10_REC,
4713     width => FEATURE_M12N10_REC,
4714 wakaba 1.1 }),
4715     };
4716    
4717     $Element->{$HTML_NS}->{tbody} = {
4718 wakaba 1.40 %HTMLChecker,
4719 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4720 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4721     %cellalign,
4722     }, {
4723 wakaba 1.49 %HTMLAttrStatus,
4724 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4725 wakaba 1.49 align => FEATURE_M12N10_REC,
4726     char => FEATURE_M12N10_REC,
4727     charoff => FEATURE_M12N10_REC,
4728 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4729 wakaba 1.49 valign => FEATURE_M12N10_REC,
4730     }),
4731 wakaba 1.40 check_child_element => sub {
4732     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4733     $child_is_transparent, $element_state) = @_;
4734     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4735     $self->{onerror}->(node => $child_el,
4736     type => 'element not allowed:minus',
4737     level => $self->{must_level});
4738     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4739     #
4740     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'tr') {
4741 wakaba 1.84 #
4742 wakaba 1.40 } else {
4743     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4744     }
4745     },
4746     check_child_text => sub {
4747     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4748     if ($has_significant) {
4749     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4750 wakaba 1.1 }
4751 wakaba 1.40 },
4752 wakaba 1.1 };
4753    
4754     $Element->{$HTML_NS}->{thead} = {
4755 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4756 wakaba 1.1 };
4757    
4758     $Element->{$HTML_NS}->{tfoot} = {
4759 wakaba 1.40 %{$Element->{$HTML_NS}->{tbody}},
4760 wakaba 1.1 };
4761    
4762     $Element->{$HTML_NS}->{tr} = {
4763 wakaba 1.40 %HTMLChecker,
4764 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4765 wakaba 1.69 check_attrs => $GetHTMLAttrsChecker->({
4766     %cellalign,
4767     bgcolor => $HTMLColorAttrChecker,
4768     }, {
4769 wakaba 1.49 %HTMLAttrStatus,
4770 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4771 wakaba 1.49 align => FEATURE_M12N10_REC,
4772     bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4773     char => FEATURE_M12N10_REC,
4774     charoff => FEATURE_M12N10_REC,
4775 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4776 wakaba 1.49 valign => FEATURE_M12N10_REC,
4777     }),
4778 wakaba 1.40 check_child_element => sub {
4779     my ($self, $item, $child_el, $child_nsuri, $child_ln,
4780     $child_is_transparent, $element_state) = @_;
4781     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
4782     $self->{onerror}->(node => $child_el,
4783     type => 'element not allowed:minus',
4784     level => $self->{must_level});
4785     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
4786     #
4787     } elsif ($child_nsuri eq $HTML_NS and
4788     ($child_ln eq 'td' or $child_ln eq 'th')) {
4789 wakaba 1.84 #
4790 wakaba 1.40 } else {
4791     $self->{onerror}->(node => $child_el, type => 'element not allowed');
4792     }
4793     },
4794     check_child_text => sub {
4795     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
4796     if ($has_significant) {
4797     $self->{onerror}->(node => $child_node, type => 'character not allowed');
4798 wakaba 1.1 }
4799     },
4800     };
4801    
4802     $Element->{$HTML_NS}->{td} = {
4803 wakaba 1.72 %HTMLFlowContentChecker,
4804 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4805 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4806 wakaba 1.69 %cellalign,
4807     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4808     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4809     bgcolor => $HTMLColorAttrChecker,
4810 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4811 wakaba 1.87 headers => sub {
4812     ## NOTE: Will be checked by Whatpm::HTMLTable->assign_header.
4813     ## Though that method does not check the |headers| attribute of a
4814     ## |td| element if the element does not form a table, in that case
4815     ## the |td| element is non-conforming anyway.
4816     },
4817 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4818 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4819 wakaba 1.69 scope => $GetHTMLEnumeratedAttrChecker
4820     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
4821 wakaba 1.49 }, {
4822     %HTMLAttrStatus,
4823 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4824     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4825 wakaba 1.49 align => FEATURE_M12N10_REC,
4826 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4827 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4828     char => FEATURE_M12N10_REC,
4829     charoff => FEATURE_M12N10_REC,
4830 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4831 wakaba 1.87 headers => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4832 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
4833 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4834 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
4835 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4836     scope => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4837 wakaba 1.49 valign => FEATURE_M12N10_REC,
4838     width => FEATURE_M12N10_REC_DEPRECATED,
4839 wakaba 1.1 }),
4840     };
4841    
4842     $Element->{$HTML_NS}->{th} = {
4843 wakaba 1.40 %HTMLPhrasingContentChecker,
4844 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4845 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
4846 wakaba 1.69 %cellalign,
4847     abbr => sub {}, ## NOTE: HTML4 %Text; and SHOULD be short.
4848     axis => sub {}, ## NOTE: HTML4 "cdata", comma-separated
4849     bgcolor => $HTMLColorAttrChecker,
4850 wakaba 1.1 colspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4851 wakaba 1.87 ## TODO: HTML4(?) |headers|
4852 wakaba 1.69 nowrap => $GetHTMLBooleanAttrChecker->('nowrap'),
4853 wakaba 1.1 rowspan => $GetHTMLNonNegativeIntegerAttrChecker->(sub { shift > 0 }),
4854     scope => $GetHTMLEnumeratedAttrChecker
4855     ->({row => 1, col => 1, rowgroup => 1, colgroup => 1}),
4856 wakaba 1.49 }, {
4857     %HTMLAttrStatus,
4858 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
4859     abbr => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4860 wakaba 1.49 align => FEATURE_M12N10_REC,
4861 wakaba 1.82 axis => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4862 wakaba 1.49 bgcolor => FEATURE_M12N10_REC_DEPRECATED,
4863     char => FEATURE_M12N10_REC,
4864     charoff => FEATURE_M12N10_REC,
4865 wakaba 1.82 colspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4866     headers => FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4867 wakaba 1.49 height => FEATURE_M12N10_REC_DEPRECATED,
4868 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4869 wakaba 1.49 nowrap => FEATURE_M12N10_REC_DEPRECATED,
4870 wakaba 1.82 rowspan => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4871     scope => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
4872 wakaba 1.49 valign => FEATURE_M12N10_REC,
4873     width => FEATURE_M12N10_REC_DEPRECATED,
4874 wakaba 1.1 }),
4875     };
4876    
4877 wakaba 1.52 $Element->{$HTML_NS}->{form} = {
4878 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: Flow* [WF2]
4879 wakaba 1.56 ## TODO: form in form is allowed in XML [WF2]
4880 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4881     check_attrs => $GetHTMLAttrsChecker->({
4882 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4883 wakaba 1.52 'accept-charset' => $AttrCheckerNotImplemented, ## TODO: Charsets
4884     action => $HTMLURIAttrChecker, ## TODO: "User agent behavior for a value other than HTTP URI is undefined" [HTML4]
4885 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
4886     enctype => $HTMLIMTAttrChecker, ## TODO: "multipart/form-data" should be used when type=file is used [HTML4] ## TODO: MUST NOT parameter [WF2]
4887     method => $GetHTMLEnumeratedAttrChecker->({
4888     get => 1, post => 1, put => 1, delete => 1,
4889     }),
4890 wakaba 1.52 ## NOTE: "get" SHOULD be used for idempotent submittion,
4891     ## "post" SHOULD be used otherwise [HTML4]. This cannot be tested.
4892     name => sub { }, # CDATA in HTML4 ## TODO: must be same as |id| (informative!) [XHTML10]
4893 wakaba 1.56 onreceived => $HTMLEventHandlerAttrChecker,
4894     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4895 wakaba 1.52 target => $HTMLTargetAttrChecker,
4896     ## TODO: Warn for combination whose behavior is not defined.
4897     }, {
4898     %HTMLAttrStatus,
4899     %HTMLM12NCommonAttrStatus,
4900 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
4901 wakaba 1.52 'accept-charset' => FEATURE_M12N10_REC,
4902 wakaba 1.56 action => FEATURE_WF2 | FEATURE_M12N10_REC,
4903     data => FEATURE_WF2,
4904     enctype => FEATURE_WF2 | FEATURE_M12N10_REC,
4905 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4906 wakaba 1.56 method => FEATURE_WF2 | FEATURE_M12N10_REC,
4907 wakaba 1.52 name => FEATURE_M12N10_REC_DEPRECATED,
4908 wakaba 1.56 onreceived => FEATURE_WF2,
4909 wakaba 1.52 onreset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4910     onsubmit => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
4911 wakaba 1.56 replace => FEATURE_WF2,
4912 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
4913     sdasuff => FEATURE_HTML20_RFC,
4914 wakaba 1.52 target => FEATURE_M12N10_REC,
4915     }),
4916     ## TODO: Tests
4917     ## TODO: Tests for <nest/> in <form>
4918 wakaba 1.66 check_start => sub {
4919     my ($self, $item, $element_state) = @_;
4920    
4921     $element_state->{uri_info}->{action}->{type}->{action} = 1;
4922     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
4923 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
4924     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
4925 wakaba 1.66 },
4926 wakaba 1.52 };
4927    
4928     $Element->{$HTML_NS}->{fieldset} = {
4929 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: legend, %Flow; ## TODO: legend
4930 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4931 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
4932     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4933     ## TODO: form [WF2]
4934     }, {
4935 wakaba 1.52 %HTMLAttrStatus,
4936     %HTMLM12NCommonAttrStatus,
4937 wakaba 1.56 disabled => FEATURE_WF2,
4938     form => FEATURE_WF2,
4939 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
4940     }),
4941     ## TODO: Tests
4942     ## TODO: Tests for <nest/> in <fieldset>
4943     };
4944    
4945     $Element->{$HTML_NS}->{input} = {
4946 wakaba 1.56 %HTMLEmptyChecker, ## MUST [WF2]
4947 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
4948     check_attrs => $GetHTMLAttrsChecker->({
4949 wakaba 1.56 accept => $AttrCheckerNotImplemented, ## TODO: ContentTypes [WF2]
4950 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
4951 wakaba 1.56 action => $HTMLURIAttrChecker,
4952 wakaba 1.52 align => $GetHTMLEnumeratedAttrChecker->({
4953     top => 1, middle => 1, bottom => 1, left => 1, right => 1,
4954     }),
4955     alt => sub {}, ## NOTE: Text [M12N] ## TODO: |alt| should be provided for |type=image| [HTML4]
4956     ## NOTE: HTML4 has a "should" for accessibility, which cannot be tested
4957     ## here.
4958 wakaba 1.56 autocomplete => $GetHTMLEnumeratedAttrChecker->({on => 1, off => 1}),
4959     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
4960 wakaba 1.52 checked => $GetHTMLBooleanAttrChecker->('checked'),
4961     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
4962 wakaba 1.56 enctype => $HTMLIMTAttrChecker,
4963     ## TODO: form [WF2]
4964     ## TODO: inputmode [WF2]
4965 wakaba 1.52 ismap => $GetHTMLBooleanAttrChecker->('ismap'),
4966 wakaba 1.56 ## TODO: list [WF2]
4967     ## TODO: max [WF2]
4968 wakaba 1.52 maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4969 wakaba 1.56 method => $GetHTMLEnumeratedAttrChecker->({
4970     get => 1, post => 1, put => 1, delete => 1,
4971     }),
4972     ## TODO: min [WF2]
4973 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
4974     readonly => $GetHTMLBooleanAttrChecker->('readonly'),
4975 wakaba 1.56 replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
4976     required => $GetHTMLBooleanAttrChecker->('required'),
4977 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
4978     src => $HTMLURIAttrChecker,
4979 wakaba 1.56 ## TODO: step [WF2]
4980     target => $HTMLTargetAttrChecker,
4981 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |input| attribute has |template|
4982     ## attribute to support the |add| button type (as part of repetition
4983     ## template feature). It conflicts with the |template| global attribute
4984     ## introduced as part of the data template feature.
4985     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
4986     ## author requirement.
4987 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
4988     text => 1, password => 1, checkbox => 1, radio => 1, submit => 1,
4989     reset => 1, file => 1, hidden => 1, image => 1, button => 1,
4990 wakaba 1.56 ## [WF2]
4991     datatime => 1, 'datetime-local' => 1, date => 1, month => 1, week => 1,
4992     time => 1, number => 1, range => 1, email => 1, url => 1,
4993     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
4994 wakaba 1.52 }),
4995     usemap => $HTMLUsemapAttrChecker,
4996 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]
4997     ## TODO: "authors should ensure that in each set of radio buttons that one is initially "on"." [HTML4] [WF2]
4998 wakaba 1.52 }, {
4999     %HTMLAttrStatus,
5000     %HTMLM12NCommonAttrStatus,
5001 wakaba 1.56 accept => FEATURE_WF2 | FEATURE_M12N10_REC,
5002 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5003 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5004 wakaba 1.56 action => FEATURE_WF2,
5005 wakaba 1.52 align => FEATURE_M12N10_REC_DEPRECATED,
5006     alt => FEATURE_M12N10_REC,
5007 wakaba 1.56 autocomplete => FEATURE_WF2,
5008     autofocus => FEATURE_WF2,
5009 wakaba 1.52 checked => FEATURE_M12N10_REC,
5010     datafld => FEATURE_HTML4_REC_RESERVED,
5011     dataformatas => FEATURE_HTML4_REC_RESERVED,
5012     datasrc => FEATURE_HTML4_REC_RESERVED,
5013 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5014 wakaba 1.65 enctype => FEATURE_WF2,
5015 wakaba 1.56 form => FEATURE_WF2,
5016     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5017 wakaba 1.52 ismap => FEATURE_M12N10_REC,
5018     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5019 wakaba 1.56 list => FEATURE_WF2,
5020     max => FEATURE_WF2,
5021     maxlength => FEATURE_WF2 | FEATURE_M12N10_REC,
5022     method => FEATURE_WF2,
5023     min => FEATURE_WF2,
5024 wakaba 1.52 name => FEATURE_M12N10_REC,
5025     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5026     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5027     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5028     onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5029 wakaba 1.56 readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5030 wakaba 1.65 replace => FEATURE_WF2,
5031 wakaba 1.56 required => FEATURE_WF2,
5032 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5033 wakaba 1.56 size => FEATURE_WF2_DEPRECATED | FEATURE_M12N10_REC,
5034 wakaba 1.52 src => FEATURE_M12N10_REC,
5035 wakaba 1.56 step => FEATURE_WF2,
5036 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5037 wakaba 1.65 target => FEATURE_WF2,
5038 wakaba 1.56 template => FEATURE_WF2,
5039 wakaba 1.52 type => FEATURE_M12N10_REC,
5040     usemap => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC,
5041     value => FEATURE_M12N10_REC,
5042     }),
5043     ## TODO: Tests
5044     ## TODO: Tests for <nest/> in <input>
5045 wakaba 1.66 check_start => sub {
5046     my ($self, $item, $element_state) = @_;
5047    
5048     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5049     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5050     $element_state->{uri_info}->{src}->{type}->{embedded} = 1;
5051 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5052     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5053 wakaba 1.66 },
5054 wakaba 1.52 };
5055    
5056 wakaba 1.56 ## TODO: Form |name| attributes: MUST NOT conflict with RFC 3106 [WF2]
5057    
5058 wakaba 1.80 ## NOTE: "authors who are nesting repetition blocks should position such
5059     ## [repetition-block-related] buttons carefully to make clear which block a
5060     ## button applies to.": I have no idea how this can be tested.
5061    
5062 wakaba 1.52 $Element->{$HTML_NS}->{button} = {
5063 wakaba 1.72 %HTMLFlowContentChecker, ## NOTE: %Flow; - something [XHTML10]
5064 wakaba 1.52 ## TODO: -A|%formctrl;|form|fieldset [HTML4]
5065     ## TODO: image map (img) in |button| is "illegal" [HTML4].
5066     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5067     check_attrs => $GetHTMLAttrsChecker->({
5068 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5069 wakaba 1.56 action => $HTMLURIAttrChecker,
5070     autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5071 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5072 wakaba 1.56 ## TODO: form [WF2]
5073     method => $GetHTMLEnumeratedAttrChecker->({
5074     get => 1, post => 1, put => 1, delete => 1,
5075     }),
5076 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5077 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5078     replace => $GetHTMLEnumeratedAttrChecker->({document => 1, values => 1}),
5079     target => $HTMLTargetAttrChecker,
5080 wakaba 1.80 ## NOTE: According to Web Forms 2.0, |button| attribute has |template|
5081     ## attribute to support the |add| button type (as part of repetition
5082     ## template feature). It conflicts with the |template| global attribute
5083     ## introduced as part of the data template feature.
5084     ## NOTE: |template| attribute as defined in Web Forms 2.0 has no
5085     ## author requirement.
5086 wakaba 1.52 type => $GetHTMLEnumeratedAttrChecker->({
5087     button => 1, submit => 1, reset => 1,
5088 wakaba 1.80 ## [WF2]
5089     add => 1, remove => 1, 'move-up' => 1, 'move-down' => 1,
5090 wakaba 1.52 }),
5091     value => sub {}, ## NOTE: CDATA [M12N]
5092     }, {
5093     %HTMLAttrStatus,
5094     %HTMLM12NCommonAttrStatus,
5095     accesskey => FEATURE_M12N10_REC,
5096 wakaba 1.56 action => FEATURE_WF2,
5097     autofocus => FEATURE_WF2,
5098 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5099     dataformatas => FEATURE_HTML4_REC_RESERVED,
5100     datasrc => FEATURE_HTML4_REC_RESERVED,
5101 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5102     enctype => FEATURE_WF2,
5103     form => FEATURE_WF2,
5104 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5105 wakaba 1.56 method => FEATURE_WF2,
5106 wakaba 1.52 name => FEATURE_M12N10_REC,
5107     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5108     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5109 wakaba 1.56 oninvalid => FEATURE_WF2,
5110     replace => FEATURE_WF2,
5111 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5112 wakaba 1.56 target => FEATURE_WF2,
5113     template => FEATURE_WF2,
5114 wakaba 1.52 type => FEATURE_M12N10_REC,
5115     value => FEATURE_M12N10_REC,
5116     }),
5117     ## TODO: Tests
5118     ## TODO: Tests for <nest/> in <button>
5119 wakaba 1.66 check_start => sub {
5120     my ($self, $item, $element_state) = @_;
5121    
5122     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5123     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5124 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5125     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5126 wakaba 1.66 },
5127 wakaba 1.52 };
5128    
5129     $Element->{$HTML_NS}->{label} = {
5130     %HTMLPhrasingContentChecker, ## NOTE: %Inline - label [XHTML10] ## TODO: -label
5131 wakaba 1.56 ## TODO: At most one form control [WF2]
5132 wakaba 1.82 status => FEATURE_WF2 | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
5133 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5134 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5135 wakaba 1.52 for => $AttrCheckerNotImplemented, ## TODO: IDREF ## TODO: Must be |id| of control [HTML4] ## TODO: Or, "may only contain one control element"
5136     }, {
5137     %HTMLAttrStatus,
5138 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
5139 wakaba 1.56 accesskey => FEATURE_WF2 | FEATURE_M12N10_REC,
5140 wakaba 1.52 for => FEATURE_M12N10_REC,
5141     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5142     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5143     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5144     }),
5145     ## TODO: Tests
5146     ## TODO: Tests for <nest/> in <label>
5147     };
5148    
5149     $Element->{$HTML_NS}->{select} = {
5150 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (optgroup|option)* [HTML4] + [WF2] ## TODO: SHOULD avoid empty and visible [WF2]
5151 wakaba 1.52 ## TODO: author should SELECTED at least one OPTION in non-MULTIPLE case [HTML4].
5152     ## TODO: more than one OPTION with SELECTED in non-MULTIPLE case is "error" [HTML4]
5153     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5154 wakaba 1.56 is_root => 1, ## TODO: SHOULD NOT in application/xhtml+xml [WF2]
5155 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5156 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5157 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5158 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5159 wakaba 1.56 data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5160     ## TODO: form [WF2]
5161 wakaba 1.52 multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5162     name => sub {}, ## NOTE: CDATA [M12N]
5163 wakaba 1.56 oninvalid => $HTMLEventHandlerAttrChecker,
5164     ## TODO: pattern [WF2] ## TODO: |title| semantics
5165 wakaba 1.52 size => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5166     }, {
5167     %HTMLAttrStatus,
5168     %HTMLM12NCommonAttrStatus,
5169 wakaba 1.56 accesskey => FEATURE_WF2,
5170     autofocus => FEATURE_WF2,
5171     data => FEATURE_WF2,
5172 wakaba 1.52 datafld => FEATURE_HTML4_REC_RESERVED,
5173     dataformatas => FEATURE_HTML4_REC_RESERVED,
5174     datasrc => FEATURE_HTML4_REC_RESERVED,
5175 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5176     form => FEATURE_WF2,
5177 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5178     multiple => FEATURE_M12N10_REC,
5179     name => FEATURE_M12N10_REC,
5180     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5181     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5182     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5183 wakaba 1.56 oninvalid => FEATURE_WF2,
5184     pattern => FEATURE_WF2,
5185 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5186     sdapref => FEATURE_HTML20_RFC,
5187 wakaba 1.52 size => FEATURE_M12N10_REC,
5188     tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5189     }),
5190     ## TODO: Tests
5191     ## TODO: Tests for <nest/> in <select>
5192 wakaba 1.66 check_start => sub {
5193     my ($self, $item, $element_state) = @_;
5194    
5195     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5196     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
5197 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5198     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5199 wakaba 1.66 },
5200 wakaba 1.52 };
5201 wakaba 1.1
5202 wakaba 1.52 $Element->{$HTML_NS}->{datalist} = {
5203 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (transparent | option)*
5204 wakaba 1.56 ## TODO: |option| child MUST be empty [WF2]
5205 wakaba 1.52 status => FEATURE_WF2,
5206 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5207     data => $HTMLURIAttrChecker, ## TODO: MUST point ... [WF2]
5208     }, {
5209 wakaba 1.52 %HTMLAttrStatus,
5210 wakaba 1.56 data => FEATURE_WF2,
5211 wakaba 1.52 }),
5212     ## TODO: Tests
5213     ## TODO: Tests for <nest/> in <datalist>
5214 wakaba 1.66 check_start => sub {
5215     my ($self, $item, $element_state) = @_;
5216    
5217     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5218 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5219     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5220 wakaba 1.66 },
5221 wakaba 1.52 };
5222 wakaba 1.49
5223 wakaba 1.52 $Element->{$HTML_NS}->{optgroup} = {
5224 wakaba 1.72 %HTMLFlowContentChecker, ## TODO: (option|optgroup)* [HTML4] + [WF2] SHOULD avoid empty and visible [WF2]
5225 wakaba 1.52 status => FEATURE_WF2 | FEATURE_M12N10_REC,
5226     check_attrs => $GetHTMLAttrsChecker->({
5227     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5228     label => sub {}, ## NOTE: Text [M12N] ## TODO: required
5229     }, {
5230     %HTMLAttrStatus,
5231     %HTMLM12NCommonAttrStatus,
5232 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5233 wakaba 1.52 label => FEATURE_M12N10_REC,
5234     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5235     }),
5236     ## TODO: Tests
5237     ## TODO: Tests for <nest/> in <optgroup>
5238     };
5239    
5240     $Element->{$HTML_NS}->{option} = {
5241     %HTMLTextChecker,
5242     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5243     check_attrs => $GetHTMLAttrsChecker->({
5244     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5245     label => sub {}, ## NOTE: Text [M12N]
5246     selected => $GetHTMLBooleanAttrChecker->('selected'),
5247     value => sub {}, ## NOTE: CDATA [M12N]
5248     }, {
5249     %HTMLAttrStatus,
5250     %HTMLM12NCommonAttrStatus,
5251 wakaba 1.56 disabled => FEATURE_WF2, FEATURE_M12N10_REC,
5252 wakaba 1.52 label => FEATURE_M12N10_REC,
5253     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5254 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5255     sdapref => FEATURE_HTML20_RFC,
5256 wakaba 1.52 selected => FEATURE_M12N10_REC,
5257     value => FEATURE_M12N10_REC,
5258     }),
5259     ## TODO: Tests
5260     ## TODO: Tests for <nest/> in <option>
5261     };
5262 wakaba 1.49
5263 wakaba 1.52 $Element->{$HTML_NS}->{textarea} = {
5264     %HTMLTextChecker,
5265     status => FEATURE_WF2 | FEATURE_M12N10_REC,
5266     check_attrs => $GetHTMLAttrsChecker->({
5267 wakaba 1.56 accept => $HTMLIMTAttrChecker, ## TODO: MUST be a text-based type
5268 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
5269 wakaba 1.56 autofocus => $GetHTMLBooleanAttrChecker->('autofocus'),
5270     cols => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }), ## TODO: SHOULD if wrap=hard [WF2]
5271 wakaba 1.52 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5272 wakaba 1.56 ## TODO: form [WF2]
5273     ## TODO: inputmode [WF2]
5274     maxlength => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5275 wakaba 1.52 name => sub {}, ## NOTE: CDATA [M12N]
5276 wakaba 1.56 ## TODO: pattern [WF2] ## TODO: |title| special semantics
5277 wakaba 1.52 readonly => $GetHTMLBooleanAttrChecker->('readonly'),
5278 wakaba 1.56 required => $GetHTMLBooleanAttrChecker->('required'),
5279     rows => $GetHTMLNonNegativeIntegerAttrChecker->(sub { 1 }),
5280     oninvalid => $HTMLEventHandlerAttrChecker,
5281     wrap => $GetHTMLEnumeratedAttrChecker->({soft => 1, hard => 1}),
5282 wakaba 1.52 }, {
5283     %HTMLAttrStatus,
5284     %HTMLM12NCommonAttrStatus,
5285 wakaba 1.56 accept => FEATURE_WF2,
5286 wakaba 1.61 'accept-charset' => FEATURE_HTML2X_RFC,
5287 wakaba 1.52 accesskey => FEATURE_M12N10_REC,
5288 wakaba 1.56 autofocus => FEATURE_WF2,
5289 wakaba 1.52 cols => FEATURE_M12N10_REC,
5290     datafld => FEATURE_HTML4_REC_RESERVED,
5291 wakaba 1.49 dataformatas => FEATURE_HTML4_REC_RESERVED,
5292     datasrc => FEATURE_HTML4_REC_RESERVED,
5293 wakaba 1.56 disabled => FEATURE_WF2 | FEATURE_M12N10_REC,
5294     form => FEATURE_WF2,
5295     inputmode => FEATURE_WF2 | FEATURE_XHTMLBASIC11_CR,
5296 wakaba 1.52 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5297 wakaba 1.56 maxlength => FEATURE_WF2,
5298 wakaba 1.52 name => FEATURE_M12N10_REC,
5299     onblur => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5300     onchange => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5301     onfocus => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5302 wakaba 1.56 oninvalid => FEATURE_WF2,
5303 wakaba 1.52 onselect => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5304 wakaba 1.56 pattern => FEATURE_WF2,
5305     readonly => FEATURE_WF2 | FEATURE_M12N10_REC,
5306     required => FEATURE_WF2,
5307 wakaba 1.61 rows => FEATURE_M12N10_REC,
5308     sdaform => FEATURE_HTML20_RFC,
5309     sdapref => FEATURE_HTML20_RFC,
5310 wakaba 1.52 tabindex => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5311 wakaba 1.56 wrap => FEATURE_WF2,
5312 wakaba 1.52 }),
5313     ## TODO: Tests
5314     ## TODO: Tests for <nest/> in <textarea>
5315 wakaba 1.66 check_start => sub {
5316     my ($self, $item, $element_state) = @_;
5317    
5318     $element_state->{uri_info}->{data}->{type}->{resource} = 1;
5319 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5320     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5321 wakaba 1.66 },
5322 wakaba 1.52 };
5323 wakaba 1.49
5324 wakaba 1.52 $Element->{$HTML_NS}->{output} = {
5325 wakaba 1.56 %HTMLPhrasingContentChecker, ## Inline [WF2]
5326 wakaba 1.52 status => FEATURE_WF2,
5327 wakaba 1.56 check_attrs => $GetHTMLAttrsChecker->({
5328     ## TODO: for [WF2]
5329     ## TODO: form [WF2]
5330     ## TODO: name [WF2]
5331     ## onformchange[WF2]
5332     ## onforminput[WF2]
5333     }, {
5334 wakaba 1.52 %HTMLAttrStatus,
5335 wakaba 1.56 for => FEATURE_WF2,
5336     form => FEATURE_WF2,
5337     name => FEATURE_WF2,
5338     onchange => FEATURE_HTML5_DEFAULT | FEATURE_WF2,
5339     onformchange => FEATURE_WF2,
5340     onforminput => FEATURE_WF2,
5341 wakaba 1.52 }),
5342     ## TODO: Tests
5343     ## TODO: Tests for <nest/> in <output>
5344 wakaba 1.56 ## NOTE: "The output element should be used when ..." [WF2]
5345 wakaba 1.52 };
5346    
5347     $Element->{$HTML_NS}->{isindex} = {
5348     %HTMLEmptyChecker,
5349 wakaba 1.54 status => FEATURE_M12N10_REC_DEPRECATED |
5350     Whatpm::ContentChecker::FEATURE_DEPRECATED_SHOULD, ## [HTML4]
5351 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5352     prompt => sub {}, ## NOTE: Text [M12N]
5353     }, {
5354     %HTMLAttrStatus,
5355     class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5356     dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5357     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5358     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5359     prompt => FEATURE_M12N10_REC_DEPRECATED,
5360 wakaba 1.61 sdapref => FEATURE_HTML20_RFC,
5361 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5362 wakaba 1.52 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5363     }),
5364     ## TODO: Tests
5365     ## TODO: Tests for <nest/> in <isindex>
5366 wakaba 1.66 check_start => sub {
5367     my ($self, $item, $element_state) = @_;
5368    
5369     $element_state->{uri_info}->{action}->{type}->{action} = 1;
5370 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5371     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5372 wakaba 1.66 },
5373 wakaba 1.52 };
5374 wakaba 1.49
5375 wakaba 1.1 $Element->{$HTML_NS}->{script} = {
5376 wakaba 1.40 %HTMLChecker,
5377 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5378 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5379 wakaba 1.91 charset => sub {
5380     my ($self, $attr) = @_;
5381    
5382     unless ($attr->owner_element->has_attribute_ns (undef, 'src')) {
5383     $self->{onerror}->(type => 'attribute not allowed',
5384     node => $attr,
5385     level => $self->{must_level});
5386     }
5387    
5388     $HTMLCharsetChecker->($attr->value, @_);
5389     },
5390 wakaba 1.86 language => sub {}, ## NOTE: No syntax constraint according to HTML4.
5391 wakaba 1.91 src => $HTMLURIAttrChecker, ## TODO: pointed resource MUST be in type of type="" (resource error)
5392 wakaba 1.1 defer => $GetHTMLBooleanAttrChecker->('defer'),
5393     async => $GetHTMLBooleanAttrChecker->('async'),
5394 wakaba 1.91 type => $HTMLIMTAttrChecker, ## TODO: MUST NOT: |charset=""| parameter
5395 wakaba 1.49 }, {
5396     %HTMLAttrStatus,
5397     %HTMLM12NCommonAttrStatus,
5398 wakaba 1.50 async => FEATURE_HTML5_DEFAULT,
5399 wakaba 1.91 charset => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5400 wakaba 1.50 defer => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5401 wakaba 1.49 event => FEATURE_HTML4_REC_RESERVED,
5402     for => FEATURE_HTML4_REC_RESERVED,
5403 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5404 wakaba 1.49 language => FEATURE_M12N10_REC_DEPRECATED,
5405 wakaba 1.50 src => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5406     type => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5407 wakaba 1.9 }),
5408 wakaba 1.40 check_start => sub {
5409     my ($self, $item, $element_state) = @_;
5410 wakaba 1.1
5411 wakaba 1.40 if ($item->{node}->has_attribute_ns (undef, 'src')) {
5412     $element_state->{must_be_empty} = 1;
5413 wakaba 1.1 } else {
5414     ## NOTE: No content model conformance in HTML5 spec.
5415 wakaba 1.40 my $type = $item->{node}->get_attribute_ns (undef, 'type');
5416     my $language = $item->{node}->get_attribute_ns (undef, 'language');
5417 wakaba 1.1 if ((defined $type and $type eq '') or
5418     (defined $language and $language eq '')) {
5419     $type = 'text/javascript';
5420     } elsif (defined $type) {
5421     #
5422     } elsif (defined $language) {
5423     $type = 'text/' . $language;
5424     } else {
5425     $type = 'text/javascript';
5426     }
5427 wakaba 1.93
5428     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)]) {
5429     $type = "$1/$2";
5430     $type =~ tr/A-Z/a-z/; ## NOTE: ASCII case-insensitive
5431     ## TODO: Though we strip prameter here, it should not be ignored for the purpose of conformance checking...
5432     }
5433     $element_state->{script_type} = $type;
5434 wakaba 1.40 }
5435 wakaba 1.66
5436     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5437 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5438     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5439 wakaba 1.40 },
5440     check_child_element => sub {
5441     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5442     $child_is_transparent, $element_state) = @_;
5443     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5444     $self->{onerror}->(node => $child_el,
5445     type => 'element not allowed:minus',
5446     level => $self->{must_level});
5447     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5448     #
5449     } else {
5450     if ($element_state->{must_be_empty}) {
5451     $self->{onerror}->(node => $child_el,
5452     type => 'element not allowed');
5453     }
5454     }
5455     },
5456     check_child_text => sub {
5457     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5458     if ($has_significant and
5459     $element_state->{must_be_empty}) {
5460     $self->{onerror}->(node => $child_node,
5461     type => 'character not allowed');
5462     }
5463     },
5464     check_end => sub {
5465     my ($self, $item, $element_state) = @_;
5466     unless ($element_state->{must_be_empty}) {
5467 wakaba 1.93 if ($element_state->{script_type} =~ m![+/][Xx][Mm][Ll]\z!) {
5468     ## NOTE: XML content should be checked by THIS instance of checker
5469     ## as part of normal tree validation.
5470     #
5471     } else {
5472     $self->{onsubdoc}->({s => $element_state->{text},
5473     container_node => $item->{node},
5474     media_type => $element_state->{script_type},
5475     is_char_string => 1});
5476     ## ISSUE: Should we raise some kind of error for
5477     ## <script type="text/xml">aaaaa</script>?
5478     }
5479    
5480     #$self->{onerror}->(node => $item->{node}, level => 'unsupported',
5481     # type => 'script:'.$element_state->{script_type});
5482 wakaba 1.40 ## TODO: text/javascript support
5483    
5484     $HTMLChecker{check_end}->(@_);
5485 wakaba 1.1 }
5486     },
5487 wakaba 1.91 ## TODO: There MUST be |type| unless the script type is JavaScript. (resource error)
5488     ## NOTE: "When used to include script data, the script data must be embedded
5489     ## inline, the format of the data must be given using the type attribute,
5490     ## and the src attribute must not be specified." - not testable.
5491     ## TODO: It would be possible to err <script type=text/plain src=...>
5492 wakaba 1.1 };
5493 wakaba 1.25 ## ISSUE: Significant check and text child node
5494 wakaba 1.1
5495     ## NOTE: When script is disabled.
5496     $Element->{$HTML_NS}->{noscript} = {
5497 wakaba 1.40 %HTMLTransparentChecker,
5498 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5499     check_attrs => $GetHTMLAttrsChecker->({}, {
5500     %HTMLAttrStatus,
5501     %HTMLM12NCommonAttrStatus,
5502 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5503 wakaba 1.49 }),
5504 wakaba 1.40 check_start => sub {
5505     my ($self, $item, $element_state) = @_;
5506 wakaba 1.3
5507 wakaba 1.40 unless ($item->{node}->owner_document->manakai_is_html) {
5508     $self->{onerror}->(node => $item->{node}, type => 'in XML:noscript');
5509 wakaba 1.3 }
5510    
5511 wakaba 1.40 unless ($self->{flag}->{in_head}) {
5512     $self->_add_minus_elements ($element_state,
5513     {$HTML_NS => {noscript => 1}});
5514     }
5515 wakaba 1.79
5516     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5517     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5518 wakaba 1.3 },
5519 wakaba 1.40 check_child_element => sub {
5520     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5521     $child_is_transparent, $element_state) = @_;
5522     if ($self->{flag}->{in_head}) {
5523     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5524     $self->{onerror}->(node => $child_el,
5525     type => 'element not allowed:minus',
5526     level => $self->{must_level});
5527     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5528     #
5529     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'link') {
5530     #
5531     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'style') {
5532     if ($child_el->has_attribute_ns (undef, 'scoped')) {
5533     $self->{onerror}->(node => $child_el,
5534     type => 'element not allowed:head noscript',
5535     level => $self->{must_level});
5536     }
5537     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'meta') {
5538 wakaba 1.47 my $http_equiv_attr
5539     = $child_el->get_attribute_node_ns (undef, 'http-equiv');
5540     if ($http_equiv_attr) {
5541     ## TODO: case
5542     if (lc $http_equiv_attr->value eq 'content-type') {
5543 wakaba 1.40 $self->{onerror}->(node => $child_el,
5544 wakaba 1.34 type => 'element not allowed:head noscript',
5545     level => $self->{must_level});
5546 wakaba 1.47 } else {
5547     #
5548 wakaba 1.3 }
5549 wakaba 1.47 } else {
5550     $self->{onerror}->(node => $child_el,
5551     type => 'element not allowed:head noscript',
5552     level => $self->{must_level});
5553 wakaba 1.3 }
5554 wakaba 1.40 } else {
5555     $self->{onerror}->(node => $child_el,
5556     type => 'element not allowed:head noscript',
5557     level => $self->{must_level});
5558     }
5559     } else {
5560     $HTMLTransparentChecker{check_child_element}->(@_);
5561     }
5562     },
5563     check_child_text => sub {
5564     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5565     if ($self->{flag}->{in_head}) {
5566     if ($has_significant) {
5567     $self->{onerror}->(node => $child_node,
5568     type => 'character not allowed');
5569 wakaba 1.3 }
5570     } else {
5571 wakaba 1.40 $HTMLTransparentChecker{check_child_text}->(@_);
5572     }
5573     },
5574     check_end => sub {
5575     my ($self, $item, $element_state) = @_;
5576     $self->_remove_minus_elements ($element_state);
5577     if ($self->{flag}->{in_head}) {
5578     $HTMLChecker{check_end}->(@_);
5579     } else {
5580     $HTMLPhrasingContentChecker{check_end}->(@_);
5581 wakaba 1.3 }
5582 wakaba 1.1 },
5583     };
5584 wakaba 1.3 ## ISSUE: Scripting is disabled: <head><noscript><html a></noscript></head>
5585 wakaba 1.1
5586     $Element->{$HTML_NS}->{'event-source'} = {
5587 wakaba 1.40 %HTMLEmptyChecker,
5588 wakaba 1.48 status => FEATURE_HTML5_LC,
5589 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5590 wakaba 1.1 src => $HTMLURIAttrChecker,
5591 wakaba 1.50 }, {
5592     %HTMLAttrStatus,
5593     src => FEATURE_HTML5_LC,
5594 wakaba 1.1 }),
5595 wakaba 1.66 check_start => sub {
5596     my ($self, $item, $element_state) = @_;
5597    
5598     $element_state->{uri_info}->{src}->{type}->{resource} = 1;
5599 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5600     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5601 wakaba 1.66 },
5602 wakaba 1.1 };
5603    
5604     $Element->{$HTML_NS}->{details} = {
5605 wakaba 1.72 %HTMLFlowContentChecker,
5606 wakaba 1.48 status => FEATURE_HTML5_WD,
5607 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5608 wakaba 1.1 open => $GetHTMLBooleanAttrChecker->('open'),
5609 wakaba 1.50 }, {
5610     %HTMLAttrStatus,
5611 wakaba 1.59 open => FEATURE_HTML5_WD,
5612 wakaba 1.1 }),
5613 wakaba 1.72 ## NOTE: legend, Flow
5614 wakaba 1.43 check_child_element => sub {
5615     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5616     $child_is_transparent, $element_state) = @_;
5617     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5618     $self->{onerror}->(node => $child_el,
5619     type => 'element not allowed:minus',
5620     level => $self->{must_level});
5621     $element_state->{has_non_legend} = 1;
5622     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5623     #
5624     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'legend') {
5625     if ($element_state->{has_non_legend}) {
5626     $self->{onerror}->(node => $child_el,
5627     type => 'element not allowed:details legend',
5628     level => $self->{must_level});
5629     }
5630     $element_state->{has_legend} = 1;
5631     $element_state->{has_non_legend} = 1;
5632     } else {
5633 wakaba 1.72 $HTMLFlowContentChecker{check_child_element}->(@_);
5634 wakaba 1.43 $element_state->{has_non_legend} = 1 unless $child_is_transparent;
5635     ## ISSUE: |<details><object><legend>xx</legend></object>..</details>|
5636     ## is conforming?
5637     }
5638     },
5639     check_child_text => sub {
5640     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5641     if ($has_significant) {
5642     $element_state->{has_non_legend} = 1;
5643     }
5644     },
5645     check_end => sub {
5646     my ($self, $item, $element_state) = @_;
5647 wakaba 1.1
5648 wakaba 1.43 unless ($element_state->{has_legend}) {
5649     $self->{onerror}->(node => $item->{node},
5650     type => 'element missing:legend',
5651     level => $self->{must_level});
5652     }
5653    
5654 wakaba 1.72 $HTMLFlowContentChecker{check_end}->(@_);
5655 wakaba 1.43 ## ISSUE: |<details><legend>aa</legend></details>| error?
5656 wakaba 1.1 },
5657     };
5658    
5659     $Element->{$HTML_NS}->{datagrid} = {
5660 wakaba 1.72 %HTMLFlowContentChecker,
5661 wakaba 1.48 status => FEATURE_HTML5_WD,
5662 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5663 wakaba 1.1 disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5664     multiple => $GetHTMLBooleanAttrChecker->('multiple'),
5665 wakaba 1.50 }, {
5666     %HTMLAttrStatus,
5667     disabled => FEATURE_HTML5_WD,
5668     multiple => FEATURE_HTML5_WD,
5669 wakaba 1.1 }),
5670 wakaba 1.40 check_start => sub {
5671     my ($self, $item, $element_state) = @_;
5672 wakaba 1.1
5673 wakaba 1.40 $self->_add_minus_elements ($element_state,
5674     {$HTML_NS => {a => 1, datagrid => 1}});
5675     $element_state->{phase} = 'any';
5676 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5677     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5678 wakaba 1.40 },
5679 wakaba 1.95 ## NOTE: Flow -(text* (table|select|datalist) Flow*) | table | select |
5680     ## datalist | Empty
5681 wakaba 1.40 check_child_element => sub {
5682     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5683     $child_is_transparent, $element_state) = @_;
5684     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5685     $self->{onerror}->(node => $child_el,
5686     type => 'element not allowed:minus',
5687     level => $self->{must_level});
5688     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5689     #
5690 wakaba 1.72 } elsif ($element_state->{phase} eq 'flow') {
5691     if ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5692 wakaba 1.44 if (not $element_state->{has_element} and
5693 wakaba 1.40 $child_nsuri eq $HTML_NS and
5694 wakaba 1.95 {
5695     table => 1, select => 1, datalist => 1,
5696     }->{$child_ln}) {
5697 wakaba 1.40 $self->{onerror}->(node => $child_el,
5698     type => 'element not allowed');
5699     } else {
5700 wakaba 1.8 #
5701 wakaba 1.1 }
5702 wakaba 1.40 } else {
5703     $self->{onerror}->(node => $child_el,
5704     type => 'element not allowed');
5705     }
5706 wakaba 1.43 $element_state->{has_element} = 1;
5707 wakaba 1.40 } elsif ($element_state->{phase} eq 'any') {
5708     if ($child_nsuri eq $HTML_NS and
5709     {table => 1, select => 1, datalist => 1}->{$child_ln}) {
5710     $element_state->{phase} = 'none';
5711 wakaba 1.72 } elsif ($HTMLFlowContent->{$child_nsuri}->{$child_ln}) {
5712 wakaba 1.40 $element_state->{has_element} = 1;
5713 wakaba 1.72 $element_state->{phase} = 'flow';
5714 wakaba 1.40 } else {
5715     $self->{onerror}->(node => $child_el,
5716     type => 'element not allowed');
5717     }
5718     } elsif ($element_state->{phase} eq 'none') {
5719     $self->{onerror}->(node => $child_el,
5720     type => 'element not allowed');
5721     } else {
5722     die "check_child_element: Bad |datagrid| phase: $element_state->{phase}";
5723     }
5724     },
5725     check_child_text => sub {
5726     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5727     if ($has_significant) {
5728 wakaba 1.72 if ($element_state->{phase} eq 'flow') {
5729 wakaba 1.40 #
5730     } elsif ($element_state->{phase} eq 'any') {
5731 wakaba 1.72 $element_state->{phase} = 'flow';
5732 wakaba 1.40 } else {
5733     $self->{onerror}->(node => $child_node,
5734     type => 'character not allowed');
5735 wakaba 1.1 }
5736     }
5737 wakaba 1.40 },
5738     check_end => sub {
5739     my ($self, $item, $element_state) = @_;
5740     $self->_remove_minus_elements ($element_state);
5741 wakaba 1.1
5742 wakaba 1.95 if ($element_state->{phase} eq 'flow') {
5743     if ($element_state->{has_significant}) {
5744     $item->{real_parent_state}->{has_significant} = 1;
5745     } elsif ($item->{transparent}) {
5746     #
5747     } else {
5748     $self->{onerror}->(node => $item->{node},
5749     level => $self->{should_level},
5750     type => 'no significant content');
5751     }
5752     } else {
5753     ## NOTE: Since the content model explicitly allows a |datagird| element
5754     ## being empty, we don't raise "no significant content" error for this
5755     ## element when there is no element. (We should raise an error for
5756     ## |<datagrid><br></datagrid>|, however.)
5757     ## NOTE: As a side-effect, when the |datagrid| element only contains
5758     ## non-conforming content, then the |phase| flag has not changed from
5759     ## |any|, no "no significant content" error is raised neither.
5760     ## NOTE: Another side-effect of the current implementation:
5761     ## |<daragrid><datagrid/></datagrid>| has no "no significant content"
5762     ## error at all.
5763 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5764     }
5765     },
5766 wakaba 1.1 };
5767    
5768     $Element->{$HTML_NS}->{command} = {
5769 wakaba 1.40 %HTMLEmptyChecker,
5770 wakaba 1.48 status => FEATURE_HTML5_WD,
5771 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5772 wakaba 1.1 checked => $GetHTMLBooleanAttrChecker->('checked'),
5773     default => $GetHTMLBooleanAttrChecker->('default'),
5774     disabled => $GetHTMLBooleanAttrChecker->('disabled'),
5775     hidden => $GetHTMLBooleanAttrChecker->('hidden'),
5776     icon => $HTMLURIAttrChecker,
5777     label => sub { }, ## NOTE: No conformance creteria
5778     radiogroup => sub { }, ## NOTE: No conformance creteria
5779     type => sub {
5780     my ($self, $attr) = @_;
5781     my $value = $attr->value;
5782     unless ({command => 1, checkbox => 1, radio => 1}->{$value}) {
5783     $self->{onerror}->(node => $attr, type => 'attribute value not allowed');
5784     }
5785     },
5786 wakaba 1.50 }, {
5787     %HTMLAttrStatus,
5788     checked => FEATURE_HTML5_WD,
5789     default => FEATURE_HTML5_WD,
5790     disabled => FEATURE_HTML5_WD,
5791     hidden => FEATURE_HTML5_WD,
5792     icon => FEATURE_HTML5_WD,
5793     label => FEATURE_HTML5_WD,
5794     radiogroup => FEATURE_HTML5_WD,
5795     type => FEATURE_HTML5_WD,
5796 wakaba 1.1 }),
5797 wakaba 1.66 check_start => sub {
5798     my ($self, $item, $element_state) = @_;
5799    
5800     $element_state->{uri_info}->{icon}->{type}->{embedded} = 1;
5801 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5802     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5803 wakaba 1.66 },
5804 wakaba 1.1 };
5805    
5806     $Element->{$HTML_NS}->{menu} = {
5807 wakaba 1.40 %HTMLPhrasingContentChecker,
5808 wakaba 1.54 #status => FEATURE_M12N10_REC_DEPRECATED | FEATURE_HTML5_WD,
5809     status => FEATURE_M12N10_REC | FEATURE_HTML5_WD,
5810     ## NOTE: We don't want any |menu| element warned as deprecated.
5811 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5812 wakaba 1.1 autosubmit => $GetHTMLBooleanAttrChecker->('autosubmit'),
5813 wakaba 1.68 compact => $GetHTMLBooleanAttrChecker->('compact'),
5814 wakaba 1.1 id => sub {
5815     ## NOTE: same as global |id=""|, with |$self->{menu}| registeration
5816     my ($self, $attr) = @_;
5817     my $value = $attr->value;
5818     if (length $value > 0) {
5819     if ($self->{id}->{$value}) {
5820     $self->{onerror}->(node => $attr, type => 'duplicate ID');
5821     push @{$self->{id}->{$value}}, $attr;
5822     } else {
5823     $self->{id}->{$value} = [$attr];
5824     }
5825     } else {
5826     ## NOTE: MUST contain at least one character
5827     $self->{onerror}->(node => $attr, type => 'empty attribute value');
5828     }
5829     if ($value =~ /[\x09-\x0D\x20]/) {
5830     $self->{onerror}->(node => $attr, type => 'space in ID');
5831     }
5832     $self->{menu}->{$value} ||= $attr;
5833     ## ISSUE: <menu id=""><p contextmenu=""> match?
5834     },
5835     label => sub { }, ## NOTE: No conformance creteria
5836     type => $GetHTMLEnumeratedAttrChecker->({context => 1, toolbar => 1}),
5837 wakaba 1.49 }, {
5838     %HTMLAttrStatus,
5839     %HTMLM12NCommonAttrStatus,
5840 wakaba 1.61 align => FEATURE_HTML2X_RFC,
5841 wakaba 1.50 autosubmit => FEATURE_HTML5_WD,
5842 wakaba 1.49 compat => FEATURE_M12N10_REC_DEPRECATED,
5843 wakaba 1.50 label => FEATURE_HTML5_WD,
5844     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
5845 wakaba 1.61 sdaform => FEATURE_HTML20_RFC,
5846     sdapref => FEATURE_HTML20_RFC,
5847 wakaba 1.50 type => FEATURE_HTML5_WD,
5848 wakaba 1.1 }),
5849 wakaba 1.40 check_start => sub {
5850     my ($self, $item, $element_state) = @_;
5851     $element_state->{phase} = 'li or phrasing';
5852     $element_state->{in_menu_original} = $self->{flag}->{in_menu};
5853     $self->{flag}->{in_menu} = 1;
5854 wakaba 1.79
5855     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5856     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5857 wakaba 1.40 },
5858     check_child_element => sub {
5859     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5860     $child_is_transparent, $element_state) = @_;
5861     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5862     $self->{onerror}->(node => $child_el,
5863     type => 'element not allowed:minus',
5864     level => $self->{must_level});
5865     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5866     #
5867     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'li') {
5868     if ($element_state->{phase} eq 'li') {
5869     #
5870     } elsif ($element_state->{phase} eq 'li or phrasing') {
5871     $element_state->{phase} = 'li';
5872     } else {
5873     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5874     }
5875     } elsif ($HTMLPhrasingContent->{$child_nsuri}->{$child_ln}) {
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_el, type => 'element not allowed');
5882     }
5883     } else {
5884     $self->{onerror}->(node => $child_el, type => 'element not allowed');
5885     }
5886     },
5887     check_child_text => sub {
5888     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5889     if ($has_significant) {
5890     if ($element_state->{phase} eq 'phrasing') {
5891     #
5892     } elsif ($element_state->{phase} eq 'li or phrasing') {
5893     $element_state->{phase} = 'phrasing';
5894     } else {
5895     $self->{onerror}->(node => $child_node,
5896     type => 'character not allowed');
5897 wakaba 1.1 }
5898     }
5899 wakaba 1.40 },
5900     check_end => sub {
5901     my ($self, $item, $element_state) = @_;
5902     delete $self->{flag}->{in_menu} unless $element_state->{in_menu_original};
5903    
5904     if ($element_state->{phase} eq 'li') {
5905     $HTMLChecker{check_end}->(@_);
5906     } else { # 'phrasing' or 'li or phrasing'
5907     $HTMLPhrasingContentChecker{check_end}->(@_);
5908 wakaba 1.1 }
5909     },
5910 wakaba 1.8 };
5911    
5912     $Element->{$HTML_NS}->{datatemplate} = {
5913 wakaba 1.40 %HTMLChecker,
5914 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5915 wakaba 1.40 check_child_element => sub {
5916     my ($self, $item, $child_el, $child_nsuri, $child_ln,
5917     $child_is_transparent, $element_state) = @_;
5918     if ($self->{minus_elements}->{$child_nsuri}->{$child_ln}) {
5919     $self->{onerror}->(node => $child_el,
5920     type => 'element not allowed:minus',
5921     level => $self->{must_level});
5922     } elsif ($self->{plus_elements}->{$child_nsuri}->{$child_ln}) {
5923     #
5924     } elsif ($child_nsuri eq $HTML_NS and $child_ln eq 'rule') {
5925     #
5926     } else {
5927     $self->{onerror}->(node => $child_el,
5928     type => 'element not allowed:datatemplate');
5929     }
5930     },
5931     check_child_text => sub {
5932     my ($self, $item, $child_node, $has_significant, $element_state) = @_;
5933     if ($has_significant) {
5934     $self->{onerror}->(node => $child_node, type => 'character not allowed');
5935 wakaba 1.8 }
5936     },
5937     is_xml_root => 1,
5938     };
5939    
5940     $Element->{$HTML_NS}->{rule} = {
5941 wakaba 1.40 %HTMLChecker,
5942 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5943 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5944 wakaba 1.23 condition => $HTMLSelectorsAttrChecker,
5945 wakaba 1.92 mode => $GetHTMLUnorderedUniqueSetOfSpaceSeparatedTokensAttrChecker->(),
5946 wakaba 1.50 }, {
5947     %HTMLAttrStatus,
5948     condition => FEATURE_HTML5_AT_RISK,
5949     mode => FEATURE_HTML5_AT_RISK,
5950 wakaba 1.8 }),
5951 wakaba 1.40 check_start => sub {
5952     my ($self, $item, $element_state) = @_;
5953 wakaba 1.79
5954 wakaba 1.40 $self->_add_plus_elements ($element_state, {$HTML_NS => {nest => 1}});
5955 wakaba 1.79 $element_state->{in_rule_original} = $self->{flag}->{in_rule};
5956     $self->{flag}->{in_rule} = 1;
5957    
5958     $element_state->{uri_info}->{template}->{type}->{resource} = 1;
5959     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
5960 wakaba 1.40 },
5961     check_child_element => sub { },
5962     check_child_text => sub { },
5963     check_end => sub {
5964     my ($self, $item, $element_state) = @_;
5965 wakaba 1.79
5966 wakaba 1.40 $self->_remove_plus_elements ($element_state);
5967 wakaba 1.79 delete $self->{flag}->{in_rule} unless $element_state->{in_rule_original};
5968    
5969 wakaba 1.40 $HTMLChecker{check_end}->(@_);
5970 wakaba 1.8 },
5971     ## NOTE: "MAY be anything that, when the parent |datatemplate|
5972     ## is applied to some conforming data, results in a conforming DOM tree.":
5973     ## We don't check against this.
5974     };
5975    
5976     $Element->{$HTML_NS}->{nest} = {
5977 wakaba 1.40 %HTMLEmptyChecker,
5978 wakaba 1.48 status => FEATURE_HTML5_AT_RISK,
5979 wakaba 1.40 check_attrs => $GetHTMLAttrsChecker->({
5980 wakaba 1.23 filter => $HTMLSelectorsAttrChecker,
5981     mode => sub {
5982     my ($self, $attr) = @_;
5983     my $value = $attr->value;
5984     if ($value !~ /\A[^\x09-\x0D\x20]+\z/) {
5985     $self->{onerror}->(node => $attr, type => 'mode:syntax error');
5986     }
5987     },
5988 wakaba 1.50 }, {
5989     %HTMLAttrStatus,
5990     filter => FEATURE_HTML5_AT_RISK,
5991     mode => FEATURE_HTML5_AT_RISK,
5992 wakaba 1.8 }),
5993 wakaba 1.1 };
5994    
5995     $Element->{$HTML_NS}->{legend} = {
5996 wakaba 1.40 %HTMLPhrasingContentChecker,
5997 wakaba 1.49 status => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
5998 wakaba 1.52 check_attrs => $GetHTMLAttrsChecker->({
5999 wakaba 1.66 accesskey => $HTMLAccesskeyAttrChecker,
6000 wakaba 1.52 # align => $GetHTMLEnumeratedAttrChecker->({
6001     # top => 1, bottom => 1, left => 1, right => 1,
6002     # }),
6003     }, {
6004 wakaba 1.49 %HTMLAttrStatus,
6005     %HTMLM12NCommonAttrStatus,
6006     accesskey => FEATURE_M12N10_REC,
6007     align => FEATURE_M12N10_REC_DEPRECATED,
6008 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6009 wakaba 1.49 }),
6010 wakaba 1.1 };
6011    
6012     $Element->{$HTML_NS}->{div} = {
6013 wakaba 1.72 %HTMLFlowContentChecker,
6014 wakaba 1.82 status => FEATURE_HTML5_DEFAULT | FEATURE_XHTML2_ED | FEATURE_M12N10_REC,
6015 wakaba 1.68 check_attrs => $GetHTMLAttrsChecker->({
6016     align => $GetHTMLEnumeratedAttrChecker->({
6017     left => 1, center => 1, right => 1, justify => 1,
6018     }),
6019     }, {
6020 wakaba 1.49 %HTMLAttrStatus,
6021 wakaba 1.82 %HTMLM12NXHTML2CommonAttrStatus,
6022 wakaba 1.49 align => FEATURE_M12N10_REC_DEPRECATED,
6023     datafld => FEATURE_HTML4_REC_RESERVED,
6024     dataformatas => FEATURE_HTML4_REC_RESERVED,
6025     datasrc => FEATURE_HTML4_REC_RESERVED,
6026 wakaba 1.50 lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6027 wakaba 1.49 }),
6028 wakaba 1.66 check_start => sub {
6029     my ($self, $item, $element_state) = @_;
6030    
6031     $element_state->{uri_info}->{datasrc}->{type}->{resource} = 1;
6032 wakaba 1.79 $element_state->{uri_info}->{template}->{type}->{resource} = 1;
6033     $element_state->{uri_info}->{ref}->{type}->{resource} = 1;
6034 wakaba 1.66 },
6035 wakaba 1.1 };
6036    
6037 wakaba 1.64 $Element->{$HTML_NS}->{center} = {
6038 wakaba 1.72 %HTMLFlowContentChecker,
6039 wakaba 1.64 status => FEATURE_M12N10_REC_DEPRECATED,
6040     check_attrs => $GetHTMLAttrsChecker->({}, {
6041     %HTMLAttrStatus,
6042     %HTMLM12NCommonAttrStatus,
6043     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6044     }),
6045     };
6046    
6047 wakaba 1.1 $Element->{$HTML_NS}->{font} = {
6048 wakaba 1.40 %HTMLTransparentChecker,
6049 wakaba 1.78 status => FEATURE_HTML5_DROPPED | FEATURE_M12N10_REC_DEPRECATED,
6050 wakaba 1.70 check_attrs => $GetHTMLAttrsChecker->({
6051     ## TODO: HTML4 |size|, |color|, |face|
6052 wakaba 1.49 }, {
6053     %HTMLAttrStatus,
6054 wakaba 1.50 class => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6055 wakaba 1.49 color => FEATURE_M12N10_REC_DEPRECATED,
6056 wakaba 1.50 dir => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6057 wakaba 1.49 face => FEATURE_M12N10_REC_DEPRECATED,
6058 wakaba 1.50 id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6059     lang => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6060 wakaba 1.49 size => FEATURE_M12N10_REC_DEPRECATED,
6061 wakaba 1.78 style => FEATURE_HTML5_DEFAULT | FEATURE_XHTML10_REC,
6062 wakaba 1.50 title => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6063 wakaba 1.49 }),
6064 wakaba 1.78 ## NOTE: When the |font| element was defined in the HTML5 specification,
6065     ## it is allowed only in a document with the WYSIWYG signature. The
6066     ## checker does not check whether there is the signature, since the
6067     ## signature is dropped, too, and has never been implemented. (In addition,
6068     ## for any |font| element an "element not defined" error is raised anyway,
6069     ## such that we don't have to raise an additional error.)
6070 wakaba 1.1 };
6071 wakaba 1.49
6072 wakaba 1.64 $Element->{$HTML_NS}->{basefont} = {
6073     %HTMLEmptyChecker,
6074     status => FEATURE_M12N10_REC_DEPRECATED,
6075     check_attrs => $GetHTMLAttrsChecker->({
6076     ## TODO: color, face, size
6077     }, {
6078     %HTMLAttrStatus,
6079     color => FEATURE_M12N10_REC_DEPRECATED,
6080     face => FEATURE_M12N10_REC_DEPRECATED,
6081     #id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC_DEPRECATED,
6082     id => FEATURE_HTML5_DEFAULT | FEATURE_M12N10_REC,
6083     size => FEATURE_M12N10_REC_DEPRECATED,
6084     }),
6085     };
6086    
6087 wakaba 1.49 ## TODO: frameset FEATURE_M12N10_REC
6088     ## class title id cols rows onload onunload style(x10)
6089     ## frame frameborder longdesc marginheight marginwidth noresize scrolling src name(deprecated) class,id,title,style(x10)
6090     ## noframes Common, lang(xhtml10)
6091    
6092 wakaba 1.100 ## TODO: CR: rbc rtc @rbspan (M12NXHTML2Common)
6093 wakaba 1.56
6094 wakaba 1.61 ## TODO: xmp, listing, plaintext FEATURE_HTML32_REC_OBSOLETE
6095     ## TODO: ^^^ lang, dir, id, class [HTML 2.x] sdaform [HTML 2.0]
6096     ## xmp, listing sdapref[HTML2,0]
6097    
6098 wakaba 1.56 =pod
6099    
6100     WF2: Documents MUST comply to [CHARMOD].
6101     WF2: Vencor extensions MUST NOT be used.
6102    
6103 wakaba 1.61 HTML 2.0 nextid @n
6104    
6105     RFC 2659: CERTS CRYPTOPTS
6106    
6107     ISO-HTML: pre-html, divN
6108 wakaba 1.82
6109     XHTML2: blockcode (Common), h (Common), separator (Common), l (Common),
6110     di (Common), nl (Common), handler (Common, type), standby (Common),
6111     summary (Common)
6112    
6113 wakaba 1.97 Access & XHTML2: access (LC)
6114 wakaba 1.82
6115     XML Events & XForms (for XHTML2 support; very, very low priority)
6116 wakaba 1.61
6117 wakaba 1.56 =cut
6118 wakaba 1.61
6119     ## NOTE: Where RFC 2659 allows additional attributes is unclear.
6120     ## We added them only to |a|. |link| and |form| might also allow them
6121     ## in theory.
6122 wakaba 1.1
6123     $Whatpm::ContentChecker::Namespace->{$HTML_NS}->{loaded} = 1;
6124    
6125     1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24